nRF52DK nRF52832 UART asynchronous receive

I am working through the Nordic Developer Academy and got stuck at lesson 4 (UART receive)

https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/lessons/lesson-4-serial-communication-uart/topic/uart-driver/

In particular, I am confused about this part:

I have created a sample project and connected external CP2102 USB->Serial adapter to P0.06 and P0.08 pins.

The code that I use:

/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include "stdio.h"
#include "zephyr/drivers/uart.h"

const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));

static uint8_t tx_buf[] =  {"nRF Connect SDK Fundamentals Course \n\r"};
static uint8_t rx_buf[10] = {0}; //A buffer to store incoming UART data 

const struct uart_config uart_cfg = {
		.baudrate = 115200,
		.parity = UART_CFG_PARITY_NONE,
		.stop_bits = UART_CFG_STOP_BITS_1,
		.data_bits = UART_CFG_DATA_BITS_8,
		.flow_ctrl = UART_CFG_FLOW_CTRL_NONE
	};

static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
	switch (evt->type) {
	
	case UART_TX_DONE:
		printf("transmission complete \n");
		break;

	case UART_TX_ABORTED:
		// do something
		break;
		
	case UART_RX_RDY:
		printf("rx rdy \n");
		if((evt->data.rx.len) != 0){
			printf("data received = %s \n", evt->data.rx.buf[evt->data.rx.offset]);
		}


		
		break;

	case UART_RX_BUF_REQUEST:
		printf("requesting buffer \n");
		// do something
		break;

	case UART_RX_BUF_RELEASED:
		printf("buffer released \n");
		// do something
		break;
		
	case UART_RX_DISABLED:
		printf("rx disabled \n");
		uart_rx_enable(dev, rx_buf, sizeof(rx_buf), 100);
		break;

	case UART_RX_STOPPED:
		// do something
		break;
		
	default:
		break;
	}
}


int main(void)
{	

	if (!device_is_ready(uart)) {
    	return;
	}

	int err;
	err = uart_callback_set(uart, uart_cb, NULL);
	if (err) {
		return err;
	}

	uart_rx_enable(uart, rx_buf, sizeof(rx_buf), 100);


	err = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);
	if (err) {
		return err;
	}



	while(1){
		k_msleep(1000);
	}
	return 0;
}

I am having issues understanding how to properly print out all the received data:

I tried to do:

	case UART_RX_RDY:
		printf("rx rdy \n");
		if((evt->data.rx.len) != 0){
			printf("data received = %s \n", evt->data.rx.buf[evt->data.rx.offset]);
		}

		break;

but when I send the data via UART (using Termite) , the following is printed:

I would appreciate if someone could clarify how to correctly print out all the received data.  Thanks in advance

Just for testing, I have also tried to print:

    case UART_RX_RDY:
        printf("rx rdy \n");
        printf("data: %s \n", rx_buf);
        break;
 
and the logs are as following: (captured using Termite):
 
As you can see, it receives the first message (ping) correctly, but after that, the buffer overflows (rx_buf is 10 bytes size) and then it cannot print out the data correctly)
Parents
  • Hi,

    Could you check the solution in the github repo and see if you're able to resolve what is wrong and then let me know if you're still having issues with resolving it and we'll get right back at to find you a solution? 

    https://github.com/NordicDeveloperAcademy/ncs-fund 

    Kind regards,
    Andreas

  • The solution in the repository shows how to toggle an LED when a serial message is received but it does not really show how to receive and print a longer serial message. I am interested in being able to receive and parse a longer serial message. For example when I receive messsage "ping", I want to respond with message "pong".

    When I send message "toggle_led1" I want to turn ON the LED1 and so on...  In order for this to work I need to be able to receive and learn how to print the full received message.

    The solution uses the following method to check what serial data has been received:

    if(evt->data.rx.buf[evt->data.rx.offset] == '1')

    So I try something simillar just for testing purposes:

    	case UART_RX_RDY:
    		printf("received message =  %s",evt->data.rx.buf[evt->data.rx.offset]);
    		break;

    The serial console logs when I send 2 messages (ping and hello):

    As you can see from above, it does not work properly

  • The weird behaviour disappears, if you run my code with larger buffers ;-).

    The point is, if you want a "seamless" DMA transfer in time, you need to request the buffers overlapping. I assume, that e.g. the new buffer is usually requested some bytes  ahead (say 32) so that the app provides the new buffer and the DMA still uses the old. When the old is filled, the DMA switches very fast to the already provided new one.

    But with 8 bytes, that's not working ;-).

     

  • After checking the implementation in uart_nrfx_uart.c my understanding is, that the new buffer is already requested, when the first buffer is initially used or the buffers are actually swapped. Anyway, I guess, using something larger will help.

  • > At this point, I am not sure how exactly  uart_event.rx.len and uart_event.rx.offset works as it does not seem to show proper values once I change to second dual buffer.

    I've tested with your example.

    I understand now the irritation, I would consider it also as bug.

    When the buffers are switched, it seems that only the first received byte is forwarded. And the others then with the next UART_RX_RDY. Looks like the number of available byte is calculated wrong, when the buffers are swapped.

    Edited:

    I've checked my "working example" and the difference in the "prj.conf" is

    CONFIG_UART_0_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2
    CONFIG_NRFX_TIMER2=y

    with that, also your example works as you expect and the irritation is gone. :-).

  • Hello. Thank you very much for all your insights.

    I have played around more with my project (Async_UART_debug) branch and I have found something interesting. printk and prinf statements have huge effect on it.

    Even replacing printk statements with cause the Async UART reception to behave differently.

    The full code (also available in my repository, UART_async_working branch.

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include "stdio.h"
    #include "my_gpio.h"
    #include "zephyr/drivers/uart.h"
    #include <zephyr/sys/ring_buffer.h>
    
    #define LOG_LEVEL 4
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(nrf52_learning);
    
    #define UART_BUF_SIZE 20
    
    #define UART_RX_TIMEOUT_MS 1000
    K_SEM_DEFINE(rx_disabled, 0, 1);
    
    // UART RX primary buffers
    uint8_t uart_double_buffer[2][UART_BUF_SIZE];
    
    uint8_t *uart_buf_next = uart_double_buffer[1];
    
    uint8_t complete_message[UART_BUF_SIZE];
    uint8_t complete_message_counter = 0;
    bool currently_active_buffer = 1; // 0 - uart_double_buffer[0] is active, 1 - uart_double_buffer[1] is active
    
    static const struct device *dev_uart;
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    
    	switch (evt->type)
    	{
    
    	case UART_TX_DONE:
    		break;
    
    	case UART_TX_ABORTED:
    		// do something
    		break;
    
    	case UART_RX_RDY:
    
    		printk("Received %i bytes \n", evt->data.rx.len);
    		printk("Offset = %i  \n", evt->data.rx.offset);
    
    		if (currently_active_buffer == 0)
    		{
    			// read all characters one by one till new line is found
    			for (int i = 0 + evt->data.rx.offset; i < UART_BUF_SIZE; i++)
    			{
    				complete_message[complete_message_counter] = uart_double_buffer[0][i];
    				complete_message_counter++;
    				if (uart_double_buffer[0][i] == '\n')
    				{
    					complete_message_counter = 0;
    					printk("complete_message = %s \n", complete_message);
    					memset(&complete_message, 0, sizeof(complete_message)); // clear out the buffer to prepare for next read.
    					break;
    				}
    			}
    		}
    
    		if (currently_active_buffer == 1)
    		{
    			// read all characters one by one till new line is found
    			for (int i = 0 + evt->data.rx.offset; i < UART_BUF_SIZE; i++)
    			{
    				complete_message[complete_message_counter] = uart_double_buffer[1][i];
    				complete_message_counter++;
    				if (uart_double_buffer[1][i] == '\n')
    				{
    					complete_message_counter = 0;
    					printk("complete_message = %s \n", complete_message);
    					memset(&complete_message, 0, sizeof(complete_message)); // clear out the buffer to prepare for next read.
    					break;
    				}
    			}
    		}
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		uart_rx_buf_rsp(dev_uart, uart_buf_next, UART_BUF_SIZE);
    		currently_active_buffer = !currently_active_buffer;
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		uart_buf_next = evt->data.rx_buf.buf;
    		break;
    
    	case UART_RX_DISABLED:
    		k_sem_give(&rx_disabled);
    		break;
    
    	case UART_RX_STOPPED:
    		// do something
    		break;
    
    	default:
    		break;
    	}
    }
    
    void app_uart_init()
    {
    	dev_uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
    
    	if (!device_is_ready(dev_uart))
    	{
    		return 0;
    	}
    
    	int err;
    	err = uart_callback_set(dev_uart, uart_cb, NULL);
    	if (err)
    	{
    		return err;
    	}
    	uart_rx_enable(dev_uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);
    }
    
    int main(void)
    {
    	app_uart_init();
    }
    

    and my prj.conf:

    CONFIG_SERIAL=y
    CONFIG_UART_ASYNC_API=y
    CONFIG_LOG=y
    
    
    
    

    I have removed most debugging printf/prink statements and only left one or two. You can see that it seems to work now. I am able to send/receive messages and uart_event.rx.len/uart_event.rx.offset seems to be correct now.

    It would be very interesting to hear response from nordic about what exactly is happening and also about the additional options that you have suggested:

    CONFIG_UART_0_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2
    CONFIG_NRFX_TIMER2=y

    UPDATE

    I can confirm that after adding additional prj.conf options that you have suggested, it works much better. I have even managed to get it to work with additional printf/printk statements just by adding these extra options.

    I have even tested it out on my master branch (the branch where I had all the issues initially).

    See the full code below:

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include "stdio.h"
    #include "my_gpio.h"
    #include "zephyr/drivers/uart.h"
    #include <zephyr/sys/ring_buffer.h>
    
    #define LOG_LEVEL 4
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(nrf52_learning);
    
    #define UART_BUF_SIZE 20
    
    #define UART_RX_TIMEOUT_MS 1000
    K_SEM_DEFINE(rx_disabled, 0, 1);
    
    // UART RX primary buffers
    uint8_t uart_double_buffer[2][UART_BUF_SIZE];
    
    uint8_t *uart_buf_next = uart_double_buffer[1];
    
    uint8_t complete_message[UART_BUF_SIZE];
    uint8_t complete_message_counter = 0;
    bool currently_active_buffer = 1; // 0 - uart_double_buffer[0] is active, 1 - uart_double_buffer[1] is active
    
    static const struct device *dev_uart;
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    
    	switch (evt->type)
    	{
    
    	case UART_TX_DONE:
    		printf("transmission complete \n");
    		break;
    
    	case UART_TX_ABORTED:
    		// do something
    		break;
    
    	case UART_RX_RDY:
    
    		printf("Received %i bytes \n", evt->data.rx.len);
    		printf("Offset = %i  \n", evt->data.rx.offset);
    
    		printf("evt->data.rx.buf: [");
    		for (int i = 0; i < UART_BUF_SIZE; i++)
    		{
    			printf("%u, ", evt->data.rx.buf[i]);
    		}
    		printf("] \n");
    
    		printf("uart_double_buffer[0]: [");
    		for (int i = 0; i < UART_BUF_SIZE; i++)
    		{
    			printf("%u, ", uart_double_buffer[0][i]);
    		}
    		printf("] \n");
    
    		printf("uart_double_buffer[1]: [");
    		for (int i = 0; i < UART_BUF_SIZE; i++)
    		{
    			printf("%u, ", uart_double_buffer[1][i]);
    		}
    		printf("] \n");
    
    		printf("Constructing a complete message \n");
    		if (currently_active_buffer == 0)
    		{
    			// read all characters one by one till new line is found
    			for (int i = 0 + evt->data.rx.offset; i < UART_BUF_SIZE; i++)
    			{
    				complete_message[complete_message_counter] = uart_double_buffer[0][i];
    				complete_message_counter++;
    				if (uart_double_buffer[0][i] == '\n')
    				{
    					printf("new line found at buffer 0 index = %i \n", i);
    					complete_message_counter = 0;
    					printf("complete_message = %s \n", complete_message);
    					memset(&complete_message, 0, sizeof(complete_message)); // clear out the buffer to prepare for next read.
    					break;
    				}
    			}
    		}
    
    		if (currently_active_buffer == 1)
    		{
    			// read all characters one by one till new line is found
    			for (int i = 0 + evt->data.rx.offset; i < UART_BUF_SIZE; i++)
    			{
    				complete_message[complete_message_counter] = uart_double_buffer[1][i];
    				complete_message_counter++;
    				if (uart_double_buffer[1][i] == '\n')
    				{
    					printf("new line found at buffer 1 index = %i \n", i);
    					complete_message_counter = 0;
    					printf("complete_message = %s \n", complete_message);
    					memset(&complete_message, 0, sizeof(complete_message)); // clear out the buffer to prepare for next read.
    					break;
    				}
    			}
    		}
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		uart_rx_buf_rsp(dev_uart, uart_buf_next, UART_BUF_SIZE);
    		currently_active_buffer = !currently_active_buffer;
    		if (currently_active_buffer == 0)
    		{
    			printf("currently active buffer is uart_double_buffer[0] \n");
    		}
    		else
    		{
    			printf("currently active buffer is uart_double_buffer[1] \n");
    		}
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		printf("Old buffer has been released \n");
    		uart_buf_next = evt->data.rx_buf.buf;
    		break;
    
    	case UART_RX_DISABLED:
    		printf("rx disabled \n");
    		k_sem_give(&rx_disabled);
    		break;
    
    	case UART_RX_STOPPED:
    		// do something
    		break;
    
    	default:
    		break;
    	}
    }
    
    void app_uart_init()
    {
    	dev_uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
    
    	if (!device_is_ready(dev_uart))
    	{
    		return 0;
    	}
    
    	int err;
    	err = uart_callback_set(dev_uart, uart_cb, NULL);
    	if (err)
    	{
    		return err;
    	}
    	uart_rx_enable(dev_uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);
    }
    
    int main(void)
    {
    	app_uart_init();
    }
    

    and my prj.conf:

    # nothing here
    CONFIG_GPIO=y
    CONFIG_SERIAL=y
    
    
    CONFIG_LOG=y
    
    CONFIG_UART_ASYNC_API=y
    CONFIG_UART_0_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2
    CONFIG_NRFX_TIMER2=y
    

    See the logs below:

    As you can see from the logs above, I have bunch of printf statements but that does not have any negative effect on receiving data. And yes, I am aware that using printf statements in callback itself is not a good practise, but that is just for testing purposes!

     

  •  Hello. It has been a while. Perhaps you had some time to look at our latest discussion with  ?

    We have discovered some interesting things about ASYNC UART and it would be really nice to get to the bottom of this.

Reply Children
No Data
Related