FreeRTOS Binary Semaphore – Update

Last week I taught a class on WICED WiFi programming, which also uses and RTOS.  During that class I yelled at a number of people for not using the RTOS mechanisms that I was showing them.  Which got me thinking, that in all of the versions of the FreeRTOS example program that I have shown you, I have used a polling loop for handling the UART.  This was kind of bugging me (at very least it was inconsistent).  In order to fix this I decided use the FreeRTOS Binary Semaphore to fix the problems by:

  • Modifying the FreeRTOS Binary Semaphore schematic and adding the UART ISR
  • Add a FreeRTOS Binary Semaphore called “uartSemaphore” that I will set in the UART ISR
  • Turn on interrupt handling for the UART when there is one or more characters in the buffer
  • In the ISR turn off interrupts and set the “uartSemaphore”
  • In the UART task, wait for the “uartSemaphore”, then handle all of the characters, then turn the UART interrupt back on

First I copied the “4-BinarySemaphore” project and called it “4a-BinarySemaphore”.  Then I modified the schematic to support the UART ISR.

FreeRTOS Binary Semaphore UART Schematic

Then I create a new global variable called uartSemaphore.

SemaphoreHandle_t uartSemaphore;

In main I initialize the semaphore and install the ISR Handler.

    uartSemaphore = xSemaphoreCreateBinary();
    uart_isr_StartEx(uart_isr_Handler); 

In the ISR Handler, I clear the interrupt from the UART.  Originally I thought that I could just clear the pending interrupt, set the semaphore and return.  However, when I returned from the ISR, there were still characters in the receive FIFO, which made the UART immediately pend the interrupt.  This kept me from ever leaving the ISR.  To fix this I turn off all interrupt sources.  In the UART Task I will turn the interrupts back on when the FIFO is empty again.

CY_ISR(uart_isr_Handler)
{
    uint32_t cause;
    cause = UART_GetRxInterruptSource();
    UART_ClearRxInterruptSource(cause);
    UART_SetRxInterruptMode(0);
    xSemaphoreGiveFromISR(uartSemaphore,NULL);
    
}

Finally I modify the UART Task to

  • Line 77 Wait for  the semaphore (which is set in the ISR)
  • Line 78 Loop until there is nothing left in the FIFO (UARTGetChar() will return 0 when there is nothing left)
  • Line 124: Turn the UART Interrupts back on
void UART_Task(void *arg)
{
    (void)arg;
    static char buff[500];
    UART_Start();
    clearScreen();
    UART_UartPutString("Started UART\n\r");
    char c;
    Color_t tempColor;
    
	while(1) {
        
        xSemaphoreTake(uartSemaphore,portMAX_DELAY);
        while( (c = UART_UartGetChar()) )
        {
            switch(c)
            {
                case 'a':
                UART_UartPutString("Working\n\r");
                break;
                
                case 't':
                    UART_UartPutString("********************************************\n\r");
                    UART_UartPutString("Task          State   Prio    Stack    Num\n\r"); 
                    UART_UartPutString("********************************************\n\r");
                    vTaskList(buff);
                    UART_UartPutString(buff);
                    UART_UartPutString("*********************************************\n\r");
       
                break;
                    
                case 'r':
                    tempColor = RED;
                    if(xQueueSend(colorQueue,&tempColor,0) != pdTRUE)
                        UART_UartPutString("queue error\n\r");
                break;
                    
                case 'b':
                    tempColor = BLUE;
                    if(xQueueSend(colorQueue,&tempColor,0) != pdTRUE)
                        UART_UartPutString("queue error\n\r");
                break;

                case 'g':
                    tempColor = GREEN;
                    if(xQueueSend(colorQueue,&tempColor,0) != pdTRUE)
                        UART_UartPutString("queue error\n\r");
                break;
                        
                case 's':
                        xSemaphoreGive(switchSemaphore);
                break;
                        
                case 'c':
                        clearScreen();
                break;

            }
        }
        UART_SetRxInterruptMode(UART_INTR_RX_NOT_EMPTY);
    }
}

This is a fairly simple modification to my program which will save power (potentially), will save CPU cycles (for sure) and is generally much better inline with the RTOS philosophy.

Topic Description
FreeRTOS: A PSoC4 FreeRTOS Port An introduction to making FreeRTOS work on PSoC 4
FreeRTOS PSoC Examples Using multiple tasks in FreeRTOS
FreeRTOS Queue Example Using a queue to communicate between tasks
PSoC 6 FreeRTOS - The First Example Booting FreeRTOS on PSoC 6
FreeRTOS Binary Semaphore An first example of a binary semaphore
FreeRTOS Binary Semaphore (Part 2) Removing polling in the UART Task
FreeRTOS Counting Semaphore An example using a counting semaphore
PSoC FreeRTOS Reading I2C Sensors with a shared I2C Bus
PSoC FreeRTOS Task Notify A light weight scheme to replace Semaphores
PSoC FreeRTOS Task Notification Values A very light weight method to transfer one word of information into a task

Recommended Posts

2 Comments

  1. In the uart_isr_Handler, the interrupt cause bits are cleared by the statement “UART_ClearRxInterruptSource(cause);” but the “NOT EMPTY” mask bit is automatically recovered as “1” by a hardware. In addition the interrupt mask bit which was cleared by the statement “UART_SetRxInterruptMode(0);” is also recovered as “1” anywhere. So, the uart_isr_Handler is invoked twice for one character.

    • You are correct. In retrospect I am not in love with what I did here.


Add a Comment

Your email address will not be published. Required fields are marked *