Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Half-duplex UART (single wire) with and two nRF52840-DK

I'm trying to achieve half-duplex UART communication between two nRF52840-DK:s (master/slave) using no external circuitry. I was looking at the thread https://devzone.nordicsemi.com/f/nordic-q-a/15513/nrf52832-single-wire-uart where the OP had a similar objective. I have been experimenting with both libuarte and app_uart(_fifo) without any success. 

The scenario I am trying to exemplify is that the master transmits a byte to the slave followed by the slave receiving that byte and respond with an incremented byte. The master should then receive the incremented byte.

The slave is able to receive the byte sent from the master correctly, and it then transmits the incremented bytes. However, the problem is that the master receives the same byte that it transmitted initially instead of the incremented byte.

This is my code so far for app_uart (libuarte code essentially does the same but does the GPIO reconfiguration in the event handler, results in the same behaviour):

Master:

    nrf_gpio_cfg(
        27,
        NRF_GPIO_PIN_DIR_OUTPUT,
        NRF_GPIO_PIN_INPUT_DISCONNECT,
        NRF_GPIO_PIN_PULLUP,
        NRF_GPIO_PIN_H0D1,
        NRF_GPIO_PIN_NOSENSE);

    uint8_t cr = 5;
    NRF_LOG_INFO("Sent char %d", cr);
    while (app_uart_put(cr) != NRF_SUCCESS);
    nrf_delay_ms(2500);
    nrf_gpio_cfg(
        27,
        NRF_GPIO_PIN_DIR_INPUT,
        NRF_GPIO_PIN_INPUT_DISCONNECT,
        NRF_GPIO_PIN_PULLUP,
        NRF_GPIO_PIN_H0D1,
        NRF_GPIO_PIN_NOSENSE);

    while (app_uart_get(&cr) != NRF_SUCCESS);
    nrf_delay_ms(2500);
    NRF_LOG_INFO("Received char %d\r\n", cr);

Slave:

uint8_t cr;

nrf_gpio_cfg(
    27,
    NRF_GPIO_PIN_DIR_INPUT,
    GPIO_PIN_CNF_INPUT_Connect,
    NRF_GPIO_PIN_NOPULL,
    NRF_GPIO_PIN_H0D1,
    NRF_GPIO_PIN_NOSENSE
);

while (app_uart_get(&cr) != NRF_SUCCESS);
nrf_delay_ms(2500);
nrf_gpio_cfg(
        27,
        NRF_GPIO_PIN_DIR_OUTPUT,
        GPIO_PIN_CNF_INPUT_Connect,
        NRF_GPIO_PIN_NOPULL,
        NRF_GPIO_PIN_S0S1,
        NRF_GPIO_PIN_NOSENSE
    );
NRF_LOG_INFO("Received char %d", cr);
cr += 1;
while (app_uart_put(cr + 1) != NRF_SUCCESS);
NRF_LOG_INFO("Sent char %d\r\n", cr);

In both cases, app_uart has been initialized with

const app_uart_comm_params_t comm_params =
{
    27,
    27,
    RTS_PIN_NUMBER,
    CTS_PIN_NUMBER,
    APP_UART_FLOW_CONTROL_DISABLED,
    false,
    115200
};

APP_UART_FIFO_INIT(&comm_params,
                     UART_RX_BUF_SIZE,
                     UART_TX_BUF_SIZE,
                     uart_error_handle,
                     APP_IRQ_PRIORITY_LOWEST,
                     err_code);

Parents Reply Children
  • Hi Jörgen,

    Thanks for the swift reply.

    I just tried your suggestion, where it currently looks like this:

    Master:

    static void init_uart(uint32_t rx_pin, uint32_t tx_pin);
    
    void uart_error_handle(app_uart_evt_t * p_event)
    {
        if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_communication);
        }
        else if (p_event->evt_type == APP_UART_FIFO_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_code);
        }
        else if (p_event->evt_type == APP_UART_TX_EMPTY)
        {
            init_uart(27, UART_PIN_DISCONNECTED);
            NRF_LOG_DEBUG("TX Empty");
        }
    }
    
    static void init_uart(uint32_t rx_pin, uint32_t tx_pin)
    {
        uint32_t err_code;
    
        const app_uart_comm_params_t comm_params =
        {
            rx_pin,
            tx_pin,
            RTS_PIN_NUMBER,
            CTS_PIN_NUMBER,
            APP_UART_FLOW_CONTROL_DISABLED,
            false,
    #if defined(UART_PRESENT)
            NRF_UART_BAUDRATE_115200
    #else
            NRF_UARTE_BAUDRATE_115200
    #endif
        };
    
        APP_UART_FIFO_INIT(&comm_params,
                           UART_RX_BUF_SIZE,
                           UART_TX_BUF_SIZE,
                           uart_error_handle,
                           APP_IRQ_PRIORITY_LOWEST,
                           err_code);
        APP_ERROR_CHECK(err_code);
    }
    
    int main(void)
    {
        .
        .
        .
        init_uart(UART_PIN_DISCONNECTED, 27);
        
        uint8_t cr = 5;
        NRF_LOG_INFO("Sent char %d", cr);
        while (app_uart_put(cr) != NRF_SUCCESS);
        nrf_delay_ms(2500);
        
        while (app_uart_get(&cr) != NRF_SUCCESS);
        nrf_delay_ms(2500);
        NRF_LOG_INFO("Received char %d\r\n", cr);
        .
        .
        .
    }

    This results in a fatal error from APP_ERROR_CHECK in init_uart(). Should I not be re-initializing app_uart in the UART event handler? 

  • You need to first un-initialize the uart library using app_uart_close(). The APP_UART_FIFO_INIT() macro will initialize the UART driver. If you call this macro again without first un-initializing the driver, it will report that it is already initialized and give you an error.

    I'm not 100% sure if the close-function will wait for any events that has lower or equal IRQ priority to the one in uart_error_handle(). It is safer to set a flag in the handler and do the re-initialization in the main context.

  • Thanks Jorgen, that fixed my problem, and the communication between the DK:s now works in half-duplex.

    For anyone trying to do accomplish something similar, keep in mind that the flag might have to be volatile if you wait for the flag in your main context and modify it in the handler.

Related