Adding libuarte to DFU bootloader causes inactivity timer to not trigger

Hello,

I'm a bit stumped on this one. We are in the process of switching over to libuarte for all of our UART communication on our main app, which has been working fine.

However, our bootloader still uses standard UART. When booting from the bootloader to the application, the bootloader will send data to the UART but then the application will not. I don't know why this is, but I figured I would just add libuarte to the bootloader as well.

This fixed the problem but caused another. Now, the DFU inactivity timer does not trigger in the bootloader. I tried playing around with the libuarte settings but I can't figure out what is causing it. I don't see any timer conflicts, and we aren't using the RTC for libuarte. However, there are a ton of settings that needed to be added to the sdk_config and to the makefile to get libuarte to work, so I'm sure there must be some incompatibility there that I'm missing. 

Any help would be appreciated. Thank you!

libuarte define:

/* Open Debug UART device. */
NRF_LIBUARTE_ASYNC_DEFINE(debug_uart, 1, 3, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 4, 255, 3);
  • Hello,

    My guess is that when you went with libuarte in your application, there was a conflict, because the UART was already initialized in your bootloader. Did you see this all the time, or only when the UART was actually being used by the bootloader (you performed a DFU)?

    Unless you are too far down the rabbit hole, I would suggest looking into what happens in the application, why the libuarte is not working after adding the bootloader. 

    The libuarte is quite resource heavy. It uses a bunch of timers and RTCs. It looks like you are using UARTE instance 1, TIMER3, no RTC and TIMER4 for your LIBUARTE. 

    It also looks like the inactivity timer is an RTC. RTC2 on the nRF52840, if I am not mistaken. I see that all of the inactivity timer are void functions, so they don't return anything. Did you try to debug the bootloader after it has started, and look at the RTC registers? What IDE are you using? If you are only using armgcc, can you try to debug it using Ozone, so that you can see the peripheral registers?

    Or if there is some way for me to reproduce it, I can have a look.

    Best regards,

    Edvin

  • Hi Edvin,

    Thank you for the tips! I followed your advice and reverted the bootloader back to standard UART, and instead have been troubleshooting why the application no longer prints after libuarte activation. Here's what I've found:

    • The application gets stuck during/after NVIC_EnableIRQ(irqn); in the nrf_libuarte_drv_init() function in nrf_libuarte_drv.c.
    • Regarding the above, I assume that it is immediately jumping to an interrupt handler after it is enabled. This does NOT occur when running without the bootloader. Is it possible that it is still trying to jump to whatever handler was registered in the bootloader?
    • In the bootloader, I am calling app_uart_close() before nrf_bootloader_app_start() which looks like it is supposed to deactivate interrupts. I assume that is not happening completely or correctly:
        // Disable and clear interrupts
        // Notice that this disables only 'external' interrupts (positive IRQn).
        NRF_LOG_INFO("Disabling interrupts. NVIC->ICER[0]: 0x%x", NVIC->ICER[0]);
    
        NVIC->ICER[0]=0xFFFFFFFF;
        NVIC->ICPR[0]=0xFFFFFFFF;
    #if defined(__NRF_NVIC_ISER_COUNT) && __NRF_NVIC_ISER_COUNT == 2
        NVIC->ICER[1]=0xFFFFFFFF;
        NVIC->ICPR[1]=0xFFFFFFFF;
    #endif
    I have tried running the application with Ozone, but am having some trouble finding exactly where it's hanging. The call stack shows the following indefinitely:
    In summary, it looks like it has something to do with the UART interrupt not being reset properly. Any insight on how to fix this would be greatly appreciate. Let me know if there is any other information I can provide.
  • Hello,

    Do you actually use the UART in the bootloader when you don't perform a DFU? 

    And are you using the same GPIOs for UART in your bootloader and application? 

    Can you check that you are not using the same GPIO as the bootloader uses to enter DFU mode (button 4) for libuarte?

    I ran a quick test using only the default UART bootloader and the libuarte example from SDK17.1.0. After moving the start address (flash) and ram start according to include the MBR, it is capable of running the libuarte after the uart bootloader. Note that as long as the bootloader doesn't enter DFU mode, the UART isn't enabled by the bootloader.

    Do you use the UART for logging in your bootloader?

    BR,
    Edvin

  • Hi Edvin,

    Yes, the UART is used in the bootloader for debugging (via fprintfs, not nrf_log) even if DFU mode is not entered, and the same pins are used for the bootloader and application UARTs.

    The bootloader is slightly modified to be buttonless - a system reset causes the bootloader to enter BLE DFU mode for 15 seconds, after which it will pitch to the application unless a DFU is triggered.

    Bootloader UART config:

    int init_uart(void)
    {
        uint32_t err_code;
    
        const app_uart_comm_params_t comm_params =
          {
              NULL,
              TX_PIN_NUMBER,
              RTS_PIN_NUMBER,
              CTS_PIN_NUMBER,
              APP_UART_FLOW_CONTROL_DISABLED,
              false,
              NRF_UART_BAUDRATE_115200
          };
    
        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);
    
        return err_code;
    }

    Application LIBUARTE config:

    int init_uart(void)
    {
        nrfx_err_t err_code;
    
        nrf_libuarte_async_config_t 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 = 50000,
            .int_prio   = APP_IRQ_PRIORITY_LOW_MID
        };
        err_code = nrf_libuarte_async_init(&debug_uart, &config, debug_uart_event_handler, (void *)&debug_uart);
        nrf_libuarte_async_enable(&debug_uart);
        return err_code;
    }

  • In that case, I think this is what's causing the issue. The bootloader initializes the UART. The nRF doesn't have a reset between the bootloader and the application, so it is trying to use the same GPIOs again, but they are taken.

    I assume that if you try to not enable the UART in the bootloader, you will see that the libuarte in your application will work just fine. If that is the case, then it is simply a manner of disabling the UART properly, and making sure that the gpios are properly reset to their default configuration (disconnected input). You can see from the link that the registers default value is 0x00000002, which means disconnected input. 

    If you are using init_uart() in your bootloader, you can try to call app_uart_close() before starting the application. This will call pins_to_default() which will set all the GPIOs in the default state. 

    Just make sure that you don't log anything after disabling the UART, before starting the application.

    Best regards,

    Edvin

Related