Author Topic: How to use the UART to get data and send data!  (Read 39648 times)

Offline tr111

  • Jr. Member
  • **
  • Posts: 72
    • View Profile
How to use the UART to get data and send data!
« on: September 07, 2007, 10:13:34 AM »
hello:
     How to use the UART to get data and send data! I only the Tty_drv.c is the uart driver!
      when I send the data to the mcf52235 ,it will auto send back to me now! I don't why?
     Now  I want to get the data from UART and send to the ETH!
    But I don't know how the get the  UART data and send the data by UART!
   Regards

tr111
« Last Edit: September 07, 2007, 10:17:48 AM by tr111 »

Offline tr111

  • Jr. Member
  • **
  • Posts: 72
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #1 on: September 07, 2007, 10:17:10 AM »
hi:
   I think the Utasker is very easy to use!

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #2 on: September 07, 2007, 02:05:10 PM »
Hi
To use the serial interface(s) first ensure that the define SERIAL_INTERFACE is active in config.h. This will include UART driver support. It will also include use of the UART in the uTasker demo project.

It is best to start with the demo since it shows how to configure and open a UART. It will receive and actively echo characters back and it will serve a menu when the Entery key is pressed. The setup of the UART is additionally controllable via the menu, TELNET or Web Browser, where the configuration can be changed 'on-the-fly'.

1. Configuring and opening

Code: [Select]
    TTYTABLE tInterfaceParameters;                                       // table for passing information to driver
    tInterfaceParameters.Channel = DEMO_UART;                            // set UART channel for serial use
    tInterfaceParameters.ucSpeed = temp_pars->temp_parameters.ucSerialSpeed; // baud rate
    tInterfaceParameters.Rx_tx_sizes.RxQueueSize = RX_BUFFER_SIZE;       // input buffer size
    tInterfaceParameters.Rx_tx_sizes.TxQueueSize = TX_BUFFER_SIZE;       // output buffer size
    tInterfaceParameters.Task_to_wake = OWN_TASK;                        // wake self when messages have been received
    #ifdef SUPPORT_FLOW_HIGH_LOW
    tInterfaceParameters.ucFlowHighWater = temp_pars->temp_parameters.ucFlowHigh;// set the flow control high and low water levels in %
    tInterfaceParameters.ucFlowLowWater = temp_pars->temp_parameters.ucFlowLow;
    #endif
    tInterfaceParameters.usConfig = temp_pars->temp_parameters.usSerialMode;
    #ifdef TEST_MSG_MODE
        tInterfaceParameters.usConfig |= (MSG_MODE);
        #if defined (TEST_MSG_CNT_MODE) && defined (SUPPORT_MSG_CNT)
            tInterfaceParameters.usConfig |= (MSG_MODE_RX_CNT);
        #endif
        tInterfaceParameters.usConfig &= ~USE_XON_OFF;
        tInterfaceParameters.ucMessageTerminator = '\r';
    #endif
    #ifdef SERIAL_SUPPORT_DMA
        tInterfaceParameters.ucDMAConfig = UART_TX_DMA;                  // activate DMA on transmission
    #endif
    if ((SerialPortID = fnOpen( TYPE_TTY, FOR_I_O, &tInterfaceParameters )) != 0) { // open the channel with defined configurations (initially inactive)
        fnDriver( SerialPortID, ( TX_ON | RX_ON ), 0 );                  // enable rx and tx
    }

All interfaces are opened using fnOpen and the parameters are passed in a table - in this example some parameters are fixed and some are set from the user parameters. Note that DEMO_UART is the UART number (0, 1, 2 etc. depending on what the used processor supports) which can be defined in app_hw_xxx.h to suit your board or project.

SerialPortID is a handle to the UART which is then used for communication.

The demo can test also different reception modes:
TEST_MSG_MODE        - the receiver task is woken only when a complete line of data, terminated by the defined character, is received.
TEST_MSG_CNT_MODE - the same as TEST_MSG_MODE but the message length is also returned in the rx buffer.

There are various other mode details, like ECHO_RX_CHARS to automatically echo received characters, USE_XON_OFF to enable XON/XOFF flow control. See driver.h for a list.

2. Sending messages

Code: [Select]
    static const CHAR ucCOMMAND_MODE_TIMEOUT[] = "Connection timed out\r\n";
    fnWrite(SerialPortID, (unsigned char *)ucCOMMAND_MODE_TIMEOUT, sizeof(ucCOMMAND_MODE_TIMEOUT));

A write to the UART handle will cause data to be transmitted.
For sending strings - eg. for debug purposes - fnDebugMsg("Test"); is typically used. Before using for the first time, divert the debug output to the required UART by setting the following global variable:
    DebugHandle = SerialPortID;
(As a side note, when not set for UART output, debug data will be sent to the routine fnNetworkTx, which can also be a TELNET connection - the demo project also support this)

3. Receiving data
Generally the UART is configured to wake the receiving task on reception.
[tInterfaceParameters.Task_to_wake = OWN_TASK;]
Depending on the exact operating mode, this can be on each received character or each received terminated line of input. The demo project shows how to receive correctly in each mode:

fnMsgs(SerialPortID); This is called to return the number of waiting messages. In character mode this is equal to the number of characters but in message mode it will indicate the number of complete messages and not individual characters.

Length = fnRead( SerialPortID, ucInputMessage, MEDIUM_MESSAGE); This will return as many waiting characters in the input buffer, up to the limit MEDIUM_MESSAGE. The number of characters actually copied to the buffer ucInputMessage is returned.

4. Simulating UARTs in the uTasker simulator
By mapping the UART(s) to COM ports on the local PC, the simulator sends and receives data as on the real target and can enable efficient testing and project development.

In app_hw_xxxx.h you can set these defines:
    #define SERIAL_PORT_0 '1'     // if we open UART channel 0 we simulate using com1 on the PC
    #define SERIAL_PORT_1 '2'     // if we open UART channel 1 we simulate using com2 on the PC
    #define SERIAL_PORT_2 '3'     // if we open UART channel 2 we simulate using com3 on the PC

etc. depending how many UARTs are available
 
If you set ‘0’ no com port will be mapped. Eg. if you instead set ‘5’ COM5. will be mapped, etc. Don’t map 2 simultaneously used UARTS to the same COM since only the first will actually work. Mapping to a non-existent or already used COM port has no detrimental effects but it will of course not be abl eto use the port.


This is only a brief introduction because the UART support has quite a lot of different configurations and there are various extra details about using DMA, flow control etc.

It should however be possible to start working with the UARTs base do this and the uTasker demo example, in the simulator and on the target. We can use this thread for extra discussion details as work progresses.

Good luck

Regards

Mark







Offline tr111

  • Jr. Member
  • **
  • Posts: 72
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #3 on: September 08, 2007, 01:28:30 AM »
Mark:
     Thank you very much!
     Your answer is always fast and professional!
      Regards

     tr111


Offline guibiao

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #4 on: September 28, 2007, 06:24:31 PM »
Hi, Mark. How difficult is it to modify the tty driver code, so in character mode, it only wakes the related task when it receives fixed number of bytes, instead checking the receiving buffer in the task code. It seems more efficient to handle UART comm with fixed length. I also like to be able to change that length easily if needed to. Thanks!

Guibiao

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #5 on: September 28, 2007, 08:17:11 PM »
Hi Guibiao

It would not be very difficult to do this, but would need a change in the TTY_drv.c code.

However this type of operation is not very common. The reason is that usually the UART is used to receive messages that can be variable length. If for example it would collect 8 characters before waking the owner task it would mean that if only 7 are received they would have to wait until one more byte arrived before being serviced.

Some times it is possible to use a terminating character - example a CR or LF. This is often useful for typed input since the low level driver can collect the message and wake the owner task only when the user has hit the ENTER key. Message mode operation solves this. Also some protocols have a terminating character and this can be implemented efficiently like this.

It is less common to find protocols with fixed length messages, but there are some. In fact the use of DMA for reception in this case is the most efficient method. This is supported by the M5223X project where the receiver can be set to fixed length DMA reception mode (really because it is the only real way of using DMA on the receiver anyway...). As a reference see the routine fnSciRxByte() in tty_drv.c and the define SERIAL_SUPPORT_DMA.

At the end of this routine (which is in fact the interrupt for each received character) you will see the following lines of code:

    if ( rx_ctl->wake_task ) {                                                   // wake up an input task, if defined, when defined number of characters have been collected
        uTaskerStateChange(rx_ctl->wake_task, UTASKER_ACTIVATE);         // wake up owner task
    }


This is where the owner task is being woken and this is called on every character.
You could easily change this to something like:

    if ((rx_ctl->tty_queue.chars == 8 ) && ( rx_ctl->wake_task )) {             // wake up an input task, if defined
        uTaskerStateChange(rx_ctl->wake_task, UTASKER_ACTIVATE);         // wake up owner task
    }


This would then only wake when the number of waiting characters reaches 8. This can be set as a global variable which can be modified (or better as an entry in the TTYQUE struct belonging to the UART channel).

Regards

Mark

Offline guibiao

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #6 on: October 19, 2007, 11:33:36 PM »
Hi, Mark. When I try the simulator, the demo serial port works (get the menu after enter key). But, when I download the demo code onto the Raisonance Demo board (STR912FX). Nothing on the serial port1 (Could not see anything even with scope). Did I miss something obvious? Thanks!
 
Guibiao

Offline guibiao

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #7 on: October 19, 2007, 11:54:50 PM »
Never mind, I forgot the jumpers!

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #8 on: October 29, 2007, 11:48:54 PM »
Hi All

For some more information about UART flow control see the following thread:

http://www.utasker.com/forum/index.php?topic=105.msg420#msg420

Regards

Mark

Offline Tino

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #9 on: January 20, 2008, 06:56:17 PM »
Hi,
I can write to the serial port and receive it on the pc. But when I try to receive data, it crashes.

I use the following code:

if (fnMsgs(SerialPortID) > 0) {
unsigned char ucRx[10];
unsigned char ucLength = 5;
ucLength = fnRead( SerialPortID, ucRx, 10);
fnWrite(SerialPortID, (unsigned char *)ucRx, ucLength);
}

When it is commented, ethernet, can and serial send work.
But as soon as I uncomment it, ethernet and serial send don't work anymore. Only can goes on but very unreliable.

I tried many things like changing the defines in config.h and the parameters in application.c.
It didn't work.
What to do?
Is there some tutorial how to do it? I tried all things written in the forum and also from the documents.

I use serial port 0.

Thanks,
Tino

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #10 on: January 20, 2008, 08:15:05 PM »
Hi Tino

I don't see any error with the code below.
However, check that the code is not being called before the interface has been opened - i.e. that it is not crashing because fnMsgs(SerialPortID) is being called with an invalid value for SerialPortID.

It is not clear whether the code crashes only when something has been received or if it crashes as soon as the program is run, even when no data has been received. (If you can run the same code in the uTasker simulator it would be interesting if you get the same results).

I can't make any statement concerning the fact that CAN is still working (but unreliably) - I believe you are using the SAM7X project where CAN is not supported so you possibly have added your own CAN driver (?).

Regards

Mark

PS. If you can send me your project (or the complete code of the modules(s) working the serial port) it may help to see the reason.

PPS. The uTasker demo project contains serial interface support. Connect with 19'200 and hit the enter key of a connected terminal emulator to activate a menu driven interface. This should work on your board and then serve as a reference.
Otherwise this thread gives most details about how best to use it.
« Last Edit: January 20, 2008, 08:17:06 PM by mark »

Offline Tino

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #11 on: January 21, 2008, 12:57:59 PM »
Hi,
It seems that I called the code before the usart was initialized.
Now I check
if (SerialPortID > 0) {
and it works.
But is the check correct? What is the initial value of SerialPortID?

I also had to comment some lines in the application.c which echoes the received chars.

Finallly everything is working!!
The CAN driver is self implemented.

Thank you for your support,
Tino

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #12 on: January 21, 2008, 01:51:07 PM »
Hi Tino

The initial value of the SerialPortID handle is defined by the code using it - generally 0 (which indicates that it has not been allocated yet).
Generally code should not try to use a serial handle before the interface has actually been opened - checking it as you have of course protects against this. The correct method is however to open the interface at a place in the code which is always called before other accesses are made.

Regards

Mark

Offline Fausto_F

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: How to use the UART to get data and send data!
« Reply #13 on: March 17, 2008, 09:24:17 PM »
Hi Mark,

I'm using the serial port to create a RS485 Bus, however in half mode.
In this case, I need to guarantee that all output buffer was sent and then change the 485 driver from transmit to receive mode, in other words I have to wait something like UART_TEMP = 1 (Transmit Buffer Empty), so my question is: Can uTasker generate any unsoliceted event to show me that the transmit buffer was really sent?
If no, which would be the most suitable way to do this?

Thanks

Fausto


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2990
    • View Profile
    • uTasker
Re: How to use the UART to get data and send data!
« Reply #14 on: March 17, 2008, 11:05:45 PM »
Hi Fausto

One possibility to get an event when the tx buffer becomes empty would be to use the TX_FREE event.

This is a small extract from tty_drv.c:
#ifdef WAKE_BLOCKED_TX
    #ifdef SUPPORT_FLOW_HIGH_LOW
            fnWakeBlockedTx(ptTTYQue, rx_control[channel]->low_water_level);
    #else


Assuming the defines WAKE_BLOCKED_TX and SUPPORT_FLOW_HIGH_LOW are active, the owner task will be sent a TX_FREE event each time the level of the output buffer readuces to equal the low water level. If you are not using flow control, this can be set to 0 so that it happens when a message has been sent.
Before transmitting the message, the following call will enter the task to be woken (it has to be reset on each message):
fnDriver( SerialHandle, MODIFY_WAKEUP, (MODIFY_TX | OWN_TASK) );

Of course, the tty_drv.c code can also be slightly modified if needed to send this event every time the output buffer becomes empty.

There is however a problem involved which is hardware specific. Most UARTs generate interrupts when there is space in the output buffer. Some can also generate interrupts when the transmission has completed (when the last interrupt arrives, it generally means that there is space to send the next byte but not that the transmission has in fact terminated). Depending on the UART in question there may be a more accurate method but tty_drv.c is HW independent and any more specific work will need to be performed in the HW driver itself.

The problem means that when the TX_FREE event arrives and the code wants to change the direction it is probably too early since the last byte is still being transmitted. If the UART that you are using doesn't give a method to get notification when the bytes has completely been transmitted, the SW will have to first wait a short time equal to the transmission delay of one character. To do this, a generic solution would be to use the TX_FREE event to start a timer equal to the bit time multiplied by the character bits, including stop bits, etc. When this timer fires, the direction of the the RS485 bus can be changed.

Good luck

Regards

Mark