Zephyr 2.4.0 with Async UART

Hi, 

   I am having the same problem as nRF9160 UART: Loosing data when RxData is split into 2 buffers (Async API) - Nordic Q&A - Nordic DevZone - Nordic DevZone (nordicsemi.com) and ASYNC uart and UART_RX_RDY timeout - Nordic Q&A - Nordic DevZone - Nordic DevZone (nordicsemi.com). I have tried the solution, namely modifying 

uart_nrfx_uarte.c

prj.config

However, I end up with this error

Ultimately, I would like this problem resolved. If I'm missing something obvious, please let me know. 

Parents
  • The solution that was proposed is very old and hence definitely not API compatible with the newer ncsv2.4.0.

    The pull request the other thread is showing seems to be merged. So I am confused that you are using ncsv2.4.0 and still have to patch the files with the PR changes? Shouldn't the changes be already in?

  • I modified DEVZONE Academy lesson 5 to echo back received data. I found that the echo shows data is getting missed. I am not sure how to resolve this issue. 

    It makes sense that data is being missed when the RECEIVE_BUFF_SIZE is less then the length of the data being sent. However, I notice missed data even when I increase the buffer size. 

    What is going on here? 

    3583.fund_less5_exer1_solution.zipI  

  • Also, I would like to get two UART's working in ASYNC mode with high data reliability. What would I need to set in the prj.config file to enable both UART's in ASYNC mode? Does each UART need a different timer? 

    For example: 

    #UART0 Config

    CONFIG_UART_0_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=1
    CONFIG_NRFX_TIMER1=y
    #UART1 Config
    CONFIG_UART_1_ASYNC=y
    CONFIG_UART_1_NRF_HW_ASYNC=y
    CONFIG_UART_1_NRF_HW_ASYNC_TIMER=2
    CONFIG_NRFX_TIMER2=y
  • Yes, Async mode in the UART needs a timer for each instance. It is a bit resource intensive. Why do you need two UARTs in Async mode? What is the use case here?

  • Hi Susheel, 

    1. Can you help me with my original problem? The UART is having problems receiving data in ASYNC mode. The fix doesn't seem to have resolved the problem. I'm also unclear if the additional lines are required in the config file anymore or not. Their presence seems to reduce the likelyhood or severity of missing or shuffled bytes, but not eliminate it. 

    2. I'm using the NRF52840 as a partial UART buffer between two other devices. 

                   Outside device <-- UART --> BMD <-- UART --> Inside device

    I want to be able to have two fully working ASYNC UART peripherals. I first need to get one ASYNC UART working, then add in a second one. As indicated before, the Lesson 5 example as modified doesn't work properly. This in essence should behave as an ECHO, return anything it receives. The problem isn't on the sending end. The problem appears to be on the receiving end. Please review my other comments for further detail. 

    thanks

  • Canadian_EE said:
    1. Can you help me with my original problem? The UART is having problems receiving data in ASYNC mode. The fix doesn't seem to have resolved the problem. I'm also unclear if the additional lines are required in the config file anymore or not. Their presence seems to reduce the likelyhood or severity of missing or shuffled bytes, but not eliminate it. 

    Like I said, the fix is not relevant for the latest SDK version. 
    It looks like your main.c is using uart0 instance to configure and test the Async Serial but I can see in v2.4.1\zephyr\boards\arm\nrf52840dk_nrf52840\nrf52840dk_nrf52840.dts that the default selections of serial for zephyr console is not changed from uart0

    	chosen {
    		zephyr,console = &uart0;
    		zephyr,shell-uart = &uart0;
    		zephyr,uart-mcumgr = &uart0;
    		zephyr,bt-mon-uart = &uart0;
    		zephyr,bt-c2h-uart = &uart0;
    		zephyr,sram = &sram0;
    		zephyr,flash = &flash0;
    		zephyr,code-partition = &slot0_partition;
    		zephyr,ieee802154 = &ieee802154;
    	};

    Either you disable zephyr console logging or you change the main.c to use another serial instance.

    Canadian_EE said:

    2. I'm using the NRF52840 as a partial UART buffer between two other devices. 

                   Outside device <-- UART --> BMD <-- UART --> Inside device

    There are other use cases which have succesfully used two ASYNC instances with the below config

     # UART
    CONFIG_SERIAL=y
    CONFIG_UART_INTERRUPT_DRIVEN=y
    CONFIG_UART_0_INTERRUPT_DRIVEN=n
    CONFIG_UART_1_INTERRUPT_DRIVEN=n
    CONFIG_UART_LINE_CTRL=y
    CONFIG_UART_ASYNC_API=y
    CONFIG_UART_0_ASYNC=y
    CONFIG_UART_1_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_1_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=1
    CONFIG_UART_1_NRF_HW_ASYNC_TIMER=2
    CONFIG_NRFX_UARTE0=y
    CONFIG_NRFX_UARTE1=y
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_TIMER2=y
    CONFIG_NRFX_PPI=y
    

    But you need to disable serial logging for having two instances available for your application. I haven't tested these samples myself but it seems it should work.

    There is one interesting issue discussed in zephyr forum which resembles the issue you are facing. It turned out to be a configuration issue.

    Also can you please post the link to the devacademy lesson that you used to test this. I can ask the writers to review the content again.

  • Hi Susheel, 

       I believe I have resolved the problem. I thought the problem was in the prj.config setting when compared with the devacademy lesson (lesson 5). However, I found (I think) that the problem lies with Zephyr. 

    The problem appears to be

    1. When the buffer fills up, the system has a problem. Data is lost. 

    2. Double buffering also doesn't resolve the problem, it merely pushes the problem into the future

    So the solution can benefit from double buffering, but is not resolved by it. Nor is it resolved with a  UART_RX_BUF_REQUEST or UART_RX_BUF_RELEASED event. The system uses a second buffer when the first one fills up. However, when it switches to the second buffer, it doesn't seem to let you setup a new secondary buffer. This is a problem, because when the secondary buffer fills up, then you loose data. 

    What did I try? 

    1. I tried setting up a new secondary buffer upon a "UART_RX_BUF_REQUEST" event 

    After all, this occurs when you have started receiving for a new buffer. It should be as simple as calling "uart_rx_buf_rsp(...)" to setup a new secondary buffer. However, this doesn't work. A problem is still encountered when the old secondary buffer is filled. 

    2. I tried forcing a reset of the UART buffers (within UART_RX_RDY)

    Every time data is received, a UART_RX_RDY event will at some point occur. So, after copying the data, disabled the UART RX side. Then, re-enable and setup your buffers again. In this case, if I start out with Primary Buffer as "Buffer A", and my Secondary Buffer as "Buffer B", I then make my new Primary Buffer "Buffer B", and my new Secondary Buffer "Buffer A"

    Buffer A should never fill up. Before it fills up, you have switched the buffers around. So your two buffers aren't providing overrun protection per say. Rather, they are providing an empty runway every time you receive data. 

        case UART_RX_RDY:
            tracking = 1;
            int i = 0;
            uint8_t temp = 0;
            uint8_t length = evt->data.rx.len;

            for(i = 0i<evt->data.rx.len; i++)
            {
                temp = evt->data.rx.buf[evt->data.rx.offset + i];
                tx_buf2[i= temp;
            }
            ret = uart_rx_disable(dev);
            ret = uart_tx(dev ,tx_buf2,length,SYS_FOREVER_MS);
            break;
        case UART_RX_DISABLED:
            tracking += 1;
            if (rx_buf_indicator == 0)
            {
                ret = uart_rx_enable(dev ,rx_buf1,sizeof(rx_buf1),RECEIVE_TIMEOUT);
                ret = uart_rx_buf_rsp(dev, rx_buf0, sizeof(rx_buf0));
                rx_buf_indicator = 1;
            }
            else
            {
                ret = uart_rx_enable(dev ,rx_buf0,sizeof(rx_buf0),RECEIVE_TIMEOUT);
                ret = uart_rx_buf_rsp(dev, rx_buf1, sizeof(rx_buf1));
                rx_buf_indicator = 0;
            }
            break;

    And I set the prj.config settings back to the defaults from lesson 5, and that doesn't seem to make a difference. 

    ## These prj.config settings work

    CONFIG_SERIAL=y
    CONFIG_UART_ASYNC_API=y

    ## These prj.config settings also works

    CONFIG_SERIAL=y
    CONFIG_UART_INTERRUPT_DRIVEN=y
    CONFIG_UART_0_INTERRUPT_DRIVEN=n
    CONFIG_UART_LINE_CTRL=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=1
    CONFIG_NRFX_UARTE0=y
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_PPI=y
    Please provide input
    1.The problem didn't appear to be with the prj.config file.
    2. The solution didn't appear to be in the DEVACADEMY example.
    3. I also have a mild worry about dropped data that could be caused by disabling the UART_RX stream.\
    4. I would also like to confirm why this is a problem or a better way to resolve this ASYNC UART problem...? 
Reply
  • Hi Susheel, 

       I believe I have resolved the problem. I thought the problem was in the prj.config setting when compared with the devacademy lesson (lesson 5). However, I found (I think) that the problem lies with Zephyr. 

    The problem appears to be

    1. When the buffer fills up, the system has a problem. Data is lost. 

    2. Double buffering also doesn't resolve the problem, it merely pushes the problem into the future

    So the solution can benefit from double buffering, but is not resolved by it. Nor is it resolved with a  UART_RX_BUF_REQUEST or UART_RX_BUF_RELEASED event. The system uses a second buffer when the first one fills up. However, when it switches to the second buffer, it doesn't seem to let you setup a new secondary buffer. This is a problem, because when the secondary buffer fills up, then you loose data. 

    What did I try? 

    1. I tried setting up a new secondary buffer upon a "UART_RX_BUF_REQUEST" event 

    After all, this occurs when you have started receiving for a new buffer. It should be as simple as calling "uart_rx_buf_rsp(...)" to setup a new secondary buffer. However, this doesn't work. A problem is still encountered when the old secondary buffer is filled. 

    2. I tried forcing a reset of the UART buffers (within UART_RX_RDY)

    Every time data is received, a UART_RX_RDY event will at some point occur. So, after copying the data, disabled the UART RX side. Then, re-enable and setup your buffers again. In this case, if I start out with Primary Buffer as "Buffer A", and my Secondary Buffer as "Buffer B", I then make my new Primary Buffer "Buffer B", and my new Secondary Buffer "Buffer A"

    Buffer A should never fill up. Before it fills up, you have switched the buffers around. So your two buffers aren't providing overrun protection per say. Rather, they are providing an empty runway every time you receive data. 

        case UART_RX_RDY:
            tracking = 1;
            int i = 0;
            uint8_t temp = 0;
            uint8_t length = evt->data.rx.len;

            for(i = 0i<evt->data.rx.len; i++)
            {
                temp = evt->data.rx.buf[evt->data.rx.offset + i];
                tx_buf2[i= temp;
            }
            ret = uart_rx_disable(dev);
            ret = uart_tx(dev ,tx_buf2,length,SYS_FOREVER_MS);
            break;
        case UART_RX_DISABLED:
            tracking += 1;
            if (rx_buf_indicator == 0)
            {
                ret = uart_rx_enable(dev ,rx_buf1,sizeof(rx_buf1),RECEIVE_TIMEOUT);
                ret = uart_rx_buf_rsp(dev, rx_buf0, sizeof(rx_buf0));
                rx_buf_indicator = 1;
            }
            else
            {
                ret = uart_rx_enable(dev ,rx_buf0,sizeof(rx_buf0),RECEIVE_TIMEOUT);
                ret = uart_rx_buf_rsp(dev, rx_buf1, sizeof(rx_buf1));
                rx_buf_indicator = 0;
            }
            break;

    And I set the prj.config settings back to the defaults from lesson 5, and that doesn't seem to make a difference. 

    ## These prj.config settings work

    CONFIG_SERIAL=y
    CONFIG_UART_ASYNC_API=y

    ## These prj.config settings also works

    CONFIG_SERIAL=y
    CONFIG_UART_INTERRUPT_DRIVEN=y
    CONFIG_UART_0_INTERRUPT_DRIVEN=n
    CONFIG_UART_LINE_CTRL=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=1
    CONFIG_NRFX_UARTE0=y
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_PPI=y
    Please provide input
    1.The problem didn't appear to be with the prj.config file.
    2. The solution didn't appear to be in the DEVACADEMY example.
    3. I also have a mild worry about dropped data that could be caused by disabling the UART_RX stream.\
    4. I would also like to confirm why this is a problem or a better way to resolve this ASYNC UART problem...? 
Children
  • Canadian_EE said:
    4. I would also like to confirm why this is a problem or a better way to resolve this ASYNC UART problem...? 

    Hi Canadian_EE,

    I missed to see the last questions. I do see any questions in point 1 and 2. 

    3. Every application is different in terms of dropped data. There should not be any dropped data at all if you are using a hardware flow control. Are you sure that you have set the hardware flow control for uart. (It is normally set in the uart_configure function). I do not see how the data is dropped or lost with hwfc unless the app is not handling the buffers correctly.

    4. Reading the source code in ncsv2.4.1, I see that data loss should not happen with hardware flow control enabled. And your workaround seems to be acting as if the hardware flow control is disabled in your setting and you are trying to handle double buffers just to give some extra buffer space for the incoming and outgoing data.

  • Hi Susheel, 

         UART with DMA should not drop data, particularly when the buffer rolls over. In my opinion, this isn't a hardware flow control or not flow control issue. Flow control might help, but the DMA with interrupts should properly handle the buffer filling up. The fact that the ASYNC UART DMA approach doesn't work properly indicates a problem or flaw with the Nordic Zephyr ASYNC UART. The DMA should alleviate the need for for flow control by providing a continuous receive capability. 

    Also, the prj.config settings do not appear to make a difference. Why is that? 

  • Canadian_EE said:
    In my opinion, this isn't a hardware flow control or not flow control issue. Flow control might help, but the DMA with interrupts should properly handle the buffer filling up.

    Well the easyDMA is not the only variable that effects the throughput of the application. The applicaiton normally have post processing latencies for every serial transaction which is more than making a new request. You need to analyze how much time is spend with every transaction of data received/sent in your application. If you are not processing the data at all (ignoring the incoming data) and only spending time in the application to reconfigure the DMA pointers, then I would understand your logic of flow control not needed with EasyDMA. You can do a simple test of lowering the baudrate on both sides and see if you see the same issue. If you do, then what I think about flow control and throughput even with Async UART is correct. If you do not see the same issue, then we can remove the flow control from our discussions.

  • Hi Susheel, 

       can you explain your comment again? I don't quite follow what you mean. 

Related