UART16550 FreeRTOS Serial Driver: Queue Full

Hi
I’m very new to FreeRTOS.
I’m using a Xilinx Virtex-5 custom board with a ppc440 processor. I’m trying to write a serial driver for this board which has a UART16550 IP core instance. I took the demo project in ML507 board, which has a uartlite instance as a reference. In my project transmission is happening well, but the reception which is using an interrupt is not working properly. The code hangs after receiving the first character. I’ll provide the code I modified below. The issues I’m facing are 1. In reception the code hangs after recieving the first character. Using debug points I understood that the queue is full always after recieving the first character and it is going to the port yield function or just getting blocked somewhere in the queue. 2. When I’m giving the XUartNs550_EnableIntr function the tasks are not even getting started. But after disabling it, the scheduler starts running, but only transmission is happening. 3. I’m not sure about the registers I’m using in the interrupt handler function vSerialISR_. I just mapped it all from xparameters.h according to the UARTlite demo which was already available in the FreeRTOS repository. I’m also doubtful about the UartNs550 initialization I’m doing in the xSerialPortInitMinimal_1 function. I’m using the heap_2.c memory model and then I have a ddr2 memory of size 256MB. Any help will be really useful. /***********************SERIAL.C FILE**************************/
/*
    BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER FOR UART
*/ /* Scheduler includes. */
#include “FreeRTOS.h”
#include “queue.h”
#include “task.h”
#include “stdio.h” /* Demo application includes. */
#include “serial_1.h” /* Library includes. */
#include “xparameters.h”
#include “xuartns550.h”
#include “xuartns550_l.h” /*—————————————*/ Xuint16 Options; /* Queues used to hold received characters, and characters waiting to be
transmitted. */
static xQueueHandle xRxedChars_1;
static xQueueHandle xCharsForTx_1; /* Structure that maintains information on the UART being used. */
static XUartNs550 xUART_1; /*
* Sample UART interrupt handler.  Note this is used to demonstrate the kernel
* features and test the port – it is not intended to represent an efficient
* implementation.
*/
static void vSerialISR_1( XUartNs550 *pxUART_1 ); /*—————————————*/ /*** unsigned long device_id is just a value I’m passing from main function as an option for different ports. Main file is include at the bottom *****/ xComPortHandle xSerialPortInitMinimal_1( unsigned long ulWantedBaud, unsigned portBASE_TYPE uxQueueLength, unsigned long device_id )
{
    /* NOTE:  the baud value passed to this function has no effect. */
    ( void ) ulWantedBaud;     /* Create the queues used to hold Rx and Tx characters. */
    xRxedChars_1 = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed char ) );
    xCharsForTx_1 = xQueueCreate( uxQueueLength + 1, ( unsigned portBASE_TYPE ) sizeof( signed char ) );     /* Only initialise the UART if the queues were created correctly. */
    if( ( xRxedChars_1 != NULL ) && ( xCharsForTx_1 != NULL ) )
    {
       XUartNs550_Initialize( &xUART_1, XPAR_XPS_UART16550_0_DEVICE_ID );
        XUartNs550_SetBaud(XPAR_XPS_UART16550_0_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ, 9600);
        XUartNs550_SetOptions( &xUART_1, XUN_OPTION_RESET_TX_FIFO);
        XUartNs550_SetOptions( &xUART_1, XUN_OPTION_RESET_RX_FIFO);
        XUartNs550_SetLineControlReg(XPAR_XPS_UART16550_0_BASEADDR, XUN_LCR_8_DATA_BITS);
        XUartNs550_DisableIntr( XPAR_XPS_UART16550_0_BASEADDR );
        vPortSetupInterruptController();
        Options = XUN_OPTION_DATA_INTR | XUN_OPTION_FIFOS_ENABLE;
        XUartNs550_SetOptions( &xUART_1, Options);         
    if( xPortInstallInterruptHandler( XPAR_XPS_INTC_0_XPS_UART16550_0_IP2INTC_IRPT_INTR , ( XInterruptHandler )vSerialISR_1, (void *)&xUART_1 ) == pdPASS )
        {
            /* xPortInstallInterruptHandler() could fail if
            vPortSetupInterruptController() has not been called prior to this
            function. */
        XUartNs550_EnableIntr( XPAR_XPS_UART16550_0_BASEADDR );
        }     } /* There is only one port so the handle is not used. */
    return ( xComPortHandle ) 0;
}
/*—————————————*/ signed portBASE_TYPE xSerialGetChar_1( xComPortHandle pxPort, signed char *pcRxedChar, portTickType xBlockTime, unsigned long device_id )
{
    /* The port handle is not required as this driver only supports one UART. */
    ( void ) pxPort;    /* Get the next character from the buffer.  Return false if no characters
    are available, or arrive before xBlockTime expires. */
    if( xQueueReceive( xRxedChars_1, pcRxedChar, xBlockTime ) )
    {
        return pdTRUE;
    }
    else
    {
        return pdFALSE;
    }
}
/*—————————————*/ signed portBASE_TYPE xSerialPutChar_1( xComPortHandle pxPort, signed char cOutChar, portTickType xBlockTime, unsigned long device_id )
{
portBASE_TYPE xReturn = pdTRUE;     /* Just to remove compiler warning. */
    ( void ) pxPort;     portENTER_CRITICAL();
    {
        /* If the UART FIFO is full we can block posting the new data on the
        Tx queue. */
        if( !(XUartNs550_IsTransmitEmpty(XPAR_XPS_UART16550_0_BASEADDR)) )
        {
            if( xQueueSend( xCharsForTx_1, &cOutChar, xBlockTime ) != pdPASS )
            {
                xReturn = pdFAIL;
            }
        }
        /* Otherwise, if there is data already in the queue we should add the
        new data to the back of the queue to ensure the sequencing is
        maintained. */
        else if( uxQueueMessagesWaiting( xCharsForTx_1 ) )
        {
            if( xQueueSend( xCharsForTx_1, &cOutChar, xBlockTime ) != pdPASS )
            {
                xReturn = pdFAIL;
            }          
        }
        /* If the UART FIFO is not full and there is no data already in the
        queue we can write directly to the FIFO without disrupting the
        sequence. */
        else
        {
            XIo_Out32( XPAR_XPS_UART16550_0_BASEADDR + XUN_THR_OFFSET, cOutChar );
        }
    }
    portEXIT_CRITICAL();     return xReturn;
}
/*—————————————*/ void vSerialClose_1( xComPortHandle xPort )
{
    /* Not supported as not required by the demo application. */
    ( void ) xPort;
}
/*—————————————*/ static void vSerialISR_1( XUartNs550 *pxUART_1 )
{ unsigned long ulISRStatus;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, lDidSomething;
char cChar;     /* Just to remove compiler warning. */
    ( void ) pxUART_1;
    do
    {
        lDidSomething = pdFALSE;     ulISRStatus = XIo_In32( XPAR_XPS_UART16550_0_BASEADDR + XUN_IER_OFFSET );  /* I’m not sure which register to use here. Recieving 1 character when I’m using XUN_IER_OFFSET */      if( ( ulISRStatus & XUN_IER_RX_DATA ) != 0 )   
        {
            /* A character is available – place it in the queue of received
            characters.  This might wake a task that was blocked waiting for
            data. */
            cChar = ( char ) XIo_In32( XPAR_XPS_UART16550_0_BASEADDR + XUN_RBR_OFFSET );
          
            xQueueSendToFrontFromISR( xRxedChars_1, &cChar, &xHigherPriorityTaskWoken );
            lDidSomething = pdTRUE;
          
        }
      
    if( ( ulISRStatus & XUN_IER_TX_EMPTY ) != 0 )
        {
            /* There is space in the FIFO – if there are any characters queue for
            transmission they can be sent to the UART now.  This might unblock a
            task that was waiting for space to become available on the Tx queue. */
            if( xQueueReceiveFromISR( xCharsForTx_1, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
            {
                   XIo_Out32(XPAR_XPS_UART16550_0_BASEADDR +  XUN_THR_OFFSET, cChar );
                   lDidSomething = pdTRUE;
            }          
        }
    } while( lDidSomething == pdTRUE );     /* If we woke any tasks we may require a context switch. */
    if( xHigherPriorityTaskWoken )
    {
        portYIELD_FROM_ISR();
    }
  
} /*********************COMTEST.C FILE*************************************/ void vAltStartComTestTasks_1( unsigned portBASE_TYPE uxPriority, unsigned long ulBaudRate, unsigned portBASE_TYPE uxLED, unsigned long device_id )
{
    /* Initialise the com port then spawn the Rx and Tx tasks. */
  
    xSerialPortInitMinimal_1( ulBaudRate, comBUFFER_LEN, device_id );     /* The Tx task is spawned with a lower priority than the Rx task. */
    xTaskCreate( vComTxTask_1, ( signed char * ) “COMTx”, comSTACK_SIZE, NULL, (uxPriority-1), ( xTaskHandle * ) NULL );
    xTaskCreate( vComRxTask_1, ( signed char * ) “COMRx”, comSTACK_SIZE, NULL, (uxPriority), ( xTaskHandle * ) NULL );
} static portTASK_FUNCTION( vComTxTask_1, pvParameters )
{
signed char cByteToSend;
portTickType xTimeToWait; /* Just to stop compiler warnings. */
    ( void ) pvParameters;     for( ;; )
    {
        /* Simply transmit a sequence of characters from comFIRST_BYTE to comLAST_BYTE. */
   for( cByteToSend = comFIRST_BYTE; cByteToSend <= comLAST_BYTE; cByteToSend++ )
        {
            if( xSerialPutChar_1( xPort, cByteToSend, comNO_BLOCK, 1 ) == pdPASS )
            {
            }
        }         /* We have posted all the characters in the string – wait before
        re-sending.  Wait a pseudo-random time as this will provide a better
        test. */
        xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;         /* Make sure we don’t wait too long… */
        xTimeToWait %= comTX_MAX_BLOCK_TIME;         /* …but we do want to wait. */
        if( xTimeToWait < comTX_MIN_BLOCK_TIME )
        {
            xTimeToWait = comTX_MIN_BLOCK_TIME;
        }         vTaskDelay( xTimeToWait );
    }
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*—————————————*/ static portTASK_FUNCTION( vComRxTask_1, pvParameters )
{
signed char cExpectedByte, cByteRxed;
portBASE_TYPE xResyncRequired = pdFALSE, xErrorOccurred = pdFALSE;
/* Just to stop compiler warnings. */
    ( void ) pvParameters;     for( ;; )
    {
       /* We expect to receive the characters from comFIRST_BYTE to comLAST_BYTE in an incrementing order.  Loop to receive each byte. */
        for( cExpectedByte = comFIRST_BYTE; cExpectedByte <= comLAST_BYTE; cExpectedByte++ )
        {
          /* Block on the queue that contains received bytes until a byte is
            available. */
      
            if( xSerialGetChar_1( xPort, &cByteRxed, comRX_BLOCK_TIME, 1 ) )
            {
          
            printf(”n data : %c n”,cByteRxed);
                /* Was this the byte we were expecting? otherwise we are out on sync and should break out of the loop
                until the expected character sequence is about to restart. */
                if( cByteRxed == cExpectedByte )
                {
                }
                else
                {
                    xResyncRequired = pdTRUE;
                  break; /*lint !e960 Non-switch break allowed. */
                }
            }
       }           /* Did we break out of the loop because the characters were received in
        an unexpected order?  If so wait here until the character sequence is
        about to restart. */
    if( xResyncRequired == pdTRUE )
        {
            while( cByteRxed != comLAST_BYTE )
            {
                /* Block until the next char is available. */
                xSerialGetChar_1( xPort, &cByteRxed, comRX_BLOCK_TIME,1 );
            }             /* Note that an error occurred which caused us to have to resync.
            We use this to stop incrementing the loop counter so
            sAreComTestTasksStillRunning() will return false – indicating an
            error. */
            xErrorOccurred++;             /* We have now resynced with the Tx task and can continue. */
            xResyncRequired = pdFALSE;
        }
    else
        {
           if( xErrorOccurred < comTOTAL_PERMISSIBLE_ERRORS )
            {
                /* Increment the count of successful loops.  As error occurring (i.e. an unexpected character being received) will prevent this counter being incremented for the rest of the execution.   Don’t worry about mutual exclusion on this variable – it doesn’t really matter as we just want it to change. */
                uxRxLoops++;
            }
        }
    }
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*—————————————*/ /**************************************MAIN.C*************************************/ int main( void )
{
/* Must be called prior to installing any interrupt handlers! */
vPortSetupInterruptController(); /* In this case prvSetupHardware() just enables the caches */
prvSetupHardware(); /* Start the standard demo application tasks.  Note that the baud rate used
by the comtest tasks is set by the hardware, so the baud rate parameter
passed has no effect. */ vAltStartComTestTasks_1( mainCOM_TEST_PRIORITY, mainBAUD_SET_IN_HARDWARE, mainCOM_TEST_LED , 1); /* Now start the scheduler.  Following this call the created tasks should
be executing. */
vTaskStartScheduler(); /* vTaskStartScheduler() will only return if an error occurs while the
idle task is being created. */
for( ;; ); return 0;
} Thanks & Regards
LEO K YOHAN

UART16550 FreeRTOS Serial Driver: Queue Full

I can’t really comment on peripherals of individual microcontrollers, especially peripherals I haven’t used myself.  All I can do is discuss FreeRTOS issues.  Your use of FreeRTOS looks ok, but I have no idea about the configuration of or use of the UART shown in your code. Using queues to send and receive characters is only really advisable when you have a very low communication overhead.  If you have faster communications the a DMA (as demonstrated by the recent Atmel integration), or circular buffer receive/zero copy transmit is preferable (as demonstrated by the FreeRTOS+IO code). Regards.

UART16550 FreeRTOS Serial Driver: Queue Full

Hi Richard Thank you for the prompt answer and I know the issue is bit specific to Xilinx. Sorry for that. In this code when I start the scheduler after enabling the interrupt, the tasks are not even getting executed. But if I disable the interrupt and call the vTaskStartScheduler(), the tasks are getting executed and also the interrupts are automatically getting enabled. Where do you think the issue might be, in the scheduler or in the interrupt initialization or is it like that in FreeRTOS? any suggestion about this issue? Really sorry if the question itself is wrong.. :) im kinda new to this field and this is just my observation when i was trying to execute this code with debug points. regards
LEO

UART16550 FreeRTOS Serial Driver: Queue Full

Calling FreeRTOS API functions during initialisation deliberately leaves interrupts disabled.  This is because the execution of an interrupt that calls a FreeRTOS API function, or attempts a context switch, before the scheduler has started will most likely cause a crash.  All the ports automatically enable interrupts when the first task starts executing. Regards.