uart interrupts have one character delay

bones23 wrote on Saturday, January 19, 2008:

I am working on an str91x board and am having problems with my uart interrupts. The interrupt does not fire until the second character has been received from input. Essentially this causes the last character of any message sent to the device to sit ignored in the Rx FIFO until the next message is sent. Has anybody had this problem? If so how did you fix it? My code is below.

//serial set up function

xComPortHandle xSerialPortInitMedium( unsigned portLONG ulWantedBaud, unsigned portBASE_TYPE uxQueueLength, int comNum )
{
xComPortHandle xReturn;
UART_InitTypeDef xUARTx_Init;
GPIO_InitTypeDef GPIO_InitStructure;
xSemaphoreHandle semiphorTest;
xQueueHandle xRxedChartest;
UART_TypeDef* UARTx;
   
        switch(comNum){
        case 0:
          UARTx = UART0;
        break;
        case 1:
          UARTx = UART1;
        break;
        case 2:
          UARTx = UART2;
        break;
        }
       
        switch(comNum){
        case 0:
          xReturn = &COM0;
        break;
        case 1:
          xReturn = &COM1;
        break;
        case 2:
          xReturn = &COM2;
        break;
        }
       
        switch(comNum){
        case 0:
          xRxedChars0 = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed portCHAR ) );
          xRxedChartest = xRxedChars0;
          vSemaphoreCreateBinary( xTxFIFOSemaphore0 );
          semiphorTest = xTxFIFOSemaphore0;
          break;
        case 1:
          xRxedChars = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed portCHAR ) );
          xRxedChartest = xRxedChars;
          vSemaphoreCreateBinary( xTxFIFOSemaphore );
          semiphorTest = xTxFIFOSemaphore;
          break;
        case 2:
          xRxedChars2 = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed portCHAR ) );
          xRxedChartest = xRxedChars2;
          vSemaphoreCreateBinary( xTxFIFOSemaphore2 );
          semiphorTest = xTxFIFOSemaphore2;
          break;
        }

    /* If the queue/semaphore was created correctly then setup the serial port
    hardware. */
    if( ( xRxedChartest != serINVALID_QUEUE ) && ( semiphorTest != serINVALID_QUEUE ) )
    {
        /* Pre take the semaphore so a task will block if it tries to access
        it. */
                 switch(comNum){
                  case 0:
                    xSemaphoreTake( xTxFIFOSemaphore0, 0 );
                    break;
                  case 1:
                    xSemaphoreTake( xTxFIFOSemaphore, 0 );
                    break;
                  case 2:
                    xSemaphoreTake( xTxFIFOSemaphore2, 0 );
                    break;
                  }
       
        /* Configure the UART. */
        xUARTx_Init.UART_WordLength = UART_WordLength_8D;
        xUARTx_Init.UART_StopBits = UART_StopBits_1;
        xUARTx_Init.UART_Parity = UART_Parity_No;
        xUARTx_Init.UART_BaudRate = ulWantedBaud;
        xUARTx_Init.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
        xUARTx_Init.UART_Mode = UART_Mode_Tx_Rx;
        xUARTx_Init.UART_FIFO = UART_FIFO_Enable;

        /* Enable the UARTx Clock */
                 switch(comNum){
                  case 0:
                    SCU_APBPeriphClockConfig( __UART0, ENABLE );
                    break;
                  case 1:
                    SCU_APBPeriphClockConfig( __UART1, ENABLE );
                    break;
                  case 2:
                    SCU_APBPeriphClockConfig( __UART2, ENABLE );
                    break;
                  }

        portENTER_CRITICAL();
        {
            /* Configure the UART itself. */
                       switch(comNum){
                        case 0:
                          UART_DeInit( UART0 );       
              UART_Init( UART0, &xUARTx_Init );
              UART_ITConfig( UART0, UART_IT_Receive | UART_IT_Transmit, ENABLE );
              UART0->ICR = serCLEAR_ALL_INTERRUPTS;
              UART_LoopBackConfig( UART0, DISABLE );
                          UART_IrDACmd( IrDA0, DISABLE );
                          VIC_Config( UART0_ITLine, VIC_IRQ, 7 );
              VIC_ITCmd( UART0_ITLine, ENABLE );
                          UART_Cmd( UART0, ENABLE );
                          break;
                        case 1:
                          UART_DeInit( UART1 );       
              UART_Init( UART1, &xUARTx_Init );
              UART_ITConfig( UART1, UART_IT_Receive | UART_IT_Transmit, ENABLE );
              UART1->ICR = serCLEAR_ALL_INTERRUPTS;
              UART_LoopBackConfig( UART1, DISABLE );
                          UART_IrDACmd( IrDA1, DISABLE );
                          VIC_Config( UART1_ITLine, VIC_IRQ, 8 );
              VIC_ITCmd( UART1_ITLine, ENABLE );
                          UART_Cmd( UART1, ENABLE );
                          break;
                        case 2:
                          UART_DeInit( UART2 );       
              UART_Init( UART2, &xUARTx_Init );
              UART_ITConfig( UART2, UART_IT_Receive | UART_IT_Transmit, ENABLE );
              UART2->ICR = serCLEAR_ALL_INTERRUPTS;
              UART_LoopBackConfig( UART2, DISABLE );
                          UART_IrDACmd( IrDA2, DISABLE );
                          VIC_Config( UART2_ITLine, VIC_IRQ, 10 );
              VIC_ITCmd( UART2_ITLine, ENABLE );
                          UART_Cmd( UART2, ENABLE );
                          break;
                        }
                      
            lTaskWaiting = pdFALSE;
        }
        portEXIT_CRITICAL();
    }
    else
    {
        xReturn = ( xComPortHandle ) 0;
    }

    /* This demo file only supports a single port but we have to return
    something to comply with the standard demo header file. */
    return xReturn;
}

// IRQ handler
void UART0_IRQHandler( void )
{
signed portCHAR cChar;
portBASE_TYPE xTaskWokenByTx = pdFALSE, xTaskWokenByPost = pdFALSE;

    while( UART0->RIS &    mainRXRIS )
    {
         /*The interrupt was caused by a character being received.  Grab the
        character from the DR and place it in the queue of received
        characters.*/
        cChar = UART0->DR;
        xTaskWokenByPost = xQueueSendFromISR( xRxedChars0, &cChar, xTaskWokenByPost );
    }   
    if( UART0->RIS & mainTXRIS )
    {
        if( lTaskWaiting == pdTRUE )
        {
            /* This interrupt was caused by space becoming available on the Tx
            FIFO, wake any task that is waiting to post (if any). */
            xTaskWokenByTx = xSemaphoreGiveFromISR( xTxFIFOSemaphore0, xTaskWokenByTx );
            lTaskWaiting = pdFALSE;
        }
       
        UART0->ICR = mainTXRIS;
    }

    /* If a task was woken by either a character being received or a character
    being transmitted then we may need to switch to another task. */
    portEND_SWITCHING_ISR( ( xTaskWokenByPost || xTaskWokenByTx ) );
}

bones23 wrote on Saturday, January 19, 2008:

Update: On looking farther into the problem I found that the Rx FIFO is set to trigger the Rx interrupt only when it is two bytes full. I have not yet figured out how to configure it to trigger on having one byte. The trigger level is configured in the UART_IFLS register but the lowest I can configure it for is 2 bytes.

jorick23 wrote on Monday, January 21, 2008:

You need to handle the Receive Timeout interrupt in your serial interrupt handler.  This interrupt occurs after a certain period of time elapses without a character being received for 32 bit periods.  When it comes in, check the FIFO for any characters that need to be processed.

When you use this interrupt, you can set your FIFO trigger level higher.  Mine are set to the default of 1/2 full.

Here’s my serial IRQ handler.  You can see how the Receive Timeout interrupt is handled.  Warnings: 1) I’m using the STR750.  2) I’m using ST’s hardware library so you won’t see very many hardware accesses like UART0->DR.

(Don’t forget to activate the Receive Timeout interrupt in your initialization code.)

Notes:
1) mkapsUartBase [ziUART] contains a pointer to either UART0 or UART1.
2) mcPort is the currently active port (0 or 1).
3) mnIntRxChars is a character array that holds the received characters.

/******************************************************************************
*~    _Serial_IRQHandler_UART    UART interrupt handler                       *
*******************************************************************************
*                                                                             *
* This function is called by the two UART interrupts.  Since the code is the  *
* same for both of them, a single handler is used to process the interrupt.   *
*                                                                             *
* The following UART interrupts are handled by this function:                 *
*                                                                             *
*        Receive character                                                    *
*        Receive timeout                                                      *
*        Transmit FIFO space available                                        *
*                                                                             *
* RECEIVE: When a Receive Character or Receive Timeout interrupt is received, *
* all characters in the receive FIFO are sent to the Serial Receive task.     *
*                                                                             *
* TRANSMIT: An interrupt is generated as soon as space becomes available on   *
* the transmit FIFO.  No character is transmitted by this function but the    *
* task responsible for transmitting on this UART is awakened, and it will     *
* transmit the character.                                                     *
*                                                                             *
* Warning: This function runs at the interrupt level.                         *
*                                                                             *
*     Args:    ziUART            UART number (0 or 1)                         *
*                                                                             *
*     Return:  void                                                           *
*                                                                             *
******************************************************************************/

static void _Serial_IRQHandler_UART (int ziUART) {
   int xiIndex;                        // Received character string index
   uint16_t xuwErrorFlags;             // Error flags read
   portBASE_TYPE xlWakeTask = pdFALSE; // Task needs to wake flag

// ***** Transmit interrupt
// This interrupt was caused by space becoming available on the transmit FIFO.
// The task that is waiting to post is awakened by unlocking the transmit FIFO
// semaphore.

   if (UART_GetITStatus (mkapsUartBase [ziUART], UART_IT_Transmit)) {
      if (ziUART == mcPort) {
         xlWakeTask = xSemaphoreGiveFromISR (mapsSemaphoreTxUART [ziUART],
               xlWakeTask);
      }

// The transmit interrupt is cleared so that this function isn’t entered again.

      UART_ClearITPendingBit (mkapsUartBase [ziUART], UART_IT_Transmit);
   }

// ***** Receive interrupt
// The interrupt was caused either by a character being received or by nothing
// received for 32 bit periods.

   if (UART_GetITStatus (mkapsUartBase [ziUART],
         UART_IT_Receive | UART_IT_ReceiveTimeOut)) {

// Characters are retrieved from the UART and stored in the receive buffer
// until the receive FIFO is empty.  Note that reading from the data register
// also clears the interrupt.

      xiIndex = 0;
      while (!UART_GetFlagStatus (mkapsUartBase [ziUART],
            UART_FLAG_RxFIFOEmpty)) {

// If errors occurred while reading the character, the character is discarded
// and the error flags are reset by writing them back to the flag register.

         mnIntRxChars [xiIndex++] = UART_ReceiveData (mkapsUartBase [ziUART]);
         xwErrorFlags = mkapsUartBase [ziUART]->RSR;
         if (xwErrorFlags & 0x000F) {
            --xiIndex;
            mkapsUartBase [ziUART]->RSR = xwErrorFlags;
         }
      }

// The received string is terminated.

      mnIntRxChars [xiIndex] = ‘\0’;

// If this is the active serial port, the received string is sent to the serial
// receive task for processing.  Otherwise the characters are discarded.

      if (ziUART == mcPort && strlen (mnIntRxChars) > 0) {
         xlWakeTask = xQueueSendToBackFromISR (mpsQueueRxChar, mnIntRxChars,
               xlWakeTask);
      }

// The receive interrupt is cleared so that this function isn’t entered again.

      UART_ClearITPendingBit (mkapsUartBase [ziUART],
            UART_IT_Receive | UART_IT_ReceiveTimeOut);
   }

// ***** Switch tasks
// A task switch is made to the serial receive task to process the characters.

   portEND_SWITCHING_ISR (xlWakeTask);
}