This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS: RTOS: MSP430F5438A UART interrupt

Other Parts Discussed in Thread: MSP430F5438A

Tool/software: TI-RTOS

Hello!

I am using TI-RTOS on MSP430F5438A. I want to make a UART communication between my MSP and another device.

I have a task that writes and reads from UART a message. After the UART_write(), the device return me another message. The communication consist of messages that are sent from my MSP to the device and immediately the device responds. For every message I send  to the device I need to wait a period of time for the next message to be sent from MSP to device.

In this case i don't have a problem beacuse using the TI-RTOS driver I just need to call UART_write() and after that UART_read(). My problem is that the device sends some messages at a random time period. I don't want to stay in a loop where i call UART_read() to catch the buffers that came at a random period of time. Is there any way to make an interrupt when i have something on UART(rising edge) and read the messages?

  • We've been dealing with a similar issue (on MSP432, but the UART driver is pretty much the same).
    How to use the UART driver if you don't know when you will receive bytes or how many bytes you might actually receive?

    A possible solution:

    Configure UART to receive a single character at a time in read callback mode.
    Callback puts the recieved char into a RingBufferand and posts a SWI
    SWI calls UART_read() to get the next character (RTOS does not allow you to call UART read inside the callback!).
    An external task can then process bytes out of the RingBuffer as it gets the time.

    Calling UART_read() in the SWI rather thatn the callback slightly increases the risk of missed characters at higher baud rates or heavy CPU load unless you are careful with priorities.

    With this method, you are best sticking to one character at a time even if you know that data is coming at you in fixed sized packets, otherwise you have to start thinking about how to handle error cases.
    e.g. what if you call UART_read() mid way through the packet or a character is missed somehow
    If this happens you end up out of sync with the data flow and somehow need to reestablish sync.

    Just watch processor load at higher baud rates - e.g. at 57600Baud every (roughly) 17uSec you will be getting an interrupt, posting a SWI, loading the character into a ring buffer, preparing the next read. This *might* get in the way of other things going on in your system.

    In the end we decided against using the driver for UART_read and created our own bare metal driver that is basically just an ISR that stuffs the received charater into a RingBuffer (and deals with interrupt flags appropriately) each time the UART Rx interrupt fires.

    It would be nice if the UART driver was extended to have a mode similar to our bare metal driver.  i.e. Provide the ability to start a "background read mode" that constant runs and pushes recieved characters into a ring buffer, and provide an API to pop characters out of the ring buffer (e.g. in a task that is processing the received data). 


    Cheers
    Julian

  • The UART driver in Ti-RTOS create the hwi with number 46. In this case i can use the driver for UART_write and make an ISR in bare metal for uart read?

  • Hi Ion,

    I guess I mischaracterized what we did. In the end we wrote our own simple bare metal UART driver for both read AND write.

    For a given UART you can't use the RTOS for write and your own driver for read (at least, not without changing and recompiling the RTOS UART driver.)

    If your implementation uses multiple UARTs, there would be nothing wrong with using the RTOS driver for one, and your own driver for the other.

    Cheers
    Julian
  • Ion,

    What is the timing of the random inbound message? How do you guarantee that the random inbound messages will not overlap with the ACK inbound message from the device? I assume you have a solution for this.

    The simple approach is to have two tasks on the MSP: one task for writing and one for reading. The writing task wakes up as needed to send its message to the device. The ACK message will be received by the read task. You can set a flag in the write task to tell the read task to expect an ACK message. If the read task receives a message and the flag is not set, then it is a random message. This assumes you have a solution for the question I asked above.

    If the timing is such that the ACK message and the random message never overlap, another approach is to use the UART read timeout. Let's say you send a message every 5 seconds. You can call UART read with a timeout of 5 seconds. If the call returns without data, then you know it is time to issue the UART write followed by a UART read. Then you call UART read again and wait again. If the UART read returned with data, then you have a random message. The advantage of this is that you only need one task. But the timeout value is a parameter to the open call. So, you cannot change it and all UART reads will have the same timeout. Maybe this is a problem.

    One last suggestion is to use the UART callback mode. This approach can also be done as one task instead of two. In this option, you call UART read in callback mode. What happens is that UART read returns immediately, you have simply issued a buffer to the driver. When the buffer is full, your callback is called. Now you create a semaphore. In the UART read callback, you call Semaphore_post(). Your task calls Semaphore_pend() with a timeout. If it returns with a timeout, you know it is time to do the write and read. If it returned without a timeout, you have received a random message.

    ~Ramsey

  • Hi Ramsey,

    > The ACK message will be received by the read task.

    Could you explain some more what you mean by "read task".
    I have this niggling concern that this actually harder than you say.

    How do you deal with different sized ACK/Random messages? [original poster might not have control of these message sizes]

    How do you deal with missed chars (e.g. framing error - my understanding is that in this case an error interrupt is generated, but no character is received so that character is effectively lost, which is a problem if you did a UART_Read() expect a certain number of characters.)

    How do you deal with starting a read while the remote end is sending ACK/Random message (it CAN happen and is more likely if your comms is not strictly message-response)

    Am I over thinking this?!?!

    My feeling is (and my implementation approach has been) to allow reading to be truly asynchronous (uARt!) with a HWI feeding characters into a ring buffer and a task running periodically pulling characters out of the ring buffer quickly enough that you don't get overflows. This is not a mode of operation that the UART driver supports so I've had to roll my own.

    The task needs to run periodically, pulling out characters, building up a message and verifying the message is correct (e.g. format seems sane, CRC checks out etc*). Malformed messages are thrown away. Once a correct message is found it can be dealt with appropriately. E.g. if it is an ACK, and you were waiting for an ACK then post a semaphore or set a flag or something. If it’s a "random message" then process it accordingly.

    * There is an assumption here that there is some message structure that allows you to separate different messages from one another!

    I don't think running the task every X characters (which is essentially what you get when you use UART_read() in blocking or callback modes) is the best approach. It is problematic if message sizes are not constant and if there are receive errors or sync errors (start read while Tx is occurring) . Timeouts help, but remember if you did a UART_read() for N characters (blocking or callback) a timeout means you receive less than N characters (it does NOT mean you received 0 characters) so you still need to go back and see if there is anything useful in the buffer if a time out occurs. [e.g. maybe you were expecting a longer “random message” but received a shorter “ACK” message – you still wan’t to process the ACK don’t you?!]

    I don’t know, maybe what I’m proposing is more than the original poster needs......
  • Ramsey,

    The commands the MSP sends to the device are interpreted from a file and every command has a delay so that the random messages do not overlap the ACK messages. At this time I have a task that manage the UART and another task that toggle a LED and increment a variable. The UART is in blocking mode and in this mode the task will be blocked until the UART_read or UART_write will finish to wirte/read and let other tasks to do their job. 

    Here is my task for UART and the priority is 3

    Void taskUART(UArg arg0, UArg arg1)
    {
    
        /*Create a UART with data processing off. */
        UART_Params_init(&uartParams);
        uartParams.writeDataMode 	= UART_DATA_BINARY;
        uartParams.readDataMode 	= UART_DATA_BINARY;
        uartParams.readReturnMode	= UART_RETURN_FULL;
        uartParams.readEcho 		= UART_ECHO_OFF;
        uartParams.baudRate 		= 9600;
        uartParams.readTimeout		= 100;
    
    
    
        uartHandle = UART_open(Board_UART0, &uartParams);
    
        if (uartHandle == NULL) {
            System_abort("Error opening the UART");
        }
    
        BYTE seqCount=0x00;
        BYTE cmd_on[3] ={CMD_ON,0x01,0x00};
        BYTE cmd_off[3] = {CMD_OFF,0x01,seqCount};
        BYTE cmd_ldp[31] ={ CMD_LDP,0x1D,seqCount,0x00, 0x00, 0xcc, 0x04 , 0x66 , 0x04 , 0x00 , 0x04 , 0x99 , 0x03 , 0x00 , 0x00 , 0x00 , 0x00 , 0xe8 , 0x03
        					   ,0x10, 0x00, 0x00, 0x00, 0xea, 0xfe, 0xcc, 0x0c, 0x10, 0x27, 0x00, 0x08};
    
    
        BYTE commandList[] = {CMD_ON,CMD_LDP,CMD_OFF};
        BYTE su_response[174];
        LONG time;
        LONG time1;
    
    
        int i;
        for(i=0;i<sizeof(commandList);i++)
        {
        	seqCount++;
        	switch(commandList[i])
        	{
        		case CMD_ON 	:{
    								cmd_obc_su_on[2] = seqCount;
        				
    								UART_write(uartHandle, &cmd_obc_su_on, sizeof(cmd_obc_su_on));
        							time = getSystemTimestamp(&newTime, &y2k, &y1k);
        							while(time1 < time + 60)
        							{
        							    time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
        							}
        							time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
    							}
        							break;
    							
    
        		case CMD_OFF :	{
    								cmd_obc_su_off[2] = seqCount;
        							UART_write(uartHandle, &cmd_obc_su_off, sizeof(cmd_obc_su_off));
    
        							time = getSystemTimestamp(&newTime, &y2k, &y1k);
        							while(time1 < time + 60)
        							{
        							    time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
        							}
        							time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
    							}
        							break;
    							
    
        		case CMD_LDP:	{
    								cmd_su_ldp[2] = seqCount;
        							UART_write(uartHandle, &cmd_su_ldp, sizeof(cmd_su_ldp));
        						
    
        							time = getSystemTimestamp(&newTime, &y2k, &y1k);
        							while(time1 < time + 60)
        							{
        							    UART_read(uartHandle,&su_response,sizeof(su_response));
        							    
        							    time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
        							}
        							time1 = getSystemTimestamp(&newTime, &y2k, &y1k);
    							}
        							break;
    							
        	};
        }

    The other task has priority 2



    Void task3(UArg arg0, UArg arg1)
    {
    	 int i=0;
    	while(1)
    	{
    		i++;
    		if (i==255)
    		   i=0;
    		GPIO_toggle(Board_LED0);
    	}
    }

    The random messages comes only after the CMD_LDP command was sent. In this case I call UART_read in a loop that lasts as the delay between the commands. The UART_write works properly but i am unable to read the messages that comes from the device.

    TO BE MENTIONED: the command packets sizes that are sent from MSP to the device differs from command to command. The ACKs and random messages has the same sizes.

  • Ion, Julian,

    There are many questions posed in this thread. Maybe too many for me to answer.  I see that you are both looking for a way to receive data from UART. Maybe this data is asynchronous and maybe message size will vary. I assume that there must be some message protocol otherwise you would not be able to decode and process the messages.

    Let me suggest a solution for the receive data path. We can discuss the transmit data path later. I have not built and tested this, but the main goal is to give you ideas for building your own solution which will be suited for your application.

    Please refer to the attached diagram:  .

    I propose a design where a task is used to assemble the received data into a complete message for processing. The UART should be configured for callback mode. Data queues will be used to pass data from the driver to the task.

    A key element is that the data receive path must run independently from the processing task. In other words, anytime data arrives, it must be stored in a safe place. This must have highest priority.

    The task will run based on two inputs: 1) sufficient data has been received to initiate a process loop, and 2) after some amount of idle time to process any available data.

    The UART receives data on the wire. When a frame (e.g. byte) has been received, it is placed into the RXBUF buffer. If the UART driver has been primed to receive data, this will raise an interrupt. Otherwise, not.

    Calling UART_read() will prime the UART driver. Each time a new frame has been received, an interrupt will copy the data into the buffer. If the buffer is full, the application callback will be invoked. Otherwise, not.

    It is important to note that when the buffer is full, the UART will no longer save received data. Therefore, the system must respond quickly to reclaim the full buffer and to issue a new empty buffer which will prime the UART driver again. When the UART driver invokes the callback, this runs in interrupt context which is highest priority. The Ap_uartRxCb() will store the data count in the buffer object, and place the buffer in the Swi argument handle. It will then post the Swi.

    A Swi also runs at very high priority. The Ap_uartRxSwi() will fetch a new empty buffer from the free queue and issue it to the UART driver with a call to UART_read(). Now we can safely receive more data. The full buffer is placed on the Data queue and the task is notified by posting the semaphore.

    If the Swi has been given an empty data buffer (see below), then it simply returns the buffer to the Free queue and does *not* post the semaphore.

    The task takes the data form the Data queue and copies the message fragment into a command buffer. The data buffer is returned to the Free queue. The task processes the data. Eventually, the task waits for more data by pending on the semaphore.

    When the UART becomes idle, after some time the task will run to fetch any data which has not yet been processes. This happens because the task used a timeout (e.g. 50 msec) when pending on the semaphore. The task calls UART_readCancel(). In this call, the UART driver will invoke the callback with the count of bytes already received. As before, the callback stores the count in the data object and posts the Swi. The Swi primes the UART with a new buffer, places the data buffer in the Data queue and posts the semaphore.  After all this happens, the call to UART_readCancel() finally returns back into the task. The task simply jumps to the beginning of the loop and pends on the semaphore.

    If there was data to process, then the semaphore would already be posted and the task does not block. It immediately returns from the semaphore pend and fetched the buffer from the Data queue. However, if there had not been any data, the callback would have passed an empty buffer to the Swi. The Swi checks the count. If the count is zero, it returns the empty buffer to the Free queue and does not post the semaphore. This would cause the task to block again, waiting for more data.

    Here is some pseudo code to help fill in the details.

    typedef struct {
        BYTE data[32];
        int count;
        Queue_Elem link;
    } Ap_Buffer;
    
    inline Ap_Buffer *Ap_BaseLink(Queue_Elem *link)
    {
        int offset = (int)&(((Ap_Buffer *)0)->link);
        reutrn ((Ap_Buffer *)((char *)link - offset));
    }
    
    Ap_Buffer *Ap_swiRxArg0;
    
    Ap_uartRxCb(void *data, size_t count)
    {
        Ap_Buffer *buf = (Ap_Buffer *)data;
    
        /* store count in buffer object */
        buf->count = count;
    
        /* pass the buffer to the Swi */
        Ap_swiArg0 = (UArg)buf;
        Swi_post(Ap_uartRxSwiHndl);
    }
    
    Ap_uartRxSwiFxn(UArg arg0, UArg arg1)
    {
        Ap_Buffer *buf;
    
        /* prime uart with new buffer */
        ptr = Que_get(FreeQ);
        buf = Ap_BaseLink(ptr);
        UART_read(uart, buf->data, 32);
    
        buf = (Ap_Buffer *)arg0;
        if (buf->count > 0) {
            /* pass data to task for processing */
            Que_put(DataQ, &(buf->link));
            Semaphore_post(Ap_workSem);
        }
        else {
            /* return empty buffer to free queue */
            Que_put(FreeQ, &(buf->link));
        }
    }
    
    Ap_processTskFxn()
    {
        bool doWork = true;
        Ap_Buffer *buf;
    
        while (doWork) {
    
            /* wait on semaphore with timeout */
            if (!Sempahore_pend(Ap_workSem, 50)) {
    
                /* timeout, fetch unprocessed data */
                UART_readCancel(uart);
                continue;
            }
    
            /* fetch data from data queue */
            ptr = Que_get(DataQ);
            buf = Ap_BaseLink(ptr);
    
            /* copy data into command buffer */
            ...
    
            /* return buffer to free queue */
            Queue_put(FreeQ, &(buf->link));
    
            /* process command buffer */
        }
    }

    Let's continue this discussion in the next post.

    ~Ramsey

  • G'Day Ramsey,

    This is a similar approach to what I have taken, though I've chosen to go a bit more bare-metal.

    I think I will step out of this discussion now. You are probably better able to steer in the right direction.

    Cheers
    Julian
  • Julian,

    Okay. Thanks for your participation. Its good to hear we both came to a similar solution. This indicates a promising design.

    ~Ramsey