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

[S110][SDK10][PCA10028] Calling uart function inside timer event handler

Hi, I'm using a timer to send message over uart. Here is the initialization code:

	    err_code = app_timer_create(&m_uart_timeout_timer, APP_TIMER_MODE_SINGLE_SHOT, uart_timeout_handler);
APP_ERROR_CHECK(err_code);

err_code = app_timer_create(&m_cmd_timer, APP_TIMER_MODE_REPEATED, send_cmd_handler);
APP_ERROR_CHECK(err_code);

    nrf_drv_uart_config_t	uart_config = NRF_DRV_UART_DEFAULT_CONFIG;
uart_config.baudrate = NRF_UART_BAUDRATE_9600;
uart_config.pselrxd = 2;
uart_config.pseltxd = 1;

err_code = nrf_drv_uart_init(&uart_config, my_uart_event_handler);
APP_ERROR_CHECK(err_code);

err_code = app_timer_start(m_cmd_timer, APP_TIMER_TICKS(5000, 0), NULL);
APP_ERROR_CHECK(err_code);

nrf_drv_uart_rx_enable();

Here is the handler:

void send_cmd_handler(void *p_context)
{
uint8_t tx_data[3] = {0x05, 0xdf, 0xad};
uint8_t tx_len = 3;
uint32_t err_code;

err_code = nrf_drv_uart_tx(tx_data, 3);
APP_ERROR_CHECK(err_code);

err_code = app_timer_start(m_uart_timeout_timer, UART_TIMEOUT, NULL);
APP_ERROR_CHECK(err_code);
}

I am receiving the uart data using ftdi board. The incoming data is shown as:

05 80 02 05 ff 01 05 ff 01 Rest all are 05 ff 01.

If I remove the timer and call the function separately, the data received is correct. Don't know what I'm doing wrong. Please help.
Regards
Lalit

  • Hi

    You should avoid starting a UART TX inside a timer callback. Try to make your callback routines as short as possible. Inside nrf_drv_uart_tx() there is a lot of code, while() loops, and invocations of more callbacks. Try to set a flag in the timer callback instead. Then do your thing in the main while() loop when the flag is set. You can also look into more advanced solutions like the scheduler.

    EDIT: I might have phrased my self a little inaccurate in my initial answer. The problem in your first code is that you define tx_data locally as an automatic variable. Hence the data is stored on the stack. Once the send_cmd_handler() handler is completed, the UART might still be reading from your buffer on the stack. The image below shows a logic trace of what happens.

    • Channel 3: Clears once your application enters send_cmd_handler()and goes high again when it exits.
    • Channel 7: Shows that the UART is busy transmitting for a long time after the handler is completely executed.

    image description

    So the problem occurs when the send_cmd_handler() is completely executed, because then the CPU thinks that all the memory on the stack is free and starts to store other application data on it. It might even overwrite your buffer which the UART is still busy reading from. What happens when you declare your buffer globally (as in the code in your comment below) is that the buffer is allocated some other place in memory (or the heep) where it is safe, even after the handler is complete.

  • Hi, I did according to what you told and it is working fine. Also I tried a bit different code that is working as well. Here is the snippet:

    void send_cmd_handler(void *p_context)
    {
    uint32_t err_code;
    
    txd_data = (uint8_t*)calloc(3, sizeof(uint8_t));
    uint8_t cmd[] = "abc";
    
    memcpy(&txd_data[0], cmd, strlen(cmd));
    
    err_code = nrf_drv_uart_tx(txd_data, 3);
    APP_ERROR_CHECK(err_code);
    
    err_code = app_timer_start(m_uart_timeout_timer, UART_TIMEOUT, NULL);
    APP_ERROR_CHECK(err_code);
    }
    

    I have defined txd_data as a global pointer. Is there any reason why this is working and the previous one wasn't?

Related