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

nrf_serial_write not working

This is my code:

void send_serial_message(char *buffer, size_t length) {
	ret_code_t ret;
	
	//DEBUG
	NRF_LOG_INFO("tx buffer length = %d", length);
	for(int i=0; i<length; i++)
	{
		NRF_LOG_INFO("%02x", buffer[i]);
	}
	//DEBUG end
	
    nrf_gpio_pin_set(TRANSMIT_ENABLE);
	ret = nrf_serial_write(&serial1_uart,
	                           buffer,
	                           length,
	                           NULL,
	                           NRF_SERIAL_MAX_TIMEOUT);
	APP_ERROR_CHECK(ret);
    NRF_LOG_INFO("serial return val: 0x%x", ret);
}

This is the results of the logs in the code: 

<info> app: tx buffer length = 8
<info> app: AA
<info> app: FF
<info> app: 10
<info> app: 08
<info> app: F7
<info> app: 10
<info> app: 10
<info> app: D8
<info> app: serial return val: 0x0

The return code shows that it was "successful." However, the serial write is not working properly. Here is what I measure using a logic analyzer on the serial transmission pin:

This is showing the 0xAA gets sent properly, but then the line goes low, and the rest of the sequence fails to transmit. 

What is happening??

Parents
  • How about your baud rate and the NRF_SERIAL_MAX_TIMEOUT?

    If TX is latched to low, and never go up to high. I think that it must be conflicted with other function with P1.14 or the external chip latch the output level.

    You may check code for these cause.

    Another question is ....What's the TRANSMIT_ENABLE pin used for?

  • Thanks for the suggestions

    The transmit_enable pin is for the external serial comms chip that connects the TX output from NRF52840 to the serial bus. Even if data is being sent out on TX, if the transmit_enable is not on, then the data will not be received on the serial bus. 

    I've successfully used the NRF chip with the same code above with the same serial comms chip before, but somehow it's not working anymore. It doesn't seem hardware related because I've tried multiple PCBs (all containing the same serial comms chip and the same NRF chip, in the same configuration) and the result is the same. 

    I think that it must be conflicted with other function with P1.14

    From the data sheet p. 576 it looks like P1.14 has only one function. 

    Is there a way to get the serial port driver to tell me what specific error it is encountering? Right now I am not really getting any errors, I just notice a failed transmission, so it is hard to troubleshoot.

  • So...May you show the  nrf_serial_write() function? Because it's seems like TX level latched low after first byte.

    And your nrf log output is assigned by RTT or UART? If nrf log is drived by UART, you should  take care the UART initialization just one olny. And the UART handler can't be conflicted.

    Usually you can use printf() function ,if uart driver is enable.

      As my experience, there is no any  nrf_serial_write()  function in SDK17.0.2

    If you want to make the TX FIFO out function , the below example which I make in old project for reference.

     

    static bool uart_serial_tx(uint8_t * p_buf, uint32_t len)
    {
    bool ret = true;

    uint32_t ret_code = nrf_drv_uart_tx(&m_uart, p_buf, len);
    if (ret_code != NRF_SUCCESS)
    {
    ret = false;
    }

    return ret;
    }

    apply for nrf_uart_drv.c  nrf_drv_uart_tx() with uart fifo.

  • nrf_serial_write comes from the Nordic SDK. I am using SDK 14.2. It is part of the serial port library.

    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_drv_is_in_RAM(p_data) &&
             p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_DMA)
        {
            return NRF_ERROR_INVALID_ADDR;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t const * p_buff = p_data;
    
        do
        {
            size_t wcnt = serial_tx(p_serial, p_buff, left);
            left -= wcnt;
            p_buff += wcnt;
            if (!left)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (p_written)
        {
            *p_written = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }

    your nrf log output is assigned by RTT or UART?

    I have this in my app_config.h:

    #define NRF_LOG_BACKEND_UART_ENABLED 0
    #define NRF_LOG_BACKEND_RTT_ENABLED 1

    To be honest, I have always been confused about the difference between the serial port library, the UART driver, and the UART module. Are they compatible with each other? Are they built on each other? I inherited code that uses the serial port library, but there are often serial driver errors that we can't find the source of, and of course it is a huge issue that the serial transmission isn't working properly. What are the pros and cons of each? nrf_drv_uart_tx() looks like it comes out of the UART driver, and it seems much simpler. Do you know what is the purpose of the serial port library if you can use nrf_drv_uart_tx() directly? 

    EDIT: 

      Thanks to you both for your help! After more investigation, I have figured out what is causing the problem, but I do not know how to solve it, and I am hoping you might have some ideas.

    In the screenshot below, TE is transmit enable, TX is the GPIO pin controlled by nrf_serial_write(), and A is the serial bus. 

    The problem seems to be that TE (transmit enable) does not go high for long enough. I turn TE off when I get a NRF_SERIAL_EVENT_TX_DONE event. From the answer here, I thought that the NRF_SERIAL_EVENT_TX_DONE occurs only after the whole message is transmitted. But based on my capture below, it looks like I get a NRF_SERIAL_EVENT_TX_DONE after the first byte. 

    Do you have any suggestions to make sure that the Transmit_Enable goes high for the whole transmission, and turns off only after the transmission is done? Maybe is there a different event that occurs after the entire buffer is empty and has been transmitted over UART? Thanks! 

  • Hi 

    The TX_DONE event basically happens after every transmitted byte. When using the older UART peripheral there is no buffering happening in the UART peripheral itself, this is all handled by the driver. 

    If you want a callback when the TX buffer in the driver is empty I think you will need to change the nrf_serial.c implementation itself. 

    If you look at how NRF_DRV_UART_EVT_TX_DONE is handled you will see that it polls the TX queue to see if there are more bytes to send, and breaks otherwise:

    case NRF_DRV_UART_EVT_TX_DONE:
    {
        nrf_queue_t const * p_txq =
                p_serial->p_ctx->p_config->p_queues->p_txq;
        nrf_serial_buffers_t const * p_buffs =
                p_serial->p_ctx->p_config->p_buffers;
    
        event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);
        size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
        if (len == 0)
        {
            break;
        }
    
        ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
        ASSERT(ret == NRF_SUCCESS);
        break;
    }

    You will also note that the application event is triggered for every byte, by this call:

    event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);

    By moving this call into the if (len == 0) check you should change the driver to only call the TX_DONE event when all the bytes are sent, which sounds like what you need. 

    Best regards
    Torbjørn

Reply
  • Hi 

    The TX_DONE event basically happens after every transmitted byte. When using the older UART peripheral there is no buffering happening in the UART peripheral itself, this is all handled by the driver. 

    If you want a callback when the TX buffer in the driver is empty I think you will need to change the nrf_serial.c implementation itself. 

    If you look at how NRF_DRV_UART_EVT_TX_DONE is handled you will see that it polls the TX queue to see if there are more bytes to send, and breaks otherwise:

    case NRF_DRV_UART_EVT_TX_DONE:
    {
        nrf_queue_t const * p_txq =
                p_serial->p_ctx->p_config->p_queues->p_txq;
        nrf_serial_buffers_t const * p_buffs =
                p_serial->p_ctx->p_config->p_buffers;
    
        event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);
        size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
        if (len == 0)
        {
            break;
        }
    
        ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
        ASSERT(ret == NRF_SUCCESS);
        break;
    }

    You will also note that the application event is triggered for every byte, by this call:

    event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);

    By moving this call into the if (len == 0) check you should change the driver to only call the TX_DONE event when all the bytes are sent, which sounds like what you need. 

    Best regards
    Torbjørn

Children
No Data
Related