This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FreeRTOS + UART receive blocks everything

Hi,

Using the nRF5 11.0.0 release SDK, I'm trying to set up one task to transmit to and one task to receive data from the UART. Nothing special.

The configuration block in nrf_drv_config.h looks like:

/* UART */
#define UART0_ENABLED 1

#if (UART0_ENABLED == 1)
#define UART0_CONFIG_HWFC         NRF_UART_HWFC_DISABLED
#define UART0_CONFIG_PARITY       NRF_UART_PARITY_EXCLUDED
#define UART0_CONFIG_BAUDRATE     NRF_UART_BAUDRATE_115200
#define UART0_CONFIG_PSEL_TXD     26
#define UART0_CONFIG_PSEL_RXD     25
#define UART0_CONFIG_PSEL_CTS     30
#define UART0_CONFIG_PSEL_RTS     31
#define UART0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#ifdef NRF52
#define UART0_CONFIG_USE_EASY_DMA false
//Compile time flag
#define UART_EASY_DMA_SUPPORT     1
#define UART_LEGACY_SUPPORT       1
#endif //NRF52
#endif

The initialization in main() of /home/max/devsrc/minibrain/nRF5_SDK_11.0.0_89a8197/examples/ble_peripheral/ble_app_hrs_freertos/main.c looks like:

m_uart_output_queue = xQueueCreate(10, sizeof (struct SerialPortOutput *));

if (m_uart_output_queue)
{
    printf("Queue created successfully!\n");
}
else
{
    printf("Queue could not be created!\n");
}

// Start execution.
if(pdPASS != xTaskCreate(ble_stack_thread, "BLE", 256, NULL, 1, &m_ble_stack_thread))
{
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}

nrf_drv_uart_config_t uart_config = NRF_DRV_UART_DEFAULT_CONFIG;

if (NRF_SUCCESS == nrf_drv_uart_init(&uart_config, NULL))
{
    printf("UART initialized properly.\n");
}
else
{
    printf("UART failed to initialize properly.\n");
}
    
if (pdPASS != xTaskCreate(uart_input_task, "UART Input Task", 64, NULL, 1, &m_uart_input_thread))
{
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}

if (pdPASS != xTaskCreate(uart_output_task, "UART Output Task", 64, NULL, 1, &m_uart_output_thread))
{
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}

And the tasks themselves look like:

static void uart_input_task(void * arg)
{
    uint8_t data[9] = {0};
    nrf_drv_uart_rx_enable();
    
    for (;;)
    {
        memset(data, 0, sizeof(data));
        
        // Read UART input.
        printf("Reading UART (should block until something arrives).\n");
        nrf_drv_uart_rx(data, (sizeof(data) - 1));
        
        // Second task should interrupt the blocked UART every now and then,
        // since the Nordic doesn't have a UART read timeout by default.

        printf("%s\n", (char *) data);
        
        // Push UART data to input processing queue.
    }
    
    nrf_drv_uart_rx_disable();
}

struct SerialPortOutput
{
    char      output[32];
    uint8_t   length;
};

static void uart_output_task(void * arg)
{
    for (;;)
    {
        struct SerialPortOutput * spo;

        printf("uart_output_task: Waiting for data to send.\n");
        
        // Block waiting on data in the queue.
        if (xQueueReceive(m_uart_output_queue, &spo, portMAX_DELAY))
        {
            // Send data to UART.
            // nrf_drv_uart_tx(data, length);
        }
        else
        {
            printf("uart_output_task: xQueueReceive had nothing to send.\n");
        }
    }
}

Unfortunately, when monitoring the debug output via RTT, I only see:

Queue created successfully!
UART initialized properly.
Hello SEGGER_RTT_WriteString().
Hello RTT-rerouted printf()
sd_ble_enable: RAM START at 0x20001FE8
Reading UART (should block until something arrives).

So it seems to be blocking after running nrf_drv_uart_rx() in the RX task. It never preempts and runs the rest of main() or starts the TX task. I tried setting #define configUSE_TICKLESS_IDLE 0 in FreeRTOSConfig.h but this did not help.

I also noticed some differences in port_cmsis.c, which may have helped the nRF51 series, but may not have been ported to the nRF52? I'm unsure. (See: devzone.nordicsemi.com/.../)

Any help in solving this would be greatly appreciated.

Thanks, Max

  • See my reply to this thread, my reply to this one would be very similar.

    Your stack looks relatively small - do you have stack overflow checking on?

  • I haven't got stack overflow checking on.

    However, I was wondering if it's common to implement an indepedent task to check on all the other tasks' high water mark, using uxTaskGetStackHighWaterMark?

    I'm thinking about printing this information out at runtime.

    Update 1:

    After doing some more digging, it does look like the legacy UART driver is not compatible with FreeRTOS. This is likely a problem on the Nordic side. It's not a stack size issue. It's probably fixable by using the UARTE non-blocking Rx/Tx, and I'll be trying that path next.

    When I trace the problem with gdb, it tells me that the nrf_drv_uart_rx_for_uart gets stuck in a while loop, but the OS should have at some point preempted this, and allowed my other tasks to run:

    Program received signal SIGTRAP, Trace/breakpoint trap.
    nrf_drv_uart_rx_for_uart (p_data=<optimized out>, length=<optimized out>, second_buffer=<optimized out>) at /home/max/nRF5_SDK_11.0.0_89a8197/components/drivers_nrf/uart/nrf_drv_uart.c:421
    421	            } while ((!rxrdy) && (!rxto) && (!error));
    (gdb) bt
    #0  nrf_drv_uart_rx_for_uart (p_data=<optimized out>, length=<optimized out>, second_buffer=<optimized out>) at /home/max/nRF5_SDK_11.0.0_89a8197/components/drivers_nrf/uart/nrf_drv_uart.c:421
    #1  nrf_drv_uart_rx (p_data=p_data@entry=0x20003314 <ucHeap+2524> "", length=length@entry=8 '\b') at /home/max/nRF5_SDK_11.0.0_89a8197/components/drivers_nrf/uart/nrf_drv_uart.c:570
    #2  0x0002191e in uart_input_task (arg=<optimized out>) at /home/max/nRF5_SDK_11.0.0_89a8197/examples/ble_peripheral/ble_app_hrs_freertos/main.c:911
    #3  0x0001d684 in ?? ()
    Backtrace stopped: previous frame identical to this frame (corrupt stack?)
    

    My task creation in main() looks like this, and the code is getting stuck immediately at the launch of uart_input_task, it never runs the tasks specified in the other xTaskCreate calls:

    if (pdPASS != xTaskCreate(uart_input_task, "UART Rx", 256, NULL, 1, &m_uart_input_thread))
    {
        printf("Out of memory.");
        APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
    
    if (pdPASS != xTaskCreate(uart_output_task, "UART Tx", 256, NULL, 1, &m_uart_output_thread))
    {
        printf("Out of memory.");
        APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
    
    if (pdPASS != xTaskCreate(uart_test_task, "Test UART Task", 256, NULL, 1, &m_uart_test_task))
    {
        printf("Out of memory.");
        APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
    

    Update 2:

    Yes, using the UARTE non-blocking TX/RX seems to work. The UART event handler passed to nrf_drv_uart_init() uses a semaphore to notify the RX task that data has been received, and the RX task sets up another receive using nrf_drv_uart_rx(), then the RX task waits on the semaphore again.

Related