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

GPIO interrupts not working with bootloader

Hello everyone. I am having problems using the interrups when a bootloader is programmed on the chip. My test program works fine when there is no bootloader. With the bootloader it never jumps to the interrupt handler. I am using the nrf51 and the s110 softdevice. I do the following in the bootloader to jump to the application.

sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, };
err_code = sd_mbr_command(&com);
APP_ERROR_CHECK(err_code);
err_code = sd_softdevice_vector_table_base_set(APP_START_ADDRESS);
APP_ERROR_CHECK(err_code);

Here is the code from my app. It basically blinks a led until a button is pressed and in the interrupt the chip is reset.

extern "C"{
#include "nrf_delay.h"
}

#include <stdbool.h>
#include <stdint.h>
#include "nrf_gpio.h"
#include "nrf_sdm.h"
#include "nrf_mbr.h"

extern "C"{
void GPIOTE_IRQHandler(void) // Interrupt HAndler
  {
      if(NRF_GPIOTE->EVENTS_IN[0] != 0)
      {
          //sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, };
          NRF_GPIOTE->EVENTS_IN[0] = 0;
          NRF_POWER->GPREGRET = 0x80;
          NVIC_SystemReset();
          //sd_softdevice_vector_table_base_set(0x0003C000);
          //sd_mbr_command(&com);
      }
  }
}

void config_CTS_Reset(void){
  NRF_GPIOTE->CONFIG[0] = (uint32_t)1 | // Event mode on IN[0]
                          ((uint32_t)8<<8) | // Pin 10 selected for Event
                          ((uint32_t)1<<16) ; // Generate event when rising edge on pin
  NRF_GPIOTE->INTENSET = (uint32_t)1; //Enable interrupt on IN[0]
  NVIC_EnableIRQ(GPIOTE_IRQn);
}

int main(void)
{
  //sd_softdevice_vector_table_base_set(0x0);

  NRF_GPIO -> PIN_CNF[21] =  ( (uint32_t)1 |    // Set as Output
                             ( (uint32_t)1 << 1) | // Disconnect input buffer
                             ( (uint32_t)0 << 2) |  //  No Pull
                             ( (uint32_t)0 << 8)); // Drive S0S1
  config_CTS_Reset();

  for(;;){
      NRF_GPIO->OUTCLR = (uint32_t)1 << 21;
      nrf_delay_ms(300);
      NRF_GPIO->OUTSET = (uint32_t)1 << 21;
      nrf_delay_ms(300);
  }

}
  • I am using version 10 of the SDK and version 8.0 of the softdevice

  • You have not configured the input pin as an input pin with Sense enabled.

    void config_CTS_Reset(void){
        NRF_GPIOTE->CONFIG[0] = (GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos) |       // Event mode on IN[0]
                                (0x0A << GPIOTE_CONFIG_PSEL_Pos)  |  // Pin 10 selected for Event
                                (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos) ;   // Generate event when rising edge on pin
        
    
        NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;   //Enable interrupt on IN[0]
        NVIC_EnableIRQ(GPIOTE_IRQn);
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        //sd_softdevice_vector_table_base_set(0x0);
    
        NRF_GPIO -> PIN_CNF[21] =  ( (uint32_t)1 |    // Set as Output
                                 ( (uint32_t)1 << 1) | // Disconnect input buffer
                                 ( (uint32_t)0 << 2) |  //  No Pull
                                 ( (uint32_t)0 << 8)); // Drive S0S1
        
        NRF_GPIO -> PIN_CNF[10] =  ( GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |    // Set as INPUT
                                    (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)| // Connect input buffer
                                    (GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos)|  //  No Pull
                                    (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |  // Drive S0S1
                                    (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos); //Sense when input is HIGH
        
        config_CTS_Reset();
    
        for(;;){
            NRF_GPIO->OUTCLR = (uint32_t)1 << 21;
            nrf_delay_ms(300);
            NRF_GPIO->OUTSET = (uint32_t)1 << 21;
            nrf_delay_ms(300);
        }
    }
    
  • The problem was not in the app, the problem was in the bootloader code that jumps to the app. I was not disabling the interrupts in the bootloader and not using the bootloader_util_reset to jump to the app.

    sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, };
    err_code = sd_mbr_command(&com);
    interrupts_disable();
    err_code = sd_softdevice_vector_table_base_set(APP_START_ADDRESS);
    bootloader_util_reset(APP_START_ADDRESS);
    

    The following code is the functions interrupts_disable() and bootloader_util_reset(), which I copied from the dfu serial example in the sdk

    static inline void bootloader_util_reset(uint32_t start_addr)
    {
        __asm volatile(
            "ldr   r0, [%0]\t\n"            // Get App initial MSP for bootloader.
            "msr   msp, r0\t\n"             // Set the main stack pointer to the applications MSP.
            "ldr   r0, [%0, #0x04]\t\n"     // Load Reset handler into R0.
    
            "movs  r4, #0xFF\t\n"           // Move ones to R4.
            "sxtb  r4, r4\t\n"              // Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF.
    
            "mrs   r5, IPSR\t\n"            // Load IPSR to R5 to check for handler or thread mode.
            "cmp   r5, #0x00\t\n"           // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
            "bne   isr_abort\t\n"           // If not zero we need to exit current ISR and jump to reset handler of bootloader.
    
            "mov   lr, r4\t\n"              // Clear the link register and set to ones to ensure no return.
            "bx    r0\t\n"                  // Branch to reset handler of bootloader.
    
            "isr_abort:  \t\n"
    
            "mov   r5, r4\t\n"              // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application.
            "mov   r6, r0\t\n"              // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR.
            "movs  r7, #0x21\t\n"           // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21.
            "rev   r7, r7\t\n"              // Reverse byte order to put 0x21 as MSB.
            "push  {r4-r7}\t\n"             // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
    
            "movs  r4, #0x00\t\n"           // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
            "movs  r5, #0x00\t\n"           // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
            "movs  r6, #0x00\t\n"           // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
            "movs  r7, #0x00\t\n"           // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
            "push  {r4-r7}\t\n"             // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
    
            "movs  r0, #0xF9\t\n"           // Move the execution return command into register, 0xFFFFFFF9.
            "sxtb  r0, r0\t\n"              // Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9.
            "bx    r0\t\n"                  // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
            ".align\t\n"
            :: "r" (start_addr)             // Argument list for the gcc assembly. start_addr is %0.
            :  "r0", "r4", "r5", "r6","r7" // List of register maintained manually.
        );
    }
    
    /**@brief Function for disabling all interrupts before jumping from bootloader to application.*/
    static void interrupts_disable(void)
    {
        uint32_t interrupt_setting_mask;
        uint32_t irq = 0; // We start from first interrupt, i.e. interrupt 0.
    
        // Fetch the current interrupt settings.
        interrupt_setting_mask = NVIC->ISER[0];
    
        for (; irq < 32; irq++)
        {
            if (interrupt_setting_mask & (0x01 << irq))
            {
                // The interrupt was enabled, and hence disable it.
                NVIC_DisableIRQ((IRQn_Type)irq);
            }
        }
    }
    

    After adding this code to the bootloader, the interrupts worked in the app.

  • With this code I had the same problem. It works without the bootloader, but when the bootloader is on the chip it doesn't work.

Related