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.

TM4C1292NCPDT: Problems with receiving data on the ISR of UART1

Part Number: TM4C1292NCPDT

Hello! I have a problem with receiving data on my ISR from UART1. From the main of the main program I send a response 
request to an external device. The device replies to me in a period of 500 milliseconds, the data that arrives is
received by the ISR, but the data arrives out of order since the response string from the external device has a defined
structure. Could you guide me if maybe I'm misconfiguring my interrupt?

int main(void)
{
   //configurar el reloj del microcontrolador
    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN |SYSCTL_USE_PLL |SYSCTL_CFG_VCO_240), 120000000);//120 MHz

    INIT_UART();
    CONFIG_UART();



    while(1)
   {
        SysCtlDelay(10000);
        sendStringUART1(S_P1);//?V913
        SysCtlDelay(19000000);//569ms
        GUARDA_ARREGLO();
     //   SysCtlDelay(22000000);//659ms
     //   sendStringUART1(S_P2);//?V914
      //  SysCtlDelay(22000000);//659ms
      //  GUARDA_ARREGLO();
      //  SysCtlDelay(22000000);//659ms
       // cabecera(UART0_BASE,0x50);
     /* SysCtlDelay(50000);
        sendStringUART1(S_P2);//?V914
        SysCtlDelay(500);
        GUARDA_ARREGLO();
        SysCtlDelay(500);
        cabecera(UART0_BASE,0x50); */
   }
}


void ISR_UART1(void)
{
  ui32Status = UARTIntStatus(UART1_BASE,true);
   UARTIntClear(UART1_BASE, ui32Status);        //limpia las banderas de interrupción?

   if (UARTCharsAvail(UART1_BASE))
     {
       char rxData = UARTCharGetNonBlocking(UART1_BASE); //recibe el dato UART entrante
           if(i_rx < 26) //si el contador es menor a 26
             {
               BUFFER_UART[i_rx]=rxData;// Guarda el dato recibido en un arreglo
               i_rx++;  //aumenta el contador en uno
             }
           impedimenta=1; //breakpoint
      }//cierra if(charsAvail)
   lumos=1; //breakpoint
}

  • Hello Mayra,

    Is i_rx declared a volatile variable as I assume it is a global variable? If not, it may not be accurately storing your counter spot and that could result in data being overwritten.

    If the counter has been declared volatile, then I'd be curious to see how the arrays actually look in terms of what the data that is wrong is versus what the actual correct data should be. That could give some clues as to what might be going on.

    Best Regards,

    Ralph Jacobi

  • Hello! Thank you very much for answering.
    
    My variable is declared as "int" as follows: "int i_rx=0".
    
    To view the data I use the "expressions" window. The correct data frame coming 
    from the external device starts with a header "=V913", then follows pressure data
    and a final data. I want to store the "=" sign at position 0 of the BUFFER_UART array,
    the "V" character at position 1 of the array, and so on, as shown in the "example 1" and
    "example 1_2" images, but in my case the data is saved in disorder as in the images "example 2" and "example 3". I hope I have explained myself correctly, I reiterate my thanks. Mayra
     example 1
    example 1



    example 1_2




    example 2




    example 3
  • Hello Marya,

    Okay so its not a volatile. This is probably your issue as if you don't declare a global variable a volatile then its modifications in an ISR may not persist.

    Please change the declaration of your index variable to this:

    volatile int i_rx=0

    See if that resolves the issue.

    Best Regards,

    Ralph Jacobi

  • Hello!
    Thanks for the suggestion, I already tried declaring the variable as volatile but it didn't work, 
    the data is still messed up (image "example 4") :( Mayra


  • Hello Mayra,

    Hmm. So the data starts with '=913' and then data follows. You mentioned 'final data' is sent. Is there an ending character to designate the data has finished being sent?

    Best Regards,

    Ralph Jacobi

  • Hello!
    
    Yes, the final character of the response strings is a carriage return.
    
    Mayra
  • Hi Mayra,

    Alright so what I think needs to be done to get your data sorted in the correct order is to add some more intelligence to the UART ISR where it is looking for the start and end characters.

    What I have done with following code is added in a boolean flag to track whether or not you should be receiving sensor data.

    If the flag is false and the UART receives an '=' character, then the flag is turned to true, the index is reset to 0, and the character is stored in the array. If the flag is false and any other character is received, nothing is done with it and it's effectively discarded.

    If the flag is true, and the UART any character except the carriage return, then it stores that in the buffer and increments the count. When the carriage return character is received, then the flag is set to false so the next character received after the carriage return now needs to be a '=' before your array data gets overwritten.

    This should allow the array to function as you want.

    Now since the limit of 26 characters was removed, I am not sure how much data you should be expecting, so you may want to lengthen the array to make sure you are only receiving 26 bytes as otherwise if you don't have enough array space reserved and the array storage overruns then your program could crash due to stack overflow.


    volatile int i_rx=0;
    volatile bool flag_rx=false;
    
    void ISR_UART1(void)
    {
        ui32Status = UARTIntStatus(UART1_BASE,true);
        UARTIntClear(UART1_BASE, ui32Status);        //limpia las banderas de interrupción?
    
        if (UARTCharsAvail(UART1_BASE))
        {
            char rxData = UARTCharGetNonBlocking(UART1_BASE); //recibe el dato UART entrante
            
            if (flag_rx)
            {
                if (rxData == 0x0D)
                {
                    flag_rx = false;
                }
                BUFFER_UART[i_rx]=rxData;// Guarda el dato recibido en un arreglo
                i_rx++;  //aumenta el contador en uno
            }
            else
            {
                if (rxData == '=')
                {
                    flag_rx = true;
                    i_rx = 0;
                    BUFFER_UART[i_rx]=rxData;// Guarda el dato recibido en un arreglo
                }
            }
            impedimenta=1; //breakpoint
        }//cierra if(charsAvail)
        lumos=1; //breakpoint
    }

    One last note is that you may need to add the following line if your program doesn't recognize the bool declaration.

    #include <stdbool.h>

    Let me know how this works for you.

    Best Regards,

    Ralph Jacobi

  • Hi!
    I already tried the suggested code, add the library for the boolean value, 
    declared 27 spaces for the BUFFER_UART array, since that is the length of the
    response string from the external device, now the "V" character in the header
    is at position 0 of the array (example 5) and the carriage return is stored in position 25 of the array,
    in addition to the "i_rx" counter remaining at a value of 19 (example 6).


    example 5



    example 6

  • Hi Mayra,

    Ahh, this is the problem when I can't test code easily myself haha.

    After initial data storage the i_rx counter wasn't incremented so it was at 0 when the next character was received.

    Easy fix at least:

                if (rxData == '=')
                {
                    flag_rx = true;
                    i_rx = 0;
                    BUFFER_UART[i_rx]=rxData;// Guarda el dato recibido en un arreglo
                    i_rx++;  //aumenta el contador en uno
                }

    Is the rest of the data coming in correctly now?

    By the way if you want to make your code a little cleaner, you can increment the index while getting the data like this:

    BUFFER_UART[i_rx++]=rxData;// Guarda el dato recibido en un arreglo

    That would be the functional equivalent of:

    BUFFER_UART[i_rx]=rxData;// Guarda el dato recibido en un arreglo
    i_rx++;  //aumenta el contador en uno

    Best Regards,

    Ralph Jacobi

  • Hi!
    I modified the code, it saves the complete data once and the "i_rx" counter varies from 0 to 20,
    when debugging in a function that requires all the data from the response of the external device
    and where the if condition so that they can be processed the incoming data must be "i_rx==26"
    (I put this to make sure that the 26 characters of the response string entered), BUFFER_UART has
    data stored from position 0 to position 19 and from there it is cut off (example 8)
     .


    even when BUFFER_UART has all the data saved, it doesn't enter the conditions I need because 
    the counter "i_rx" stays with a value of 20 :(
    
    
  • Hello Mayra,

    I see in your GUARDA_ARREGLO function you are clearing your index again. Have you stopped the UART communication and there is no data being possibly received at this time?

    If you receive any UART data from your sensor during the execution of GUARDA_ARREGLO then you could be resetting your index location in the middle of a transfer which would corrupt your data and prevent your index from reaching the value of 26.

    I would not use the index as a marker, but instead add a global flag that indicates the data has arrived in full and use that so you can avoid any manipulation of the index. This also avoids a situation where your index is reset sooner than you get to process the data.

    Best Regards,

    Ralph Jacobi

  • HI!
    
    The external device only answers me when I send a response request, in the main, 
    I send the response request through the "sendStringUART1" function and I wait 514 milliseconds for
    the external device to answer me before processing the data in the GUARDA_ARREGLO function .
    I'll try the global flag suggestion :)
    
    
  • Hello Mayra,

    I would be cautious about using a delay like that to make the determination. UART transmissions are not quick and you may not be able to accurately predict the time needed unless you are using a scope to determine the time between packets and then are adding some guard time on top of that. Generally using more deterministic methods like monitoring for the terminating character and setting a flag accordingly is recommended to have more reliable operation.

    Best Regards,

    Ralph Jacobi

  • Hello!
    
    Thank you very much for your suggestion, if it's not too much to ask,
    could you explain me a little more about how to avoid ISR stack overflow?

    Mayra
  • Hi Mayra,

    Unfortunately I have too many support requests to write up a summary on my end for that - but this article is a good start point: https://www.microcontrollertips.com/faq-avoiding-stack-overflow-embedded-processors/

    It focuses on some other areas but another way to overflow and corrupt a program is to have a buffer near the end of the stack or adjacent to critical variables that is defined as, say, 20 bytes, but then the software tries to write 24 bytes and so the processor just goes to the next memory locations and corrupts them which 'overflows' the buffer / stack which can crash the program.

    Best Regards,

    Ralph Jacobi

  • Hi!
    
    Thank you very much for the suggestions for my code, now the data arrives in order,
    but the chain arrives cut, fortunately the data that I require is in the part of
    the chain that is stored in BUFFER_UART, I have a feeling that it is due to a stack overflow. Thank you very much also for the article, I will read it. Mayra.
    
    
  • Hello Myra,

    It might be good to check the ui32RxIntStatus for RX error and handle such error as Overrun, Parity, Frame etc... For instance, if the Baud rate is not clocked nearly the same frequency on both ends unexpected errors can occur. That often tends to show up the higher the baudrate is set on both ends, limited to 150' cable run for RS232 and 1000' for RS423.

    Flush the buffer when an RX error occurs the data is no good anyway.

       
           uint32_t ui32RxErrorStatus;
           
        /* Load the RX error status bits */
        ui32RxErrorStatus = (UART_RXERROR_FRAMING|UART_RXERROR_PARITY
        						|UART_RXERROR_BREAK|UART_RXERROR_OVERRUN);
        						
            /* Durring interrupt check the RSR/ECR register for
             * RX errors: OE, BE, PE, FE  */
            if(ui32RxErrorStatus &= MAP_UARTRxErrorGet(UART3_BASE))
            {
                /* Clear all ECR error bits and return */
                MAP_UARTRxErrorClear(UART3_BASE);
                //
    #if DEBUG
                /* Print the error event message */
                UARTprintf("\n RxError: %x\n\n", ui32RxErrorStatus);
    #endif
            	/* Flush the RX buffer */
                for(i=0; i <= 32; i++)
                {
                	CcRxBuff[i] = 0;
                }
                //
    #if DEBUG
                UARTprintf("\n>> RxBuffFlush: \n");
    #endif
                //
                return;
            }    						

  • BTW: The bigger problem occurs in the for index++ loop reading 16-word FIFO beyond 16 even with bool flags. The loop needs to check the FE bit state or overrun errors will occur when the ready bit is not set after 16 words are received in the FIFO. Simply need to check for the conditions and pause for a period of cycles so the UART logic can catch up to buffering RX words >16. It seems the TM4C 150DMIPS CPU reads C code faster than UART hardware can process.  

    			/* Check RX FIFO is empty or timeout Interrupt.
    			 * The RX timeout interrupt is asserted when
    			 * the RX FIFO is not empty, and no further data
    			 * is received over a 32-bit period when the HSE bit is
    			 * clear or over a 64-bit period when the HSE bit is set.
    			 * Note:The receive timeout interrupt is cleared
    			 * either when the FIFO becomes empty through reading
    			 * all the data (or by reading the holding register) */
    			if(i >= 15 || (HWREG(UART3_BASE + UART_O_FR) & UART_FR_RXFE))
    			{
    				/* Pause here to wait for FIFO loading */
    				/* System clock 8.33ns, 1000=1.0ms, 1200=833us delay */
    				SysCtlDelay(SYSTEM_CLOCK / 1200);
    			}

  • hello!
    Thank you very much for your suggestions, I will test parity and frame errors, 
    I will also try to give the interrupt time so that the data can be read correctly.
    Excuse the question, can the provided code be added to my code without problems? Mayra.
  • can the provided code be added to my code without problems?

    You need to change the UART# and use your Buffer[] name. Add code snip to top of RX interrupt, see the (return) statement? Whenever RX error we clear the buffer exit the interrupt. The for loop FE bit delay assumes SYSCLK 120MHz and will wait to resume filling buffer with next word. The FIFO level is the word count loop INT pointer of the buffer until it is full. You could pad the end of TX data steam with 3x 0xFF Ralph mentions and check for 3x 0xFF in the RX interrupt loop to reset the buffer word count back to 16. 

    My buffer was always cleared upon 32 words deep. You may need to modify FE bit delay check >32 words. Add a static INT variable count 16 then increment variable++ and reset back to 16 words deep when the RX transfer ends. You could fill a very large buffer using that method. Reset the word count back to 16 when RX transfer ends by RX interrupt loop testing for 3x 0xFF. 

  • Thank you very much for your suggestions, 
    I will try them! :) Mayra