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

Clarification on UARTE error interrupts

Following the insn here: https://devzone.nordicsemi.com/f/nordic-q-a/54591/nrf_serial_event_drv_err-what-is-the-cause

I found that under certain conditions, I get ERRORSRC = 0x4 and 0x1, corresponding to C (frame error) and A (overrun) respectively.

I am trying to understand how the uart driver works, so that I can possibly modify this code for my needs.

1. If I am in this condition: 

#if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
    // UARTE and UART combined

How does this actually work? It seems like if this is true, it always ends up using UARTE and not UART. Is that the case? Why?

2. What actually generates the UARTE0 interrupt? It would be helpful to be able to trace it back to the source and understand what register changes, etc. causes an interrupt to be triggered, particularly for errors. 

Thanks!

Parents
  • Hi,

    1. If I am in this condition: 

    Fullscreen
    1
    2
    #if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
    // UARTE and UART combined
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    #if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
        // UARTE and UART combined

    How does this actually work? It seems like if this is true, it always ends up using UARTE and not UART. Is that the case? Why?

     Which SDK version are you using? I cannot find this section since SDK v14.2.0. This means that the driver supports both UART and UARTE. You can select which peripheral should be used by this sdk_config:

    // <q> UARTx_CONFIG_USE_EASY_DMA  - Default setting for using EasyDMA
     
    
    #ifndef UARTx_CONFIG_USE_EASY_DMA
    #define UARTx_CONFIG_USE_EASY_DMA 1
    #endif

    Where 'x' is the UART instance (only instance 0 is available in nRF52832).

    2. What actually generates the UARTE0 interrupt? It would be helpful to be able to trace it back to the source and understand what register changes, etc. causes an interrupt to be triggered, particularly for errors. 

    The Interrupt is generated by EVENTS from the peripheral, given that the two following conditions are met:

    • Interrupts are enabled for the given EVENT in the INTENSET register.
    • Interrupts are enabled for the peripheral in NVIC (by calling NVIC_EnableIRQ(IRQn))

    See the figure on this page for details on how this is connected. NVIC will call the IRQ handler defined in the MDK, named UARTE0_UART0_IRQHandler() if a interrupt is generated, the interrupt handler needs to check the events to see which event generated the interrupt.

    Best regards,
    Jørgen

  • Yes - sorry, I forgot to mention. I am using SDK 14.2. SoftDevice 140. NRF52840.

    Thanks very much for this info. So does this mean that if/when I update my SDK to a newer version like SDK 17, some things that use the UART driver might break? 

    To be more specific, my problem is that if I encounter a serial error, like a *single* frame or overrun error, often I don't stop getting NRF_SERIAL_EVENT_DRV_ERR for a long time. Usually it takes 20s or more for it to recover*, sometimes it never recovers. During this time, I have completely normal serial data coming in. There are plenty of gaps between incoming RX data, ranging from 5ms to 26ms, for the UART to "reset" itself / realign around the correct bit framing. But I am getting thousands of NRF_SERIAL_EVENT_DRV_ERR (during my tests, I recorded 4146 one time, 10161 another time, and a third time, it was an infinite stream) when I should only be getting a few handfuls. Why is this happening? Why does it keep giving serial errors when the error is long gone?

    My observations:

    • I can break out of this if I un-init and re-init the serial driver after X errors (I picked X = 100, *when it takes 20s+ to recover, it was when I picked a larger number like X=5000). But this is kind of terrible, because I shouldn't have to uninit and reinit the driver just because it encountered a serial error. It should just be able to report the error and resync with the next start bit, if not immediately, then at least after a gap in which the bus is idle for some time. If I DON'T have this uninit/reinit enabled, I can get serial errors on startup that the device NEVER recovers from! How can I make sure the UART recovers without having to do this init/uninit thing?
    • If I have logging turned on to print out when an error happens, it seems to make it more likely that I get an infinite loop of errors, even if I have the uninit/reinit enabled. Generally, it will recover after an uninit/reinit if I don't have logging on.

    I would like to be able to dig into the logic that causes the UART error IRQ to get triggered. The PPI diagram you provided is a bit too vague... can you point me toward a relevant part of the code? 

    EDIT ----------------------------------------------------------------

    I found this and suspected that this was part of the problem. It looks like instead of clearing the ERRORSRC register, the ERRORSRC register is instead assigned back to its original value. So maybe the error never gets cleared? 

    But I changed the highlighted line to say "0" instead of errsrc_mask (according to ERRORSRC documentation, 0x0 is the reset value). I also tried to change it to "1", because the comment in the code screenshot below says "write one to clear." Ultimately, I discovered that the way the code is originally written DOES clear the register (ERRORSRC set to 0x0), but this is not anywhere in documentation -- perhaps it should be included. 

    Regardless, it seems like the below condition is perpetually true:

    if(nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))

    But I don't really understand what this function is doing, can you please explain? Under what circumstances will this function return false, and where in the code are those circumstances triggered?

    It looks like it is checking to see if the value inside the address p_reg + event is non-zero, and if it is, then this means there is an error. But "event" is always 0x02, but as can be seen from the below screenshot, the EVENTS_ERROR offset is 0x124: 

    Value of "event" from above fn:

      (this is after I set a breakpoint for this fn)

    Expected value of "event", per datasheet:

    Where the value for "event" gets assigned in SDK:

    Is this a bug in the SDK? How to fix it?

    ----------------------

    Lastly, is there a "proper" place in the code for me to change the INTEN to disable UART error interrupts?

    I see from the documentation for INTENCLR that I probably want to do something like, p_reg->INTENCLR  = (1 << 9), where 9 (id = G) is the offset for EVENTS_ERROR. But where/how can I practically do this? Do you have an API for this that I can call from my application, or do I have to add it somewhere in the SDK directly?

    Thanks!

  • nordev said:
    Thanks very much for this info. So does this mean that if/when I update my SDK to a newer version like SDK 17, some things that use the UART driver might break? 

    The drivers have been rewritten with the introduction of nrfx driver library in SDK 15.0.0. There is now a legacy API layer which interfaces with two separate UART/UARTE drivers. The legacy driver layer should have similar or identical API and configs as it has in older SDK. One thing you should note is that the nrf_serial library have been deprecated and removed in SDK 17.0.0. It is replaced by the superior libUARTE library. If you are considering migrating to this SDK version in the future, I would recommend you to consider doing it right away, before you spend too much time on the nrf_serial library.

    nordev said:
    I would like to be able to dig into the logic that causes the UART error IRQ to get triggered. The PPI diagram you provided is a bit too vague... can you point me toward a relevant part of the code? 

    The interrupts are enabled in the function interrupts_enable() in nrf_drv_uart.

    nordev said:

    I found this and suspected that this was part of the problem. It looks like instead of clearing the ERRORSRC register, the ERRORSRC register is instead assigned back to its original value. So maybe the error never gets cleared? 

    But I changed the highlighted line to say "0" instead of errsrc_mask (according to ERRORSRC documentation, 0x0 is the reset value). I also tried to change it to "1", because the comment in the code screenshot below says "write one to clear." Ultimately, I discovered that the way the code is originally written DOES clear the register (ERRORSRC set to 0x0), but this is not anywhere in documentation -- perhaps it should be included. 

    The bits in the register is cleared by writing one to the bit that you want to clear. Reading the register and writing back what was read will clear each bit in the register, just as it is descibed in the documentation.

    nordev said:

    if(nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))

    But I don't really understand what this function is doing, can you please explain? Under what circumstances will this function return false, and where in the code are those circumstances triggered?

    It looks like it is checking to see if the value inside the address p_reg + event is non-zero, and if it is, then this means there is an error. But "event" is always 0x02, but as can be seen from the below screenshot, the EVENTS_ERROR offset is 0x124: 

    As you correctly describe, this will check the event register to see if the event is generated. I think there is something wrong with how you read the value of 'event', or you are looking at a different variable. I read 0x124, which corresponds to the register documentation:

    nordev said:

    Lastly, is there a "proper" place in the code for me to change the INTEN to disable UART error interrupts?

    I see from the documentation for INTENCLR that I probably want to do something like, p_reg->INTENCLR  = (1 << 9), where 9 (id = G) is the offset for EVENTS_ERROR. But where/how can I practically do this? Do you have an API for this that I can call from my application, or do I have to add it somewhere in the SDK directly?

    You can remove the NRF_UARTE_INT_ERROR_MASK from nrf_uarte_int_enable() in interrupts_enable(), but I'm not sure if this will resolve your issues. Note that simply disabling the interrupt for this event will not prevent it from being handled in the interrupt handler. Since the event is checked in the interrupt handler, this event will be handled when another interrupt occurs.

Reply
  • nordev said:
    Thanks very much for this info. So does this mean that if/when I update my SDK to a newer version like SDK 17, some things that use the UART driver might break? 

    The drivers have been rewritten with the introduction of nrfx driver library in SDK 15.0.0. There is now a legacy API layer which interfaces with two separate UART/UARTE drivers. The legacy driver layer should have similar or identical API and configs as it has in older SDK. One thing you should note is that the nrf_serial library have been deprecated and removed in SDK 17.0.0. It is replaced by the superior libUARTE library. If you are considering migrating to this SDK version in the future, I would recommend you to consider doing it right away, before you spend too much time on the nrf_serial library.

    nordev said:
    I would like to be able to dig into the logic that causes the UART error IRQ to get triggered. The PPI diagram you provided is a bit too vague... can you point me toward a relevant part of the code? 

    The interrupts are enabled in the function interrupts_enable() in nrf_drv_uart.

    nordev said:

    I found this and suspected that this was part of the problem. It looks like instead of clearing the ERRORSRC register, the ERRORSRC register is instead assigned back to its original value. So maybe the error never gets cleared? 

    But I changed the highlighted line to say "0" instead of errsrc_mask (according to ERRORSRC documentation, 0x0 is the reset value). I also tried to change it to "1", because the comment in the code screenshot below says "write one to clear." Ultimately, I discovered that the way the code is originally written DOES clear the register (ERRORSRC set to 0x0), but this is not anywhere in documentation -- perhaps it should be included. 

    The bits in the register is cleared by writing one to the bit that you want to clear. Reading the register and writing back what was read will clear each bit in the register, just as it is descibed in the documentation.

    nordev said:

    if(nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))

    But I don't really understand what this function is doing, can you please explain? Under what circumstances will this function return false, and where in the code are those circumstances triggered?

    It looks like it is checking to see if the value inside the address p_reg + event is non-zero, and if it is, then this means there is an error. But "event" is always 0x02, but as can be seen from the below screenshot, the EVENTS_ERROR offset is 0x124: 

    As you correctly describe, this will check the event register to see if the event is generated. I think there is something wrong with how you read the value of 'event', or you are looking at a different variable. I read 0x124, which corresponds to the register documentation:

    nordev said:

    Lastly, is there a "proper" place in the code for me to change the INTEN to disable UART error interrupts?

    I see from the documentation for INTENCLR that I probably want to do something like, p_reg->INTENCLR  = (1 << 9), where 9 (id = G) is the offset for EVENTS_ERROR. But where/how can I practically do this? Do you have an API for this that I can call from my application, or do I have to add it somewhere in the SDK directly?

    You can remove the NRF_UARTE_INT_ERROR_MASK from nrf_uarte_int_enable() in interrupts_enable(), but I'm not sure if this will resolve your issues. Note that simply disabling the interrupt for this event will not prevent it from being handled in the interrupt handler. Since the event is checked in the interrupt handler, this event will be handled when another interrupt occurs.

Children
  • Thank you for all this info. So basically you are saying that the best move is to port over to SDK 17.0, and in this case I will need to rewrite all my serial handling code to use the libUARTE library instead? Is there a migration guide?

    Can you explain why the below is happening / is it a known bug that is solved with the new libUARTE library?

    I encounter a serial error, like a *single* frame or overrun error, often I don't stop getting NRF_SERIAL_EVENT_DRV_ERR for a long time
    If I DON'T have this uninit/reinit enabled, I can get serial errors on startup that the device NEVER recovers from
  • nordev said:
    So basically you are saying that the best move is to port over to SDK 17.0, and in this case I will need to rewrite all my serial handling code to use the libUARTE library instead? Is there a migration guide?

    Yes, that would be my recommendation, to solve the issues and not having to do this job in the future as well. There is no specific migration guide for libUARTE, but there are general migration guides for each major SDK version release:

    nordev said:
    Can you explain why the below is happening / is it a known bug that is solved with the new libUARTE library?

    This post may be relevant.

Related