ARM Cortex-M0+ Watchdog Timer Interrupt Handling Failure

The issue at hand involves the Watchdog Timer (WDT) interrupt handler (WDT_IRQHandler) not being executed on an nRF51 microcontroller, which is based on the ARM Cortex-M0+ architecture. The user has configured the WDT to trigger an interrupt after a timeout period, but the interrupt handler is not being invoked. This results in the system failing to respond to the WDT interrupt, which could lead to unintended system resets or missed critical events.

The WDT is a crucial peripheral in embedded systems, designed to reset the system if the software becomes unresponsive. When the WDT timer expires, it should trigger an interrupt, allowing the system to handle the event gracefully (e.g., by reloading the timer or performing recovery actions). However, in this case, the interrupt handler is not being called, indicating a misconfiguration or an issue in the interrupt handling mechanism.

The nRF51 microcontroller uses the ARM Cortex-M0+ core, which has a specific interrupt handling architecture. The Nested Vectored Interrupt Controller (NVIC) is responsible for managing interrupts, including prioritization and enabling/disabling interrupts. For the WDT interrupt to work correctly, several conditions must be met:

  1. The WDT peripheral must be properly configured to generate interrupts.
  2. The NVIC must be configured to enable the WDT interrupt.
  3. The interrupt handler must be correctly defined and linked in the vector table.
  4. The system must not be blocking interrupts globally or at a higher priority level.

The failure of the WDT_IRQHandler to execute suggests that one or more of these conditions are not being met. The following sections will explore the possible causes and provide detailed troubleshooting steps to resolve the issue.


Misconfigured WDT Peripheral and NVIC Settings

The first area to investigate is the configuration of the WDT peripheral and the NVIC. The WDT peripheral on the nRF51 has several configuration registers that control its behavior, including the timer value, interrupt enable flags, and reload registers. If any of these registers are misconfigured, the WDT may not generate the expected interrupt.

WDT Configuration Registers

The WDT configuration is controlled by the following key registers:

  • NRF_WDT->CONFIG: Configures the WDT behavior, such as whether it should run in sleep mode or halt when the CPU is stopped by the debugger.
  • NRF_WDT->CRV: Sets the timeout value for the WDT.
  • NRF_WDT->RREN: Enables the reload registers, which are used to prevent the WDT from resetting the system.
  • NRF_WDT->INTENSET: Enables the WDT interrupt.

In the provided code, the WDT is configured as follows:

NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos);
NRF_WDT->CRV = 3 * 32768; // 3-second timeout
NRF_WDT->RREN |= WDT_RREN_RR0_Msk; // Enable reload register 0
NRF_WDT->INTENSET = WDT_INTENSET_TIMEOUT_Msk; // Enable WDT interrupt
NRF_WDT->TASKS_START = 1; // Start the WDT

This configuration appears correct at first glance, but there are several potential pitfalls:

  1. Incorrect Timeout Value: The timeout value (NRF_WDT->CRV) is set to 3 * 32768, which corresponds to a 3-second timeout assuming a 32.768 kHz clock. However, if the clock source is not configured correctly, the actual timeout may differ.
  2. Missing Reload Register Configuration: The reload register (NRF_WDT->RR[0]) must be periodically reloaded to prevent the WDT from resetting the system. If this is not done, the WDT will reset the system instead of triggering an interrupt.
  3. Interrupt Priority: The WDT interrupt priority must be set appropriately in the NVIC. If the priority is too low or conflicts with other interrupts, the WDT interrupt may be blocked.

NVIC Configuration

The NVIC is responsible for enabling and prioritizing interrupts. The following code enables the WDT interrupt in the NVIC:

NVIC_EnableIRQ(WDT_IRQn);

However, this alone is not sufficient. The interrupt priority must also be set using NVIC_SetPriority. If the priority is not set, the WDT interrupt may be blocked by higher-priority interrupts or may not be recognized at all.

Common Misconfigurations

  • Incorrect Clock Source: The WDT relies on the low-frequency clock (LFCLK) for its operation. If the LFCLK is not enabled or configured correctly, the WDT will not function as expected.
  • Global Interrupts Disabled: If global interrupts are disabled (e.g., via the __disable_irq() function), no interrupts will be processed, including the WDT interrupt.
  • Vector Table Misalignment: The WDT_IRQHandler must be correctly defined in the vector table. If the vector table is misconfigured or relocated (e.g., using the NO_VTOR_CONFIG preprocessor definition), the interrupt handler may not be linked correctly.

Debugging and Resolving WDT Interrupt Issues

To resolve the issue of the WDT_IRQHandler not executing, follow these detailed troubleshooting steps:

Step 1: Verify WDT Configuration

  1. Check the Clock Source: Ensure that the LFCLK is enabled and configured correctly. The LFCLK is typically sourced from an external 32.768 kHz crystal or an internal RC oscillator. Verify the clock configuration in the nrf_drv_clock module and ensure that the LFCLK is running.
  2. Verify Timeout Value: Confirm that the timeout value (NRF_WDT->CRV) is appropriate for the application. Use a debugger to inspect the value of NRF_WDT->CRV and ensure it matches the expected timeout period.
  3. Enable Reload Register: Ensure that the reload register (NRF_WDT->RR[0]) is periodically reloaded to prevent the WDT from resetting the system. Add a reload operation in the main loop or another periodic task:
    NRF_WDT->RR[0] = WDT_RR_RR_Reload;
    

Step 2: Verify NVIC Configuration

  1. Set Interrupt Priority: Use NVIC_SetPriority to set the priority of the WDT interrupt. Ensure that the priority is higher than any other interrupts that may block the WDT interrupt:
    NVIC_SetPriority(WDT_IRQn, 1); // Set priority to 1 (higher priority)
    
  2. Enable the Interrupt: Ensure that the WDT interrupt is enabled in the NVIC:
    NVIC_EnableIRQ(WDT_IRQn);
    

Step 3: Verify Interrupt Handler and Vector Table

  1. Check Vector Table Alignment: Ensure that the vector table is correctly aligned and that the WDT_IRQHandler is defined in the correct position. If the vector table is relocated (e.g., using the NO_VTOR_CONFIG preprocessor definition), verify that the new vector table is correctly populated.
  2. Define the Interrupt Handler: Ensure that the WDT_IRQHandler is correctly defined and linked in the vector table. The handler should be declared as follows:
    void WDT_IRQHandler(void) {
        // Handle the WDT interrupt
        nrf_drv_gpiote_out_toggle(PIN_WDT_IRQ_HANDLER);
        printf("WDT Interrupt Triggered\n");
        NRF_WDT->EVENTS_TIMEOUT = 0; // Clear the timeout event
    }
    

Step 4: Debugging with a Breakpoint

  1. Set a Breakpoint in the Handler: Use a debugger to set a breakpoint inside the WDT_IRQHandler. If the breakpoint is not hit, it indicates that the interrupt is not being triggered.
  2. Inspect NVIC Registers: Use the debugger to inspect the NVIC registers, including the Interrupt Set-Enable Register (ISER) and the Interrupt Priority Register (IPR). Verify that the WDT interrupt is enabled and has the correct priority.

Step 5: Check for Global Interrupt Blocking

  1. Ensure Global Interrupts are Enabled: Verify that global interrupts are not being disabled elsewhere in the code. Use the __enable_irq() function to ensure that interrupts are enabled:
    __enable_irq();
    
  2. Inspect Critical Sections: Check for any critical sections in the code where interrupts are disabled. Ensure that these sections are as short as possible and do not block the WDT interrupt.

Step 6: Use Debugging Tools

  1. Use a Logic Analyzer: If available, use a logic analyzer to monitor the WDT interrupt signal. This can help verify whether the interrupt is being generated by the WDT peripheral.
  2. Enable Debug Logging: Add debug logging to the code to trace the execution flow and identify where the issue occurs. For example, log the state of the WDT registers and NVIC registers before and after enabling the WDT interrupt.

Implementing Correct WDT and NVIC Configuration

To ensure that the WDT interrupt is handled correctly, implement the following configuration:

WDT Configuration

void wdt_init(void) {
    // Configure WDT to pause in debug mode and run in sleep mode
    NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos);
    
    // Set the timeout value to 3 seconds (assuming 32.768 kHz clock)
    NRF_WDT->CRV = 3 * 32768;
    
    // Enable reload register 0
    NRF_WDT->RREN |= WDT_RREN_RR0_Msk;
    
    // Enable the WDT interrupt
    NRF_WDT->INTENSET = WDT_INTENSET_TIMEOUT_Msk;
    
    // Start the WDT
    NRF_WDT->TASKS_START = 1;
}

NVIC Configuration

void nvic_init(void) {
    // Set WDT interrupt priority to 1 (higher priority)
    NVIC_SetPriority(WDT_IRQn, 1);
    
    // Enable the WDT interrupt in the NVIC
    NVIC_EnableIRQ(WDT_IRQn);
}

Interrupt Handler

void WDT_IRQHandler(void) {
    // Toggle the LED to indicate the interrupt
    nrf_drv_gpiote_out_toggle(PIN_WDT_IRQ_HANDLER);
    
    // Print a debug message
    printf("WDT Interrupt Triggered\n");
    
    // Clear the timeout event
    NRF_WDT->EVENTS_TIMEOUT = 0;
}

Main Function

int main(void) {
    // Initialize GPIO and WDT
    gpio_init();
    wdt_init();
    nvic_init();
    
    // Main loop
    while (true) {
        // Reload the WDT to prevent a reset
        NRF_WDT->RR[0] = WDT_RR_RR_Reload;
        
        // Enter low-power mode
        __WFE();
    }
}

By following these steps and ensuring that the WDT and NVIC are correctly configured, the WDT_IRQHandler should execute as expected when the WDT timer expires. If the issue persists, further debugging with a logic analyzer or additional logging may be necessary to identify the root cause.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *