element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Blog PSoC 6 and ModusToolbox: UART receiver with FreeRTOS
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Embedded and Microcontrollers to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 13 Mar 2021 10:26 PM Date Created
  • Views 7860 views
  • Likes 10 likes
  • Comments 21 comments
  • infineon
  • rtos
  • psoc 6
  • psoc6
  • freertos
  • cypress
Related
Recommended

PSoC 6 and ModusToolbox: UART receiver with FreeRTOS

Jan Cumps
Jan Cumps
13 Mar 2021

The steps to integrate incoming UART data with FreeRTOS on a PSoC 6.

The focus is on saving processor power. The design does not poll for incoming data.

It yields all powers to the RTOS scheduler (possibly going to low power mode) until a trigger fires after a defined number of bites arrived at the UART input.

image

 

Define a UART with TRIGGER Support

 

In the initial part of the exercise, I follow the PSoC 6 documentation for Serial Communication Block configuration.

You will recognise 90% of it. The only changes I made to their example is:

  • Deal with a board that has a 100 MHz peripheral clock instead of a 50 MHz one.
  • Make a 9600 baud UART instead of a 115200 one.
  • every hardware allocation is a define, so that it's easy to switch between all available UART ports,  pins and clock dividers.
  • integrate with FreeRTOS

 

The PSoC 6 documentation is good and linked above, so I will not repeat any of that. Just show how I changed it.

The initialisation code is fully RTOS agnostic. But before going to it, let's check the constants I defined to make it easy to change ports and speed:

 

/* dependent on the SCB and pins used */
#define UART_SCB SCB5
#define UART_PORT       P5_0_PORT
#define UART_RX_NUM     P5_0_NUM
#define UART_TX_NUM     P5_1_NUM
#define UART_RX_PIN     P5_0_SCB5_UART_RX
#define UART_TX_PIN     P5_1_SCB5_UART_TX
#define UART_DIVIDER_NUMBER 5U

#define UART_CLOCK PCLK_SCB5_CLOCK

  /* Assign UART interrupt number and priority */
#define UART_INTR_NUM        ((IRQn_Type) scb_5_interrupt_IRQn)
#define UART_INTR_PRIORITY   (7U)

/* dependent on the BAUD used */
// jc 20210313: 9600 baud needs a 16 bit divider
/* UART desired baud rate is 9600 bps
 * The UART baud rate = (clk_scb / Oversample).
 * For clk_peri = 100 MHz, select divider value 864 and get SCB clock = (100 MHz / 864) = 115.7 kHz.
 * Select Oversample = 12. These setting results UART data rate = 115.7 kHz / 12 = 9645 bps.
 */

#define UART_CLK_DIV_TYPE     (CY_SYSCLK_DIV_16_BIT)
#define UART_CLK_DIV_NUMBER   (UART_DIVIDER_NUMBER)
#define UART_OVERSAMPLE 12UL
#define UART_DIVISION 863UL

/* application dependent UART settings */
#define UART_BUFFER_SIZE 16

 

Now the init code. I call it in my main(), after the board configuration and before the interrupts are activated:

  /* Initialize the device and board peripherals */
  result = cybsp_init() ;
  if (result != CY_RSLT_SUCCESS)
  {
    CY_ASSERT(0);
  }
  initUART();
  __enable_irq();

 

Here is the code. It uses SCB 5 (the debug port) at 9600 baud. With interrupt enable.

 

/* Allocate context for UART operation */
cy_stc_scb_uart_context_t uartContext;

void initUART() {
  // https://cypresssemiconductorco.github.io/psoc6pdl/pdl_api_reference_manual/html/group__group__scb__uart.html


  /* Populate configuration structure */
  const cy_stc_scb_uart_config_t uartConfig = {
      .uartMode                   = CY_SCB_UART_STANDARD,
      .enableMutliProcessorMode   = false,
      .smartCardRetryOnNack       = false,
      .irdaInvertRx               = false,
      .irdaEnableLowPowerReceiver = false,
      .oversample                 = UART_OVERSAMPLE,
      .enableMsbFirst             = false,
      .dataWidth                  = 8UL,
      .parity                     = CY_SCB_UART_PARITY_NONE,
      .stopBits                   = CY_SCB_UART_STOP_BITS_1,
      .enableInputFilter          = false,
      .breakWidth                 = 11UL,
      .dropOnFrameError           = false,
      .dropOnParityError          = false,
      .receiverAddress            = 0UL,
      .receiverAddressMask        = 0UL,
      .acceptAddrInFifo           = false,
      .enableCts                  = false,
      .ctsPolarity                = CY_SCB_UART_ACTIVE_LOW,
      .rtsRxFifoLevel             = 0UL,
      .rtsPolarity                = CY_SCB_UART_ACTIVE_LOW,
      .rxFifoTriggerLevel  = 0UL,
      .rxFifoIntEnableMask = 0UL,
      .txFifoTriggerLevel  = 0UL,
      .txFifoIntEnableMask = 0UL,
  };
  /* Configure UART to operate */
  (void) Cy_SCB_UART_Init(UART_SCB, &uartConfig, &uartContext);


  /* Assign pins for UART on SCBx */


  /* Connect SCB UART function to pins */
  Cy_GPIO_SetHSIOM(UART_PORT, UART_RX_NUM, UART_RX_PIN);
  Cy_GPIO_SetHSIOM(UART_PORT, UART_TX_NUM, UART_TX_PIN);
  /* Configure pins for UART operation */
  Cy_GPIO_SetDrivemode(UART_PORT, UART_RX_NUM, CY_GPIO_DM_HIGHZ);
  Cy_GPIO_SetDrivemode(UART_PORT, UART_TX_NUM, CY_GPIO_DM_STRONG_IN_OFF);


  /* Assign divider type and number for UART */


  /* Connect assigned divider to be a clock source for UART */
  Cy_SysClk_PeriphAssignDivider(UART_CLOCK, UART_CLK_DIV_TYPE, UART_CLK_DIV_NUMBER);




  // set baud
  Cy_SysClk_PeriphSetDivider   (UART_CLK_DIV_TYPE, UART_CLK_DIV_NUMBER, UART_DIVISION);
  Cy_SysClk_PeriphEnableDivider(UART_CLK_DIV_TYPE, UART_CLK_DIV_NUMBER);


  /* Populate configuration structure (code specific for CM4) */
  cy_stc_sysint_t uartIntrConfig =
  {
      .intrSrc      = UART_INTR_NUM,
      .intrPriority = UART_INTR_PRIORITY,
  };
  /* Hook interrupt service routine and enable interrupt */
  (void) Cy_SysInt_Init(&uartIntrConfig, &UART_Isr);
  NVIC_EnableIRQ(UART_INTR_NUM);


  /* Enable UART to operate */
  Cy_SCB_UART_Enable(UART_SCB);
}

 

Interrupt Handler with FreeRTOS brain

 

The interrupt handler is, by my design choice, FreeRTOS aware. Based on the FreeRTOS tutorial for sending data, but I changed it to work on the receive side.

It's written to release a task as soon as an agreed number of characters arrived:

 

// UART interrupt handler
void UART_Isr() {
  Cy_SCB_UART_Interrupt(UART_SCB, &uartContext);

  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  configASSERT( xTaskToNotify != NULL );

  /* Notify the task that the receive is complete. */
  vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );
  /* There are no receive in progress, so no tasks to notify. */
  xTaskToNotify = NULL;

  /* If xHigherPriorityTaskWoken is now set to pdTRUE then a
  context switch should be performed to ensure the interrupt
  returns directly to the highest priority task.  The macro used
  for this purpose is dependent on the port in use and may be
  called portEND_SWITCHING_ISR(). */
  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

 

The design has a FreeRTOS task that patiently waits on a notification. The code above in the interrupt is the code that generates that notification.

It fires exactly at the right time: when the agreed number of bytes are available and have been moved to the read buffer.

What happens is:

  • the default interrupt processing for the PSoC 6 is done at line 03.
  • We ask FreeRTOS to notify the waiting task that the agreed bytes have been received, at line 10.
  • Then we inform FreeRTOS that our interrupt handler has finished.

I did not invent this. It's boilerplate RTOS interrupt processing.

 

The PSoC 6 Function to enable UART read with interrupt handling

 

Then the read function that primes the PSoC 6 UART read function, every time the buffer has been filled:

 

// UART activate a receive with interrupt. Wait for ever for UART_BUFFER_SIZE bytes
void UART_receive() {
  /* At this point xTaskToNotify should be NULL as no receive
  is in progress.  A mutex can be used to guard access to the
  peripheral if necessary. */
  configASSERT( xTaskToNotify == NULL );

  /* Store the handle of the calling task. */
  xTaskToNotify = xTaskGetCurrentTaskHandle();

  /* Start receive operation (do not check status) */
  (void) Cy_SCB_UART_Receive(UART_SCB, rxBuffer, sizeof(rxBuffer), &uartContext);
}

 

The first two lines of code are again FreeRTOS common, to set the deactivation of the UART RTOS task until the read received its data over the UART.

The last line actually activates the PSoC 6 read function. Non-blocking, with the trigger primed. It tells the SCB on the PSoC to accept data by itself, buffer it and trigger the trigger when the agreed amount of bytes arrived.

 

FreeRTOS Task that wakes up when a buffer of UART data is received

 

Now the FreeRTOS task:

 

void uart_task(void *pvParameters) {

  /* To avoid compiler warnings */
  (void)pvParameters;
  uint32_t ulNotificationValue;

  while (true) {
    /* Start the receiving from UART. */
    UART_receive();
    /* Wait to be notified that the receive is complete.  Note
        the first parameter is pdTRUE, which has the effect of clearing
        the task's notification value back to 0, making the notification
        value act like a binary (rather than a counting) semaphore.  */
    ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY  );

    if( ulNotificationValue == 1 )
    {
      /* Blocking wait until buffer is full */
      // in this example, it will never handle because the UART interrupt fires exaxtly when the buffer is full
      while (0UL != (CY_SCB_UART_RECEIVE_ACTIVE & Cy_SCB_UART_GetReceiveStatus(UART_SCB, &uartContext))) {}
      /* Handle received data */
    }
  }
}

 

It calls the non-blocking read function we just defined a step earlier. Then it goes to sleep at line 14  because the read function holds it up.

Once the PSoC UART  gets the agreed number of characters, the trigger fires and calls this function in the handler (see a few steps above where the ,interrupt handler is described):

 

vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );

 

This will push our FreeRTOS task over line 14. The received data sits in the rxBuffer by that time. This works so well together that it's almost magic. Without burning the microcontroller ticks to poll the UART.

You can then put your logic here:

 

   if( ulNotificationValue == 1 )
    {
      /* Blocking wait until buffer is full */
      // in this example, it will never handle because the UART interrupt fires exaxtly when the buffer is full
      while (0UL != (CY_SCB_UART_RECEIVE_ACTIVE & Cy_SCB_UART_GetReceiveStatus(UART_SCB, &uartContext))) {}
      /* Handle received data */
    }

 

Line 05 is not really needed, because we already know all data arrived. The task notification told it. Because it was generated by the interrupt. And the interrupt fires when the agreed number of bytes are in.

 

In a project I'm working on with balearicdynamics, this will be the place where we push that received data on a FreeRTOS queue.

 

image

 

There are a few globals I defined for this exercise. They are only used in one .c file. If you prefer no global variables, you can use the established C practices to avoid them.

 

cy_stc_scb_uart_context_t uartContext;
static TaskHandle_t xTaskToNotify = NULL;
const UBaseType_t xArrayIndex = 1;
uint8_t rxBuffer[UART_BUFFER_SIZE];

 

 

When you try to use the debugger on the PSoC 6, and it does not break at your breakpoints, here are a few watch outs:

 

optimiser settings

The GCC optimiser is active. Even if your make file has: CONFIG=Debug

The documents seem to tell otherwise, but trust me on this one.

This will cause that the debugger may just ignore the source line you put a breakpoint on, because it's optimised out by the smart compiler.

You can override this by changing the line above to: CONFIG=Custom

and amend this line: CFLAGS=-O0

 

debugging with FreeRTOS and OpenOCD (the debugger that comes with many PSoC 6 kits)

To allow the debugger to deal with FreeRTOS, you have to prevent that your jobs take the highest priority.

You do that by adding these lines in your code before yielding to the scheduler:

 

  /* This enables RTOS aware debugging in OpenOCD. */

  uxTopUsedPriority = configMAX_PRIORITIES - 1;

 

PSoC 6 series
Create a Project with its own Board- and Module configurations
Low Power Management - prepare the board for current measurements
Power consumption without WiFi or Bluetooth
Create a FreeRTOS 10.3 Project
UART receiver with FreeRTOS
FreeRTOS message queue
UART receiver with FreeRTOS - Deep Sleep support
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 4 years ago in reply to skruglewicz +2
    I found a version that's still online: https://sdkdocs.cypress.com/html/psoc6-with-anycloud/en/latest/api/psoc-base-lib/pdl/group__group__scb__uart.html The documentation is also available locally for…
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps +2
    skruglewicz , this is the new proper online location: https://infineon.github.io/psoc6pdl/pdl_api_reference_manual/html/group__group__scb__uart.html I'm adjusting the original post.
  • balearicdynamics
    balearicdynamics over 4 years ago +1
    Great piece of cooperation Enrico
Parents
  • brandiware
    brandiware over 4 years ago

    Hello Jan,

     

    thanks for your swift answer. I think it is not an issue with your task coordination logic.

    I rather have difficulties addressing the UART which I configured using the Device Configurator

    on SCB4. Seems like I do not register the interrupt correctly as the MCU throws a "cannot address memory" error when

    Cy_SCB_UART_Interrupt(scb_4_HW, &uartContext); is called.

    I have not found any working example that uses an UART configured with the Device Configurator. Any idea on how to

    correctly address this resource ? Could be that scb_4_HW, which is generated by the Configurator, is not the correct

    address to reach the configured UART ?

     

    Thanks and best regards

    Stefan

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • brandiware
    brandiware over 4 years ago

    Hello Jan,

     

    thanks for your swift answer. I think it is not an issue with your task coordination logic.

    I rather have difficulties addressing the UART which I configured using the Device Configurator

    on SCB4. Seems like I do not register the interrupt correctly as the MCU throws a "cannot address memory" error when

    Cy_SCB_UART_Interrupt(scb_4_HW, &uartContext); is called.

    I have not found any working example that uses an UART configured with the Device Configurator. Any idea on how to

    correctly address this resource ? Could be that scb_4_HW, which is generated by the Configurator, is not the correct

    address to reach the configured UART ?

     

    Thanks and best regards

    Stefan

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to brandiware

    Because you get a memory exception, it can be related to overrunning a buffer.

    The sources for libraries are available. If you put a breakpoint at this line,

    Cy_SCB_UART_Interrupt(scb_4_HW, &uartContext)

    you can first check if uartContext isn't null.

    and if not, step into it with the debugger and try to see where the error happens.

     

    Check that you didn't declare uartContext twice, one time global, and one time in a function.

    It should be declared globally at the top of the C file, and then used in the functions.

    If you declare another variable named uartContext in one of the functions, then it would not see the global one.

     

     

    reading the UART should be kicked off by this code which is executed in a separate task:

    Are you sure that the code kicked off to write , in that separate task, has access to the same uartContext that you use in the init,  read  and interrupt functions?

    Or did you declare one in that source file too?

    In that case, it would be a wrong context. It has to be an object shared by all calls to a single AURT port.

     

     

     

    I did not use the device configurator in my code, for two reasons:

    I started from the help example, and that is using code to configure the UART, and I wanted to be able to switch between UARTs by using a define.

     

    I used these settings to switch between 1 and 5:

     

    When using UART1:

     

    // SCB 1 p10_0 rx p10_1 tx
    
    
    #define UART_SCB SCB1
    #define UART_PORT       P10_0_PORT
    #define UART_RX_NUM     P10_0_NUM
    #define UART_TX_NUM     P10_1_NUM
    #define UART_RX_PIN     P10_0_SCB1_UART_RX
    #define UART_TX_PIN     P10_1_SCB1_UART_TX
    #define UART_DIVIDER_NUMBER 1U
    
    
    #define UART_CLOCK PCLK_SCB1_CLOCK
    
    #define UART_INTR_NUM        ((IRQn_Type) scb_1_interrupt_IRQn)

     

    When using UART5:

     

    // SCB 5 p5_0 rx p5_1 tx
    
    
    #define UART_SCB SCB5
    #define UART_PORT       P5_0_PORT
    #define UART_RX_NUM     P5_0_NUM
    #define UART_TX_NUM     P5_1_NUM
    #define UART_RX_PIN     P5_0_SCB5_UART_RX
    #define UART_TX_PIN     P5_1_SCB5_UART_TX
    #define UART_DIVIDER_NUMBER 5U
    
    #define UART_CLOCK PCLK_SCB5_CLOCK
    
    #define UART_INTR_NUM        ((IRQn_Type) scb_5_interrupt_IRQn)

     

    So these are  "variable" settings.

     

     

    I created the task size for my UART RTOS job as:

     

    #define UART_TASK_STACK_SIZE     (1024 * 3)

     

    The buffer length exactly as big as what I'm taking in at each trigger event:

     

    #define UART_BUFFER_SIZE 26

     

     

    Here are the (pre)declarations:

    /* Allocate context for UART operation */
    cy_stc_scb_uart_context_t uartContext;
    /* Stores the handle of the task that will be notified when the
     receive is complete. */
    volatile TaskHandle_t xTaskToNotify_UART;
    
    
    /* The index within the target task's array of task notifications
     to use. */
    const UBaseType_t xArrayIndex = 1;
    
    uint8_t rxBuffer[UART_BUFFER_SIZE];
    
    /* Allocate context for UART operation */
    cy_stc_scb_uart_context_t uartContext;
    
    void UART_receive();
    
    // low power support
    cy_stc_syspm_callback_params_t callback_params = { .base = UART_SCB, .context =
            &uartContext };
    
    cy_stc_syspm_callback_t uart_deep_sleep_cb = { Cy_SCB_UART_DeepSleepCallback,
            CY_SYSPM_DEEPSLEEP, 0, &callback_params,
            NULL,
            NULL };
    
    extern QueueHandle_t telemetry_queue;

     

     

    This is my init() function:

     

    void initUART() {
        // https://cypresssemiconductorco.github.io/psoc6pdl/pdl_api_reference_manual/html/group__group__scb__uart.html
    
    
        /* Populate configuration structure */
        const cy_stc_scb_uart_config_t uartConfig = { .uartMode =
                CY_SCB_UART_STANDARD, .enableMutliProcessorMode = false,
                .smartCardRetryOnNack = false, .irdaInvertRx = false,
                .irdaEnableLowPowerReceiver = false, .oversample = UART_OVERSAMPLE,
                .enableMsbFirst = false, .dataWidth = 8UL, .parity =
                        CY_SCB_UART_PARITY_NONE,
                .stopBits = CY_SCB_UART_STOP_BITS_1, .enableInputFilter = false,
                .breakWidth = 11UL, .dropOnFrameError = false, .dropOnParityError =
                        false, .receiverAddress = 0UL, .receiverAddressMask = 0UL,
                .acceptAddrInFifo = false, .enableCts = false, .ctsPolarity =
                        CY_SCB_UART_ACTIVE_LOW, .rtsRxFifoLevel = 0UL,
                .rtsPolarity = CY_SCB_UART_ACTIVE_LOW, .rxFifoTriggerLevel = 0UL,
                .rxFifoIntEnableMask = 0UL, .txFifoTriggerLevel = 0UL,
                .txFifoIntEnableMask = 0UL, };
        /* Configure UART to operate */
        (void) Cy_SCB_UART_Init(UART_SCB, &uartConfig, &uartContext);
    
    
        /* Assign pins for UART on SCBx */
    
    
        /* Connect SCB UART function to pins */
        Cy_GPIO_SetHSIOM(UART_PORT, UART_RX_NUM, UART_RX_PIN);
        Cy_GPIO_SetHSIOM(UART_PORT, UART_TX_NUM, UART_TX_PIN);
        /* Configure pins for UART operation */
        // jc: HIGHZ is correct for UART. But in the design here, the input is "patch-wired" to another controller.
        // Cy_GPIO_SetDrivemode(UART_PORT, UART_RX_NUM, CY_GPIO_DM_HIGHZ);
        Cy_GPIO_SetDrivemode(UART_PORT, UART_RX_NUM, CY_GPIO_DM_PULLUP);
        Cy_GPIO_SetDrivemode(UART_PORT, UART_TX_NUM, CY_GPIO_DM_STRONG_IN_OFF);
    
    
        /* Assign divider type and number for UART */
    
    
        /* Connect assigned divider to be a clock source for UART */
        Cy_SysClk_PeriphAssignDivider(UART_CLOCK, UART_CLK_DIV_TYPE,
                UART_CLK_DIV_NUMBER);
    
    
        // set baud
        Cy_SysClk_PeriphSetDivider(UART_CLK_DIV_TYPE, UART_CLK_DIV_NUMBER,
                UART_DIVISION);
        Cy_SysClk_PeriphEnableDivider(UART_CLK_DIV_TYPE, UART_CLK_DIV_NUMBER);
    
    
        /* Populate configuration structure (code specific for CM4) */
        cy_stc_sysint_t uartIntrConfig = { .intrSrc = UART_INTR_NUM, .intrPriority =
                UART_INTR_PRIORITY, };
        /* Hook interrupt service routine and enable interrupt */
        (void) Cy_SysInt_Init(&uartIntrConfig, &UART_Isr);
        NVIC_EnableIRQ(UART_INTR_NUM);
    
    
        /* Register a deep sleep callback for UART block. */
        Cy_SysPm_RegisterCallback(&uart_deep_sleep_cb);
    
    
        /* Enable UART to operate */
        Cy_SCB_UART_Enable(UART_SCB);
    }

     

    The Interrupt handler:

     

    void UART_Isr() {
        Cy_SCB_UART_Interrupt(UART_SCB, &uartContext);
    
    
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    
        if (xTaskToNotify_UART != NULL) {
    
    
            /* Notify the task that the receive is complete. */
            vTaskNotifyGiveFromISR(xTaskToNotify_UART, &xHigherPriorityTaskWoken);
            /* There are no receive in progress, so no tasks to notify. */
            xTaskToNotify_UART = NULL;
    
    
            /* If xHigherPriorityTaskWoken is now set to pdTRUE then a
             context switch should be performed to ensure the interrupt
             returns directly to the highest priority task.  The macro used
             for this purpose is dependent on the port in use and may be
             called portEND_SWITCHING_ISR(). */
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
    }

     

    The read() function:

     

    void UART_receive() {
        /* At this point xTaskToNotify should be NULL as no receive
         is in progress.  A mutex can be used to guard access to the
         peripheral if necessary. */
        configASSERT(xTaskToNotify_UART == NULL);
    
    
        /* Store the handle of the calling task. */
        xTaskToNotify_UART = xTaskGetCurrentTaskHandle();
    
    
        /* Start receive operation (do not check status) */
        (void) Cy_SCB_UART_Receive(UART_SCB, rxBuffer, sizeof(rxBuffer),
                &uartContext);
    }

     

    I don't have a write(), my project only receives data

     

    Finally, the UART RTOS task:

     

    void uart_task(void *pvParameters) {
    
    
        /* To avoid compiler warnings */
        (void) pvParameters;
        uint32_t ulNotificationValue;
        xTaskToNotify_UART = NULL;
    
    
        while (true) {
    
    
            /* Start the receiving from UART. */
            UART_receive();
            /* Wait to be notified that the receive is complete.  Note
             the first parameter is pdTRUE, which has the effect of clearing
             the task's notification value back to 0, making the notification
             value act like a binary (rather than a counting) semaphore.  */
            ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    
    
            if (ulNotificationValue == 1) {
                /* Blocking wait until buffer is full */
                // in this example, it will never handle because the UART interrupt fires exaxtly when the buffer is full
                while (0UL
                        != (CY_SCB_UART_RECEIVE_ACTIVE
                                & Cy_SCB_UART_GetReceiveStatus(UART_SCB,
                                        &uartContext))) {
                }
                /* Handle received data */
    
    
                if (telemetry_queue) { // queue needs to be generated}
                    if ( xQueueSend( telemetry_queue,
                            ( void * ) rxBuffer,
                            ( TickType_t ) 10 ) != pdPASS) {
                        /* Failed to post the message, even after 10 ticks. */
                        CY_ASSERT_L3(!0);
                    }
                }
    
    
            }
        }
    }

     

     

    However, it fails with the set / unset of the xTaskToNotify. It reaches the ISR with xTaskToNotify = NULL.

     

    You have to set it yourself, in the RTOS task that should wake up when the interrupt fires.

    Be careful to not declare global variable xTaskToNotify more than once in your project, or you may risk that you set a different one than the one that the interrupt handler sees.

     

    edit: the version I put in this comment is the latest version of the code, that supports that RTOS goes into deep sleep.

    if you configured RTOS to allow deep sleep, you have to add the low power support too. 

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube