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

Delay before starting DMA'd UART transfer

I need to send data over ModBus-RTU (basically RS485) from an nRF52840. For this, I use uarte. The transceiver's transmit enable input is controlled through a GPIO pin, which in turn is controlled from the PPI (NRF_UARTE_EVENT_TXSTARTED and  NRF_UARTE_EVENT_TXSTOP events). When I check on the scope, I see a varying time between the enable pin being raised and the start of the actual transfer. For example: in the below capture, the delay between TXD (in blue) and the enable pin (in red) is 23.67 usec, This delay varies from 0 up to one bittime (I've seen a 103 usec delay when transmitting on 9600 bps).

Can I remove these delays or otherwise at least stabilize them so that they always are the same?

This is my code for initialization:

void rs485_init ( void )
{
    uint32_t task_addr;
    uint32_t event_address;
    static bool _init_done = false;
    nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG;
    uarte_config.pseltxd  = MODBUS_TX_PIN;
    uarte_config.pselrxd  = MODBUS_RX_PIN;
    uarte_config.hwfc     = NRF_UARTE_HWFC_DISABLED;

    nrfx_gpiote_out_config_t nrfx_gpiote_out_config = NRFX_GPIOTE_CONFIG_OUT_TASK_TOGGLE(0);

    uarte_config.baudrate = NRF_UARTE_BAUDRATE_9600;
    uarte_config.parity = NRF_UARTE_PARITY_INCLUDED;
    
    if (_init_done) nrfx_uarte_uninit(&uarte); 
    APP_ERROR_CHECK(nrfx_uarte_init( &uarte, &uarte_config, uarte_event_handler)); // Init in non-blocking mode

    if ( !nrfx_gpiote_is_init() ) 
    {
        nrfx_gpiote_init();
    }
    else
    {
        nrfx_gpiote_out_uninit( MODBUS_RTS_PIN );
    }

    // Initialize the RTS pin as output, to be driven from UART TX via PPI
    APP_ERROR_CHECK(nrfx_gpiote_out_init( MODBUS_RTS_PIN, &nrfx_gpiote_out_config ));
    nrfx_gpiote_out_task_enable( MODBUS_RTS_PIN );

    // Setup PPI to set enable on start of transmission
    APP_ERROR_CHECK( nrfx_ppi_channel_alloc( &_ppi_channel_start ) ); 
    event_address = nrfx_uarte_event_address_get( &uarte, NRF_UARTE_EVENT_TXSTARTED );
    task_addr = nrfx_gpiote_out_task_addr_get( MODBUS_RTS_PIN );
    APP_ERROR_CHECK( nrfx_ppi_channel_assign( _ppi_channel_start, event_address, task_addr ) );
    nrfx_ppi_channel_enable( _ppi_channel_start );

    // Setup PPI to clear enable on end of transmission
    APP_ERROR_CHECK( nrfx_ppi_channel_alloc( &_ppi_channel_stop ) ); 
    event_address = nrfx_uarte_event_address_get( &uarte, NRF_UARTE_EVENT_TXSTOPPED );
    task_addr = nrfx_gpiote_out_task_addr_get( MODBUS_RTS_PIN );
    APP_ERROR_CHECK( nrfx_ppi_channel_assign( _ppi_channel_stop, event_address, task_addr ) );
    nrfx_ppi_channel_enable( _ppi_channel_stop );

    // Receiver initialization removed for brevity

    _init_done = true;
    NRF_LOG_INFO( "RS485 initialized" );
}

Parents
  • Hi,

     

    The transceiver's transmit enable input is controlled through a GPIO pin, which in turn is controlled from the PPI (NRF_UARTE_EVENT_TXSTARTED and  NRF_UARTE_EVENT_TXSTOP events). When I check on the scope, I see a varying time between the enable pin being raised and the start of the actual transfer. For example: in the below capture, the delay between TXD (in blue) and the enable pin (in red) is 23.67 usec, This delay varies from 0 up to one bittime (I've seen a 103 usec delay when transmitting on 9600 bps).

    Your code shows that you set/clr a GPIOTE OUT task based on events _TXSTOPPED and _TXSTARTED.

    How do you actually stop and start the UARTE peripheral using TASKS_START and TASKS_STOP?

      

    PS: Looks like there's a picture missing?

     

    Kind regards,

    Håkon

  • Looks like there's a picture missing?

    I can see the picture from my PC, uploaded to your servers and included it here. Anyway, it's just an image of the scope's stream showing there's a 23-something usec delay between the GPIO pin rising and TXD falling for the startbit.

    I start the transmission of the uart through nrfx_uarte_tx().

  • I would hazard a guess that the baud rate prescaler is not cleared when simply doing Start and Stop actions and therefore up to a single bit time of jitter from STARTTX to start bit active would be inevitable; however maybe unitit() and init() the UARTE before each transfer would clear the baud rate prescaler. Worth a test; a nuisance to apply but it would perhaps reduce the jitter.

    The alternative is to read the secret prescaler register and wait until it reaches a specific count before triggering the STARTTX, or use a parallel timer for the situation where the secret register is unknown or non-existent.

    To find the secret register (assuming it exists) do repeated breaks and look at any UARTE hardware registers changing in a linear fashion with the slowest possible baud rate.

    Edit: Another option may be to simply rewrite the BAUDRATE register with the same baud rate value; the nRF52 chip designers may have made that generate a prescaler reset to 0.

    Edit 2: I checked and there is no secret prescaler register visible in the memory block for the Uart. I haven't tried the rewrite baud register trick yet, but will try to find time later

  • Hi,

     

    jev said:
    I can see the picture from my PC, uploaded to your servers and included it here. Anyway, it's just an image of the scope's stream showing there's a 23-something usec delay between the GPIO pin rising and TXD falling for the startbit.

    My apologies for the inconvenience, but could you re-upload the picture? I reported this incident to the web admins, as the picture itself is not available at my end.

     

    Kind regards,

    Håkon 

  • Hi,

    Håkon has been on vacation so I have taken over this case. Did you have time to test the recomendations from user ? Are you still struggling with this or can I close this case?

    Kind regards,

    Marjeris

  • Hi Marjeris,

    No, I did not test those recommendations. I can't just uninit(), it could break any transfers coming in through the same uart's RX. Not a fan of undocumented registers and/or hardware behavior either, they will break one day without prior notice.

    The problem still exists. The suggestions, however well meant they are, are based on guesswork. Is there someone that can actually answer from the sillicon design?

Reply
  • Hi Marjeris,

    No, I did not test those recommendations. I can't just uninit(), it could break any transfers coming in through the same uart's RX. Not a fan of undocumented registers and/or hardware behavior either, they will break one day without prior notice.

    The problem still exists. The suggestions, however well meant they are, are based on guesswork. Is there someone that can actually answer from the sillicon design?

Children
Related