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

  • Hello. Maybe you have any feedback regarding this issue? :)

  • Hi, 

    Apologies for the long response time.

    Unfortunately I could not find a simple sample that showcase how to do this, so my best current suggestion is to investigate the peripheral UART and Central uart BLE samples and see how it is done in these samples 

    Starting at L129 in peripheral_uarts main.c you can see how the UART_RD_RDY case is handled

    case UART_RX_RDY:
    		LOG_DBG("UART_RX_RDY");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
    		buf->len += evt->data.rx.len;
    
    		if (disable_req) {
    			return;
    		}
    
    		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
    			disable_req = true;
    			uart_rx_disable(uart);
    		}
    
    		break;

    and bt_receive_cb() L465 peripheral, ble_data_received() L83 in Central_uart should show how the uart messages are handled with regards to writing.

    Kind regards,
    Andreas

  • Hello. I had a look at the zephyr/samples/drivers/uart/echo_bot sample and that sample works exactly how I want it to work. Please look at the serial terminal logs below:

    *** Booting Zephyr OS build v3.3.99-ncs1 ***
    Hello! I'm your echo bot.
    Tell me something and press enter:
    hello
    Echo: hello
    ping
    Echo: ping
    test
    Echo: test
    apple
    Echo: apple
    

    I can easily echo any message that I send. However this sample uses different UART method (polling) and thats not what I want. I want to be able to echo the whole buffer using ASYNC uart method.

    Being able to echo the whole buffer is the most basic requirement for any UART program, it is really hard for me wrap my head around why is this so complicated using the ASYNC method. Would you be able to help any further?. Being able to print a whole received buffer should be as simple as 1 line of code..

    I have posted a ticket on zephyr git :

    https://github.com/zephyrproject-rtos/zephyr/issues/64115

    Perhaps my question is more suited for zephyr instead of here :) 

  • Of course the echo_bot sample is what I should've suggested immediately Laughing That sample was not on my mind at all this morning! 

    But as you state there is a difference in using polling and the async method, and the lesson you started with from the academy course uses Async. Have you attempted to merge the lesson you started with, i.e https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/lessons/lesson-4-serial-communication-uart/topic/uart-driver/, into the echo_bot?

    PS: Unless you've already been recommended by users on the discord to create a ticket on the github, you can also find Zephyrs discord at zephyrproject.org, which might be an even easier way to communicate with other developers using Zephyr

      

    Kind regards,
    Andreas

  • I have tried to merge the echo_bot to the async uart sample but that is not as straight forward as it may look as it is using completely different functions.. I dont think it can be done this way.

    I have posted on Zephyr Git as well as the Zephyr discord that you have recommended but there is no solution yet.. I am still waiting. If you have any more ideas let me know.

    I will be sure to post back here the answer if someone replies on git or discord :)

Reply
  • I have tried to merge the echo_bot to the async uart sample but that is not as straight forward as it may look as it is using completely different functions.. I dont think it can be done this way.

    I have posted on Zephyr Git as well as the Zephyr discord that you have recommended but there is no solution yet.. I am still waiting. If you have any more ideas let me know.

    I will be sure to post back here the answer if someone replies on git or discord :)

Children
  • Hi,

    I did some more digging and found an older, unofficial sample created by someone in tech support showcasing how to use the Asynchronous UART Driver. Here's a version of the sample that should work in NCS v2.4.2

    ncs-uart-async-count-rx.zip

    Kind regards,
    Andreas

  • I have looked at the sample project you have provided. I have removed all the TX related code since I am not interested in TX (I only want to learn about RX first). The latest  source code is available here:

    https://github.com/krupis/nrf52_learning

    I can confirm that it also does not work. Please look at the logs below:

    I have underlined response in green where the response is correct and in red where the response is not as expected.

    Could you please ask someone else from embedded software team from your side to look at this as this is getting a little frustrating?

    Thanks for keeping this going!

  • Hi,

    It looks like the repo you've provided is private as the link takes me to a 404 and the nrf52_learning is not listed in your repositories. Could you make it public/share a public version of it?

    Kind regards,
    Andreas

  • You were right. It was private, my bad!

    I have made it public now.

  • Thank you,

    I finally had the time to look at your code. Apologies for why it took so long and to the frustration you must've felt. Response times gets a bit longer when we're nearing release of a new major version of the SDK (v2.5.0).

    Your code works as expected when you're using asynch UART with static buffers (the central and peripheral_uart samples uses dynamically allocated buffers)

    Why, you ask? 
    This is the quick and dirty explanation: The uart-cb triggers either when the timeout you've set runs out (100ms) or when the buffer you've declared (16 bit) is filled. When you enter new messages and you the timeout expires before you're finished writing the message, you will be left with a message that is separated. In theory if the timeout is sufficiently small and you match your writing speed with the timeout, the callback can trigger 16 times and separate the message into 16 characters instead of 1.

    Hello (6) + ping (5) + message1 (expected to be 9)  > 16: You see the callback trigger when both the timeout expires and the buffer length limit is reached

    For further observation
    Increase the timeout so you have time to write the new messages, and or play around with the buffer length (which is currently set to 16)

    Please feel free to ask follow up questions if anything is unclear! 

    Kind regards,
    Andreas

Related