Summary

In the previous article I showed you how to use the FreeRTOS task notification mechanism to replace a binary semaphore.  In this article I will build PSoC FreeRTOS Task Notification Value firmware.  Every FreeRTOS task has one built-in “uint32_t” which you can use to pass information between the task that sends the notification and the task that receives the notification.  In this article, instead of using the task notification as a binary semaphore, I will use it as a Queue of depth 1 that can hold one 32-bit word.  I will also show you a lesson that I learned about the interrupt registers in the SCB UART.

PSoC FreeRTOS Task Notification Value

I start by copying the project 9-TaskNotify and calling it 10-TaskNotifyValue.  I thought that it would be interesting to pass the cause of the UART interrupt to the UART Task, so that it could be told to the user.  To do this I call “xTaskNotifyFromISR” instead of “vTaskGiveFromISR”.   This function lets me set the value of the FreeRTOS Task notification value, is case to the bit mask of the cause of the UART interrupt.  The function also lets you specific if you want to

  • eNoAction – don’t do anything (this is what the xTaskNotifyFromISR does)
  • eSetBits – or the current notification value with the value you send
  • eIncrement – increment the notification value by 1 (in which case it ignore the value you send)
  • eSetValueWithOverwrite – replace the current notification value with the value passed in this function
  • eSetValueWithoutOverwrite – if there is no pending write, then write the value from this function into the notification value

In the UART_Task I take the value and then clear all of the bit (aka set it to 0) when I exit.

CY_ISR(uartHandler)
{
    uint32_t intrBits = UART_GetRxInterruptSourceMasked();
    UART_SetRxInterruptMode(0); // Turn off the Rx interrupt
    BaseType_t xHigherPriorityTaskWoken;
    xTaskNotifyFromISR( uartTaskHandle,intrBits,eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
void UART_Task( void *arg)
{
    (void)arg;
    char c;
    UART_Start();
    UART_SetCustomInterruptHandler(uartHandler);
    while(1)
    {
        uint32_t intrBits;
              
        xTaskNotifyWait( 0x00,         /* Don't clear any bits on entry. */
                         ULONG_MAX,          /* Clear all bits on exit. */
                         &intrBits, 
                         portMAX_DELAY );    /* Block indefinitely. */
      
        switch(intrBits)
        {
            case UART_INTR_RX_NOT_EMPTY:
                UART_UartPutString("Interrupt: FIFO Not Empty\n");
            break;
            
            case UART_INTR_RX_ERR:
                UART_UartPutString("Interrupt: Error\n");
            break;
                
            case UART_INTR_RX_FULL:
                UART_UartPutString("Interrupt: FIFO Full\n");
            break;
            
            default:
                UART_UartPutString("Interrupt: Unknown\n");
            break;
        }
        
        while(UART_SpiUartGetRxBufferSize())
        {
            c = UART_UartGetChar();
            UART_UartPutString("Char = ");
            UART_UartPutChar(c);
            UART_UartPutString("\n");
        }
        // re-enable the interrupt
        UART_ClearRxInterruptSource(UART_INTR_RX_NOT_EMPTY);
        UART_SetRxInterruptMode(UART_INTR_RX_NOT_EMPTY);
    }
}

New Learning

When I first wrote the program I had something “weird” happening.  Specifically it looked like I was getting the interrupt service routine called twice:

I originally wrote the code like this:

CY_ISR(uartHandler)
{
    uint32_t intrBits = UART_GetRxInterruptSourceMasked();
    UART_SetRxInterruptMode(0); // Turn off the Rx interrupt
    UART_ClearRxInterruptSource(UART_INTR_RX_NOT_EMPTY);
    BaseType_t xHigherPriorityTaskWoken;
    xTaskNotifyFromISR( uartTaskHandle,intrBits,eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

But, what happens is:

  1. Turn off interrupts
  2. Clear the interrupt source (meaning turn off the flag in the SCB that says there is a character in the Rx Buffer)
  3. One or so UART clock cycles later the flag is reset (because there is still something in the UART Rx Buffer)
  4. Send the notification to the UART Task
  5. The UART Task wakes up and processes the UART Rx Buffer
  6. The UART Task turns back on the interrupts
  7. The interrupt is called because the RX_NOT_EMPTY flag is still set (it is set until it is clear)
  8. The interrupt handler clears the flag (which isn’t reset this time because there is nothing in the Rx buffer)
  9. The interrupt handler sends the notification
  10. The UART Task wakes up again… prints out the Flag..
  11. The UART Task tries to read out of the Rx Buffer… but there isn’t anything in it.

Each time I start thinking that I know what I am doing, I find something else to learn.  I suppose that is what makes this whole thing fun though.

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

No comment yet, add your voice below!


Add a Comment

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