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

LIBUARTE on ble_app_uart with DMA

Hi All,

I have 2 boards nrf52840 pc10056 and I need to run ble_app_uart using LIBUARTE with DMA in order to obtain a more robust uart communication

I would like to use an uart DMA that will work in background saving the byte received in a buffer (fixed length) and once that it is full having an interrupt where I can check this buffer and send it to the central unit.

Is there any easy way to set  everything up and change the code of ble_app_uart? Any suggestion??

Thanks

  • Hello,

    I managed to implement libuarte in ble_app_uart and, finally I have no problems compiling and I can send data from the "peripheral" to the "central".

    Obviously sending the data I have a lot of errors yet that I hope you will be able to help me.

     

    For now I have focused only on the "peripheral" side, which means that the central remained as from SDK 15.3 (except for the fact that I removed the ECHO of the data, so the data received are not sent back).

     

    Of course I’ve modified something in the sdk_config.h and the main in the ble_app_uart example.

    In the MAIN, compare to the ble_app_uart example, I’ve changed the init of the UART:

    //UART INIT:
    …………………
    NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 3, 0, 4, 75, 3);
    ………………………………………………………..
    
    static void uart_init(void)
    {
    nrf_libuarte_async_config_tnrf_libuarte_async_config = {
                      .tx_pin     = TX_PIN_NUMBER,
                      .rx_pin     = RX_PIN_NUMBER,
                      .baudrate   = NRF_UARTE_BAUDRATE_115200,
                      .parity     = NRF_UARTE_PARITY_EXCLUDED,
                      .hwfc       = NRF_UARTE_HWFC_DISABLED,
                      .timeout_us = 100,
              };
    
    ret_code_terr_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, &libuarte);
    
              APP_ERROR_CHECK(err_code);
    
    nrf_libuarte_async_enable(&libuarte);
    
    }
    

    and the "void uart_event_handler" method which I report below:

    /**@snippet [UART Initialization] */
    
    //UART_EVENT_HANDLER:
    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
            nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
                uint32_t       err_code;
            ret_code_t ret;
            
            
            switch (p_evt->type)
                {
                        case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                            bsp_board_led_invert(0);
                            break;
                        case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                        	ret=nrf_libuarte_async_tx(p_libuarte,  p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);
                        	bsp_board_led_invert(2);
                        
                        //	do
                        //                    {
                        //                        uint16_t length = (uint16_t)index;
                        //                        err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                        //                        if ((err_code != NRF_ERROR_INVALID_STATE) &&
                        //                            (err_code != NRF_ERROR_RESOURCES) &&
                        //                            (err_code != NRF_ERROR_NOT_FOUND))
                        //                        {
                        //                            APP_ERROR_CHECK(err_code);
                        //                        }
                        //                    } while (err_code == NRF_ERROR_RESOURCES);
                        
                        	nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data,p_evt->data.rxtx.length);
                        	break;
                        case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                            bsp_board_led_invert(3);
                            break;
                            default:
                            break;
                }
    }
    

    The idea is:

    Write from microcontroller on the peripheral UART, the peripheral receives and sends to the central. Before doing this, or using the "ble_nus_data_send" I would like to see what happen in the peripheral side, so,once I received  the data on the UART, instead to use "ble_nus_data_send" (that for sure once I remove the comments will give some problem),I print everything on the peripheral UART terminal with "nrf_libuarte_async_tx".

    If with a microcontroller I send a string of "N" characters that ends with "\n" every "s" m_seconds what should happen is:

    As is the program, (considering the commented lines) once the string is received it is printed. The problem is that what I print is not exactly what I send, but it is like that in the buffer something is stored and something is repleced from a new data.

    If I'm sending a buffer like this:

    ABCDEFG\n, for instance I get something "similar":

    ABCDEFG\n

    ABCDEFG\n

    ABCDEFG\n

    ABCDEFABCDEFGABCDEFGABCDEFG\n

    .

    .

    .

    ABCABCDEFG\nABCDEFG

    ABCDEFG\n

    ABCDEFG\n

    I do believe that is something releated to the size of the buffer here:

    "NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 3, 0, 4, 75, 3);"

    But, if I try with the experimental_libuarte it works perfectly. In that example I modified few rows that I report here:

     

    MAIN experimental_libuarte:

    voiduart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
    nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
    
    ret_code_t ret;
    
    switch (p_evt->type)
        {
    case NRF_LIBUARTE_ASYNC_EVT_ERROR:
    bsp_board_led_invert(0);
    break;
    case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
    bsp_board_led_invert(1);
    ret = nrf_libuarte_async_tx(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
    nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
    break;
    case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
    bsp_board_led_invert(2);
    break;
    default:
    break;
        }
    }
    

    This works perfectly.

      

    What am I doing wrong?

    Am I missing something?

    To be honest, I didn't understand how the buffer (DMA size etc) works. Can you help me?

    For instance, I would like the characters to be stored in the DMA buffer (which I don't know how to access), and an interrupt to be triggered only when the buffer is filled. Once that the interrupt happens the program goes into the event handler routine in the main and I print the buffer.

     

    - Another thing is that with the ble_app_uart that I modified, the board works just in debug. If I just flash it in Release or Debug and then I try to use, it does not work.

    -The experimental_libuarte that you released in SDK15.3 in "DEBUG" mode goes directly to NRF_BREAKPOINT_COND, in Release no.

    Thanks

  • Hi,

    I have to admit it's been a while since I worked with libuarte. but the Libuarte driver is a bit special given that it uses a timer to stop RX if the peer does not send date for a certain amount of time. This means that if you send multiple short packets with a given delay inbetween your application might not be able to read the data from the RX buffer before it is switced to the next buffer and then back again. the pointer for DMA will typically be updated between the rx events. Also this means that the other part of the uart communication has to respect these limitations. so if it want to send a lot of data to one buffer that has to be completed within a certain time (you cannot wait to send more data as the rx timer will expire). Or if you want to send short packets you have to wait for the timeout to occur, before sending the next one.

    If it's possible to use HW flow control, you could disable the timer and use that instead. Not sure if this is possible with the driver at this stage. Note that the timeout was added to make it possible to transimt at high baudrates without hwfc.

  • Hi All,

    I have partially solved the problem. When I try to send the data over Bluetooth, from the other side I get them, but the peripheral stops at certain point to send data.

    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        uint32_t       err_code;
        ret_code_t ret;
    
        switch (p_evt->type)
        {
            case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                bsp_board_led_invert(1);
                break;
            case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                bsp_board_led_invert(2);
                err_code= ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data, &p_evt->data.rxtx.length, m_conn_handle);
                do
                {
                    if ((err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_NOT_FOUND))
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                } while (err_code == NRF_ERROR_RESOURCES);
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);    
                break;
            case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                bsp_board_led_invert(3);
                break;
            default:
                break;
        }
    }

    Exactly sends the data based on the size of the buffer when I define the NRF_LIBUARTE (the RX in this case). The larger the buffer the greater the number of packets sent, then it stops to send packets.

    NRF_LIBUARTE_ASYNC_DEFINE (libuarte, 0, 3, 0, 4, "RX", 3);

    The same thing happen if for example, in stead to send the data received from UART to the central, I print them again on the UART of the peripheral and I do not make free the RX buffer:

    "nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);  " 

    See the code below:

    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        uint32_t       err_code;
        ret_code_t ret;
    
        switch (p_evt->type)
        {
            case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                bsp_board_led_invert(1);
                break;
            case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                bsp_board_led_invert(2);
                ret = nrf_libuarte_async_tx(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
    //          err_code= ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data, &p_evt->data.rxtx.length, m_conn_handle);
                do
                {
                    if ((err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_NOT_FOUND))
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                } while (err_code == NRF_ERROR_RESOURCES);
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);    // if I remove this line of code I will have more or less the same behavior 
                break;
            case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                bsp_board_led_invert(3);
                break;
            default:
                break;
        }
    }

    Any help is appreciated ;) Upside downUpside down 

    Thank you all Slight smileSlight smileSlight smile

  • Hi

    I'm having some trouble understanding what exactly you mean. Is it the UART or the BLE communication you're having trouble with? It seems to me that you're able to receive the UART data filling up your buffer to the peripheral, and then sending this data to the central. Are you having trouble clearing the RX buffer from the peripheral before receiving a new "batch" of UART data?

    Best regards,

    Simon

  • Hi Simonr,

    In this moment I'm able to receive the data from the micro-controller to the peripheral via UART (I'm sending a fixed length packet from micro to peripheral). Once that I receive this packet I've tried to do 2 things:

    1) The peripheral receive the DATA from the micro-controller via UART and print them again on its own UART (I print the packet received on the terminal, in order to have a visual of what is happening and if the peripheral is receiving good the data from the micro-controller).

    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        uint32_t       err_code;
        ret_code_t ret;
    
        switch (p_evt->type)
        {
            case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                bsp_board_led_invert(1);
                break;
            case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                bsp_board_led_invert(2);
                ret = nrf_libuarte_async_tx(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);    // if I remove this line of code I will have more or less the same behavior 
                break;
            case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                bsp_board_led_invert(3);
                break;
            default:
                break;
        }
    }

     The important code lines are:

    -     ret = nrf_libuarte_async_tx(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
    -     nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);

    In this case it works perfectly (It means I can see on the terminal the packet that I'm sending from the micro-controller).

    For instance, if I comment out the line where I make free the buffer:

    -     ret = nrf_libuarte_async_tx(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
    -   //  nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);

    What will happen is that I will see on terminal a number of packets that is proportional to the RX buffer before to stop working. Rx that I set here:

    - NRF_LIBUARTE_ASYNC_DEFINE (libuarte, 0, 3, 0, 4, "RX", 3);

    It means that, if I have a bigger RX buffer I'll see more packet on the terminal before it stops.

    Conclusion on this point: 

    - If I want to print on terminal what I' m receiving from micro-controller is important to make free the RX buffer. In that case everything is working good and reliable.

    2) The peripheral will send the packets that it receives, from the micro-controller via UART, to the central over bluetooth. The central, once got the packet, will print it on the terminal. 

    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        uint32_t       err_code;
        ret_code_t ret;
    
        switch (p_evt->type)
        {
            case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                bsp_board_led_invert(1);
                break;
            case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                bsp_board_led_invert(2);
                err_code= ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data, &p_evt->data.rxtx.length, m_conn_handle);
                do
                {
                    if ((err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_NOT_FOUND))
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                } while (err_code == NRF_ERROR_RESOURCES);
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);    // if I remove this line of code I will have more or less the same behavior 
                break;
            case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                bsp_board_led_invert(3);
                break;
            default:
                break;
        }
    }

    Important lines:

    -    err_code= ble_nus_data_send(&m_nus, p_evt->data.rxtx.p_data, &p_evt->data.rxtx.length, m_conn_handle);
    -    nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);

    In this case the peripheral start to sends packets to the central, but after a while it stops and crash.

    The number of packets sent, like in the 1) case, is proportional to the RX buffer size that I've set here:

    NRF_LIBUARTE_ASYNC_DEFINE (libuarte, 0, 3, 0, 4, "RX", 3);

    All of this happens without commenting out:

    -   nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data , p_evt->data.rxtx.length);

    So, it's like that the RX_buffer cannot be cleaned once that I send data over bluetooth with "ble_nus_data_send".

    So, my problem is that when I try to send the packet from the peripheral to the central over bluetooth, it stops after a while. The behaviour in the case 2 is the one where I'm asking you help. The case 1 is just a case where the problem seems to have the same behaviour once that I comment out the "nrf_libuarte_async_rx_free".

Related