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

Stuck in Timer Interrupt Handler

I keep getting stuck in the TIMER1_IRQHandler function.

When I debug the code, I get to line 20 (exiting interrupt function scope) and then the assembly code hits a nop instruction. This then brings the debugger back up to line 17.

Why won't the code enter my if statement?

/** @file
* @brief Example template project.
* @defgroup nrf_templates_example Example Template
*
* NOTE: To get build to work, I right clicked nRF_micro-ecc folder and excluded it from build
*/

#include "main.h"

NRF_TIMER_Type* TIMER0 = (NRF_TIMER_Type*) TIMER0_BASE; // NRF_TIMER_Type has register structure
NRF_TIMER_Type* TIMER1 = (NRF_TIMER_Type*) TIMER1_BASE; // NRF_TIMER_Type has register structure
NRF_GPIOTE_Type* GPIOTE = (NRF_GPIOTE_Type*) GPIOTE_BASE; // NRF_GPIOTE_Type has register structure

int timer_flag = false;

void TIMER1_IRQHandler(void) {
    timer_flag = true;
    TIMER0 -> TASKS_STOP = 1;
    TIMER0 -> EVENTS_COMPARE[0] = 0;
}

void timer_init(hx711_mode_t number_of_pulses) {
    /*
      Setup timer for PWM
    */
    TIMER0 -> MODE &= 0; // Timer mode [DEFAULT]
    TIMER0 -> CC[0] |= 0x10; // Set value in CC[0] to 10
    TIMER0 -> PRESCALER |= 0x4; // Counts at 1 MHz [DEFAULT]
    TIMER0 -> SHORTS |= 1; // On compare[0] event, CLEAR timer
    
    /*
      Setup counter to stop PWM after number_of_pulses
    */
    TIMER1 -> MODE |= 0x2; // Timer set to low power counter mode
    TIMER1 -> CC[0] |= number_of_pulses; // Counter set to mode
    TIMER1 -> INTENSET |= (1 << 16); // initialise interrupt
    TIMER1 -> SHORTS |= (1 << 8); // On compare[0] event, STOP timer
    NVIC_SetPriority(TIMER1_IRQn, 0); // Priority 0 (lowest)
    NVIC_ClearPendingIRQ(TIMER1_IRQn);
    NVIC_EnableIRQ(TIMER1_IRQn); // Enables interrupt in NVIC interrupt controllers

    /*
      Use PPI for PWM generation
      Mode: task, Pin: PD_SCK, Polarity: Toggle, Initial pin value: LOW
    */
    GPIOTE-> CONFIG[0] = (0x3 << 0) |
                          (PD_SCK << 8) |
                          (0x3 << 16)|
                          (0 << 20);    

    NRF_PPI -> CH[0].EEP = (uint32_t) &TIMER0 -> EVENTS_COMPARE[0]; // Connect TIMER0 event to PPI channel 0
    NRF_PPI -> CH[0].TEP = (uint32_t) &GPIOTE-> TASKS_OUT[0]; // Connect toggle task to PPI channel 0
    NRF_PPI -> FORK[0].TEP = (uint32_t) &TIMER1-> TASKS_COUNT; // Connect toggle task to PPI channel 0
    NRF_PPI -> CHEN = (0x1 << 0); // Enable PPI channel 0
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{
    timer_init(CHANNEL_A_128); // Setup Timer
    TIMER0 -> TASKS_START = 1;

    while(true) {
        if (timer_flag) {
            nrf_delay_ms(500);
            TIMER1 -> TASKS_CLEAR = 1;
            TIMER0 -> TASKS_START = 1;
            timer_flag = false;
            
        }
    }

}
/** @} */

  • Apologies. Details: SDK v15.2.0. Windows 8.1. Uploading to nRF52 DK.

  • It wont't enter the if statement because timer_flag is not declared volatile.

    Without the volatile, the optimizer sees an if that will never be true.

    Note that the EVENTS_COMPARE[0] = 0; statement should not be the last statement in the IRQ handler due to the write buffer. This will cause the interrupt be still pending during the function return - causing tail chaining back into the handler function.

  • I have optimization level set to none. For good measure, I added in the keyword. Same problem.

    Thanks for the EVENT_COMPARE heads up.

    /** @file
    * @brief Example template project.
    * @defgroup nrf_templates_example Example Template
    *
    * NOTE: To get build to work, I right clicked nRF_micro-ecc folder and excluded it from build
    */
    
    #include "main.h"
    
    NRF_TIMER_Type* TIMER0 = (NRF_TIMER_Type*) TIMER0_BASE; // NRF_TIMER_Type has register structure
    NRF_TIMER_Type* TIMER1 = (NRF_TIMER_Type*) TIMER1_BASE; // NRF_TIMER_Type has register structure
    NRF_GPIOTE_Type* GPIOTE = (NRF_GPIOTE_Type*) GPIOTE_BASE; // NRF_GPIOTE_Type has register structure
    
    volatile bool timer_flag = false;
    
    void TIMER1_IRQHandler(void) {
        timer_flag = true;
        TIMER0 -> TASKS_STOP = 1;
    }
    
    void timer_init(hx711_mode_t number_of_pulses) {
        /*
          Setup timer for PWM
        */
        TIMER0 -> MODE &= 0; // Timer mode [DEFAULT]
        TIMER0 -> CC[0] |= 0x10; // Set value in CC[0] to 10
        TIMER0 -> PRESCALER |= 0x4; // Counts at 1 MHz [DEFAULT]
        TIMER0 -> SHORTS |= 1; // On compare[0] event, CLEAR timer
        
        /*
          Setup counter to stop PWM after number_of_pulses
        */
        TIMER1 -> MODE |= 0x2; // Timer set to low power counter mode
        TIMER1 -> CC[0] |= number_of_pulses; // Counter set to mode
        TIMER1 -> INTENSET |= (1 << 16); // initialise interrupt
        TIMER1 -> SHORTS |= (1 << 8); // On compare[0] event, STOP timer
        NVIC_SetPriority(TIMER1_IRQn, 0); // Priority 0 (lowest)
        NVIC_ClearPendingIRQ(TIMER1_IRQn);
        NVIC_EnableIRQ(TIMER1_IRQn); // Enables interrupt in NVIC interrupt controllers
    
        /*
          Use PPI for PWM generation
          Mode: task, Pin: PD_SCK, Polarity: Toggle, Initial pin value: LOW
        */
        GPIOTE-> CONFIG[0] = (0x3 << 0) |
                              (PD_SCK << 8) |
                              (0x3 << 16)|
                              (0 << 20);    
    
        NRF_PPI -> CH[0].EEP = (uint32_t) &TIMER0 -> EVENTS_COMPARE[0]; // Connect TIMER0 event to PPI channel 0
        NRF_PPI -> CH[0].TEP = (uint32_t) &GPIOTE-> TASKS_OUT[0]; // Connect toggle task to PPI channel 0
        NRF_PPI -> FORK[0].TEP = (uint32_t) &TIMER1-> TASKS_COUNT; // Connect toggle task to PPI channel 0
        NRF_PPI -> CHEN = (0x1 << 0); // Enable PPI channel 0
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        timer_init(CHANNEL_A_128); // Setup Timer
        TIMER0 -> TASKS_START = 1;
    
        while(true) {
            if (timer_flag) {
                TIMER0 -> EVENTS_COMPARE[0] = 0;
                nrf_delay_ms(500);
                TIMER1 -> TASKS_CLEAR = 1;
                TIMER0 -> TASKS_START = 1;
                timer_flag = false;
                
            }
        }
    
    }
    /** @} */
    

  • Doing it this way wont work either, because you are not clearing the interupt flag for the TIMER1 event when executing the TIMER1_IRQHandler.  Your first attempt was correct except for the fact that you didn't force write back of EVENTS_COMPARE[0] = 0. You can do that by reading it to a dummy variable like this

    void TIMER1_IRQHandler(void) {
        timer_flag = true;
        TIMER0 -> TASKS_STOP = 1;
    	
    	
    	volatile uint32_t dummy;
    	if (NRF_TIMER1->EVENTS_COMPARE[0] == 1)
    	{
    		NRF_TIMER1->EVENTS_COMPARE[0] = 0;
    
    		// Read back event register so ensure we have cleared it before exiting IRQ handler.
    		dummy = NRF_TIMER1->EVENTS_COMPARE[0];
    		dummy; // to get rid of set but not used warning
    	}
    }

    Hope this answers your question.

Related