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

Hold Deadman switch pin during DFU enable

Hello,

This is a followup question regarding this old thread I started: https://devzone.nordicsemi.com/f/nordic-q-a/48295/hold-output-pin-during-bootloader-jump

IC: NRF52840 SDK: 15.3 IDE: SES

I have a battery powered device powered by a dead-man switch.  When the user presses the power on button, the NRF52840 holds this dead-man switch on to enable power to the whole system with a GPIO output.

I need to be able to boot into the DFU BLE bootloader without having this deadman output reset.  If I just call sd_nvic_SystemReset(); or NVIC_SystemReset();, I get a toggle on this pin and loose power. The pin drops low for about 2 ms, enough to disable the power switch.

I believe, per the data sheet, that all pins are reset to inputs on system reset.  This seems to be new on the NRF52840 as my old projects with the NRF52832 did not reset my output pins I reset the system.

I am trying to find a way to directly jump to the bootloader.  Im using the standard 'pca10056_ble_debug' example project using the GPRET method, only modified to use LF_SRC = 0.

I feel like I am close to getting the direct jump to work. I am able to put a breakpoint in the bootloader code and I hit the breakpoints at the bootloader reset vector with the correct 0xB1 value in the GPRET register.  However there seems to be some issue in the DFU bootloader to hang on nrf_dfu_transports_init(dfu_observer) when I directly jump to the DFU bootloader.  This only hangs when I call my jump function, it works fine when I just call NVIC_SystemReset.

My jump code is just taken from the bootloader code and I call nrf_bootloader_app_start(0xf1000)

/**@brief Function that sets the stack pointer and link register, and starts executing a particular
 * address.
 *
 * @param[in]  new_msp  The new value to set in the main stack pointer.
 * @param[in]  new_lr   The new value to set in the link register.
 * @param[in]  addr     The address to execute.
 */
__STATIC_INLINE void jump_to_addr(uint32_t new_msp, uint32_t new_lr, uint32_t addr)
{
    __ASM volatile("MSR MSP, %[arg]" : : [arg] "r"(new_msp));
    __ASM volatile("MOV LR,  %[arg]" : : [arg] "r"(new_lr) : "lr");
    __ASM volatile("BX       %[arg]" : : [arg] "r"(addr));
}

/**@brief Function for booting an app as if the chip was reset.
 *
 * @param[in]  vector_table_addr  The address of the app's vector table.
 */
__STATIC_INLINE void app_start(uint32_t vector_table_addr)
{
    const uint32_t current_isr_num = (__get_IPSR() & IPSR_ISR_Msk);
    const uint32_t new_msp =
        *((uint32_t *)(vector_table_addr)); // The app's Stack Pointer is found as the first word of
                                            // the vector table.
    const uint32_t reset_handler = *(
        (uint32_t *)(vector_table_addr + sizeof(uint32_t))); // The app's Reset Handler is found as
                                                             // the second word of the vector table.
    const uint32_t new_lr = 0xFFFFFFFF;

    __set_CONTROL(0x00000000);   // Set CONTROL to its reset value 0.
    __set_PRIMASK(0x00000000);   // Set PRIMASK to its reset value 0.
    __set_BASEPRI(0x00000000);   // Set BASEPRI to its reset value 0.
    __set_FAULTMASK(0x00000000); // Set FAULTMASK to its reset value 0.

    if (current_isr_num == 0)
    {
        // The CPU is in Thread mode (main context).
        jump_to_addr(new_msp, new_lr, reset_handler); // Jump directly to the App's Reset Handler.
    }
    else
    {
        // The CPU is in Handler mode (interrupt context).

        const uint32_t exception_stack[EXCEPTION_STACK_WORD_COUNT] = // To be copied onto the stack.
            {
                0x00000000,    // New value of R0. Cleared by setting to 0.
                0x00000000,    // New value of R1. Cleared by setting to 0.
                0x00000000,    // New value of R2. Cleared by setting to 0.
                0x00000000,    // New value of R3. Cleared by setting to 0.
                0x00000000,    // New value of R12. Cleared by setting to 0.
                0xFFFFFFFF,    // New value of LR. Cleared by setting to all 1s.
                reset_handler, // New value of PC. The CPU will continue by executing the App's
                               // Reset Handler.
                xPSR_T_Msk,    // New value of xPSR (Thumb mode set).
            };
        const uint32_t exception_sp = new_msp - sizeof(exception_stack);

        memcpy((uint32_t *)exception_sp, exception_stack,
            sizeof(exception_stack)); // 'Push' exception_stack onto the App's stack.

        jump_to_addr(exception_sp, new_lr,
            HANDLER_MODE_EXIT); // 'Jump' to the special value to exit handler mode. new_lr is
                                // superfluous here. exception_stack will be popped from the stack,
                                // so the resulting SP will be the new_msp. Execution will continue
                                // from the App's Reset Handler.
    }
}

void nrf_bootloader_app_start(uint32_t start_addr)
{
    NRF_LOG_DEBUG("Running nrf_bootloader_app_start with address: 0x%08x", start_addr);
    uint32_t err_code;

    // Disable and clear interrupts
    // Notice that this disables only 'external' interrupts (positive IRQn).
    NRF_LOG_DEBUG("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

//    err_code = nrf_dfu_mbr_irq_forward_address_set();
//    if (err_code != NRF_SUCCESS)
//    {
//        NRF_LOG_ERROR("Failed running nrf_dfu_mbr_irq_forward_address_set()");
//    }

    //NRF_LOG_FLUSH();
    app_start(start_addr);
}

Questions:

1.) Do I need to jump to the MBR instead of the bootloader vector table start address?  What MBR address would I jump to?

2.) Perhaps there is something else I need to clear or do before I jump to the bootloader? Are there any registers that might cause issues with the nrf_dfu_transports_init(dfu_observer) call?

See below for the pin toggle screenshot:

Parents Reply Children
Related