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

Strange interaction between SysTick and Sleep

I am currently using SysTick to count milliseconds in my device. This works fine, however with it enabled I can't sleep the device as it wakes up instantly.

I have tried to disable it, however it does not work. The following code blinks the LED unless I remove the references to SysTick_Config

int main(void)
{
    SysTick_Config(SystemCoreClock / 1000);

    syshal_gpio_init(GPIO_LED_GREEN);

    for (;;)
    {
        syshal_gpio_set_output_high(GPIO_LED_GREEN);

        for (uint32_t i = 0; i < 1000000; ++i)
            __NOP();

        SysTick->CTRL = 0;

        nrf_pwr_mgmt_run();

        syshal_gpio_set_output_low(GPIO_LED_GREEN);

        for (uint32_t i = 0; i < 1000000; ++i)
            __NOP();

        SysTick_Config(SystemCoreClock / 1000);
    }
}


I'm almost certainly going to switch to using an RTC but it would be nice to understand why this does not work.

I am using the nRF52840 with no softdevice

Parents Reply Children
  • The SysTick is suspended when you're in sleep, so there must be something else that is waking up the CPU. Is that the only component you have in your application now?

    How often do you expect the systick vs. how often it actually occurs?

     

    Kind regards,

    Håkon

  • Hi Håkon,

    Thanks for the input. I'm starting to arrive at the same conclusion, something else is waking the device. I am not running any other peripherals or code on my device (no bootloader/softdevice)

    I've created a simplified, one file variant with a timer and still see the same issue:

    #include "nrfx_timer.h"
    #include "nrf_gpio.h"
    #include "nrf_pwr_mgmt.h"
    
    const nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(3);
    
    void timer_irq(nrf_timer_event_t event_type, void* p_context)
    {
        nrf_gpio_pin_toggle(NRF_GPIO_PIN_MAP(1, 10)); // Toggle the LED
    }
    
    void delay(void)
    {
        for (uint32_t i = 0; i < 1000000; ++i)
            __NOP();
    }
    
    int main(void)
    {
        nrf_pwr_mgmt_init();
    
        const nrfx_timer_config_t timer_config =
        {
            .frequency          = NRF_TIMER_FREQ_125kHz,
            .mode               = NRF_TIMER_MODE_TIMER,
            .bit_width          = NRF_TIMER_BIT_WIDTH_32,
            .interrupt_priority = 6,
            .p_context          = NULL
        };
    
        nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(1, 10));
    
        nrfx_timer_init(&timer_inst, &timer_config, timer_irq);
        nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, 125 * 100, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); // 100 ms
        nrfx_timer_enable(&timer_inst);
        
        for (;;)
        {
            delay();
    
            nrfx_timer_disable(&timer_inst); // Disable the timer
            
            nrf_pwr_mgmt_run(); // We would expect to sleep here indefinitely
    
            nrfx_timer_enable(&timer_inst);
    
            delay();
        }
    }

    I'm going to try a completely new build with the latest SDK as this must be an issue with the startup file or something.

    EDIT: I've tried it with the 15.3.0 SDK by copying the above code into the blinky project. I had to make a few tweaks to the linker and makefile to add in what was missing but this has resulted in the exact same issue

    EDIT2: I've tried both gcc-arm-none-eabi-7-2018-q2-update-win32 and gcc-arm-none-eabi-8-2018-q4-major-win32 and they both act the same. I've checked the assembly code for `main()` and this appears to be correct

  • Could you try rebooting your device with the debugger detached?

  • Hi Roy, unfortunately the results are the same

    In some cases I have seen the debugger mess with the sleep modes but this code seems to fail every time with or without it.

    Increasing the timer period to 1000ms from the current 100ms seems to resolve the issue but I can't imagine why. Unfortunately that's not a fix I can use for my application

    EDIT: I've tested the following interrupts and none appear to be being entered that shouldn't. The device is also definitely not resetting

    Reset_Handler <- Not sure as the startup file handles this one
    TIMER3_IRQHandler <- Does get entered
    
    // None of the below are entered
    NMI_Handler
    HardFault_Handler
    MemoryManagement_Handler
    BusFault_Handler
    UsageFault_Handler
    SVC_Handler
    DebugMon_Handler
    PendSV_Handler
    SysTick_Handler
    POWER_CLOCK_IRQHandler
    RADIO_IRQHandler
    UARTE0_UART0_IRQHandler
    SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler
    SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler
    NFCT_IRQHandler
    GPIOTE_IRQHandler
    SAADC_IRQHandler
    TIMER0_IRQHandler
    TIMER1_IRQHandler
    TIMER2_IRQHandler
    RTC0_IRQHandler
    TEMP_IRQHandler
    RNG_IRQHandler
    ECB_IRQHandler
    CCM_AAR_IRQHandler
    WDT_IRQHandler
    RTC1_IRQHandler
    QDEC_IRQHandler
    COMP_LPCOMP_IRQHandler
    SWI0_EGU0_IRQHandler
    SWI1_EGU1_IRQHandler
    SWI2_EGU2_IRQHandler
    SWI3_EGU3_IRQHandler
    SWI4_EGU4_IRQHandler
    SWI5_EGU5_IRQHandler
    TIMER4_IRQHandler
    PWM0_IRQHandler
    PDM_IRQHandler
    MWU_IRQHandler
    PWM1_IRQHandler
    PWM2_IRQHandler
    SPIM2_SPIS2_SPI2_IRQHandler
    RTC2_IRQHandler
    I2S_IRQHandler
    FPU_IRQHandler
    USBD_IRQHandler
    UARTE1_IRQHandler
    QSPI_IRQHandler
    CRYPTOCELL_IRQHandler
    PWM3_IRQHandler
    SPIM3_IRQHandler

    EDIT2: I've tried this on two of our custom boards and also on the PCA10059 Dongle

  • I have attached two variants that cause this behaviour. One is based on SysTick and the other on a Timer. It seems to be timing related as tweaking NOP_COUNT determines if the device sleeps properly or not

    #include "nrfx_timer.h"
    #include "nrf_gpio.h"
    
    #define NOP_COUNT (1000000) // Works if changed to 100000
    
    const nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(3);
    
    void timer_irq(nrf_timer_event_t event_type, void* p_context)
    {
    }
    
    int main(void)
    {
        const nrfx_timer_config_t timer_config =
        {
            .frequency          = NRF_TIMER_FREQ_125kHz,
            .mode               = NRF_TIMER_MODE_TIMER,
            .bit_width          = NRF_TIMER_BIT_WIDTH_32,
            .interrupt_priority = 6,
            .p_context          = NULL
        };
    
        nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(0, 11));
    
        nrfx_timer_init(&timer_inst, &timer_config, timer_irq);
        nrfx_timer_extended_compare(&timer_inst, NRF_TIMER_CC_CHANNEL0, 125 * 100, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); // 100 ms
        nrfx_timer_enable(&timer_inst);
        
        for (;;)
        {
            nrf_gpio_pin_toggle(NRF_GPIO_PIN_MAP(0, 11));
    
            for (uint32_t i = 0; i < NOP_COUNT; ++i)
                __NOP();
    
            nrfx_timer_disable(&timer_inst); // Disable the timer
            
            __WFE();
            // Clear the internal event register.
            __SEV();
            __WFE();
    
            nrfx_timer_enable(&timer_inst);
    
            nrf_gpio_pin_toggle(NRF_GPIO_PIN_MAP(0, 11));
    
            for (uint32_t i = 0; i < NOP_COUNT; ++i)
                __NOP();
        }
    }
    #include "nrf_gpio.h"
    
    #define NOP_COUNT (10000) // Works if changed to 1000
    
    void SysTick_Handler(void)
    {
    }
    
    int main(void)
    {
        nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(0, 11));
    
        // Setup systick
        SysTick->LOAD  = (uint32_t)((SystemCoreClock / 1000) - 1UL);     // set reload register
        NVIC_SetPriority(SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); // set Priority for Systick Interrupt
        SysTick->VAL   = 0UL;                                            // Load the SysTick Counter Value
    
        // Enable SysTick IRQ and SysTick Timer
        SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; // Works if this line is removed
    
        for (;;)
        {
            nrf_gpio_pin_toggle(NRF_GPIO_PIN_MAP(0, 11));
    
            for (uint32_t i = 0; i < NOP_COUNT; ++i)
                __NOP();
    
            //SysTick->CTRL &= ~ (SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk); // Irrelevant
    
            __WFE();
            // Clear the internal event register.
            __SEV();
            __WFE();
    
            //SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk; // Irrelevant
    
            nrf_gpio_pin_toggle(NRF_GPIO_PIN_MAP(0, 11));
    
            for (uint32_t i = 0; i < NOP_COUNT; ++i)
                __NOP();
        }
    }

Related