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

RTC to read SAADC with PPI but never SLEEP OFF

I am using NRF52832 for BLE application with softdevice 132 under SDK15.3. My application is supposed to sleep off after certain amount of time when there is no user activity and using ppi to read SAADC (pressure sensor) with 20 ms interval. So if SAADC input doesn't go over a predefined limit, it regards no user interaction and wait for 1 hour and sleep off. It did work well using SAADC and PPI with TIMER peripheral.

I upgraded my application to low power SAADC, which provided very good energy saving with good adc reading and good sleeping.

And I continued to change TIMER to RTC, which gave more energy saving but as side effect my device never falls a sleep off anymore. (but it reads SAADC well)

I am using RTC2 with two channels. Channel 0 for sleep off and channel 1 for SAADC capture interval with compare interrupt. And I noticed that the SAADC capture interval clears whole RTC2 timer and ends up disabling the sleep off time setup. I also read https://devzone.nordicsemi.com/f/nordic-q-a/23317/rtc-clear-ppi-after-compare/91668#91668 and https://devzone.nordicsemi.com/f/nordic-q-a/8772/rtc-nrf51-sdk-example-matches-compare0-only-once#post-id-49056 but they are not my case, I think.

I am a beginner in Nordic world. Please help me out with easy words. Thanks.

#define POWER_OF_PRESCALER                4                       //0..12
#define PRESCALER                         (1<<POWER_OF_PRESCALER)-1
#define RTC_FREQ                          32768/PRESCALER   

#define NRFX_RTC_MS_TO_TICKS(ms, rtc_freq_hz)    (((ms) * (rtc_freq_hz)) / 1000U) 
#define NRFX_RTC_SEC_TO_TICKS(sec, rtc_freq_hz)  ((sec) * (rtc_freq_hz)) 
#define NRFX_RTC_MIN_TO_TICKS(min, rtc_freq_hz)  ((min) * (rtc_freq_hz) * 60) 

#define SLEEP_OFF_TIMER_MINUTES           60 //minutes to sleep off when no user activity
#define SLEEP_OFF_TICKS                   NRFX_RTC_MIN_TO_TICKS(SLEEP_OFF_TIMER_MINUTES, RTC_FREQ)

#define PS_CAPTURE_INTERVAL_MS            20  //ms for pressure seonsor capture interval
#define PS_TICKS                          NRFX_RTC_MS_TO_TICKS(PS_CAPTURE_INTERVAL_MS, RTC_FREQ)

static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    uint32_t err_code;


    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        //nrf_gpio_pin_toggle(COMPARE_EVENT_OUTPUT);
        sleep_mode_enter();
    }

    if (int_type == NRFX_RTC_INT_COMPARE1) //for test ps interval only
    {
        //workaround for recurring COMPARE[x] interrupt is diabled in nrf_drv_rtc_init_handler
        err_code = nrf_drv_rtc_cc_set(&rtc, 1, PS_TICKS, true);
        APP_ERROR_CHECK(err_code); 

        nrf_gpio_pin_toggle(LED_3); //Test only

    }
}


static void rtc_config(void)
{
    uint32_t err_code;

    //Initialize RTC instance
    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;

    config.prescaler = PRESCALER;
    err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
    APP_ERROR_CHECK(err_code);

    //Enable tick event & interrupt - NO NEED!
    //nrf_drv_rtc_tick_enable(&rtc,true);


    //Set compare channel 0 to trigger interrupt for sleep off timer 
    err_code = nrf_drv_rtc_cc_set(&rtc, 0, SLEEP_OFF_TICKS , true);
    APP_ERROR_CHECK(err_code);

    //Set compare channel 1 to trigger interrupt for pressure sensor interval for ppi
    err_code = nrf_drv_rtc_cc_set(&rtc, 1, PS_TICKS, true);
    APP_ERROR_CHECK(err_code); 


}

void timer_with_ppi_init(void)
{
    ret_code_t err_code; // a variable to hold the error code

// Initialize the PPI (make sure its initialized only once in your code)
    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code); // check for errors

    rtc_config();

    //Enable on RTC instance
    nrf_drv_rtc_enable(&rtc);

// Save the address of compare event so that it can be connected to ppi module
    uint32_t rtc_compare_event_addr = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_1);

    uint32_t rtc_clear_task_addr = nrf_drv_rtc_task_address_get(&rtc, NRF_RTC_TASK_CLEAR);

// Save the task address to a variable so that it can be connected to ppi module for automatic triggering
    uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();

// Allocate the ppi channel by passing it the struct created at the start
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code); // check for errors
	
	
// attach the addresses to the allocated ppi channel so that its ready to trigger tasks on events
    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, rtc_compare_event_addr, saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code); // check for errors

    err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel, rtc_clear_task_addr);
    APP_ERROR_CHECK(err_code);

}

Parents
  • Hi,

    You are clearing the RTC by connecting the CLEAR task to the COMPARE_1 event in your ppi_init() function. If you remove the following lines, the timer will not be cleared and you should receive the COMPARE_0 event after 60 minutes:

    err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel, rtc_clear_task_addr);
    APP_ERROR_CHECK(err_code);

    Note that if you do not clear the timer, the SAADC sample task will not be triggered a second time until the RTC has overflowed, which may take a long time depending your PRESCALER value. You may have to use a second RTC for entering sleep, or use some other method to determine when to go to sleep. One options could be to count the SAADC sample compare events and enter system OFF when you have counted events corresponding to 60 minutes.

    Best regards,
    Jørgen

Reply
  • Hi,

    You are clearing the RTC by connecting the CLEAR task to the COMPARE_1 event in your ppi_init() function. If you remove the following lines, the timer will not be cleared and you should receive the COMPARE_0 event after 60 minutes:

    err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel, rtc_clear_task_addr);
    APP_ERROR_CHECK(err_code);

    Note that if you do not clear the timer, the SAADC sample task will not be triggered a second time until the RTC has overflowed, which may take a long time depending your PRESCALER value. You may have to use a second RTC for entering sleep, or use some other method to determine when to go to sleep. One options could be to count the SAADC sample compare events and enter system OFF when you have counted events corresponding to 60 minutes.

    Best regards,
    Jørgen

Children
  • Thanks for your reply. As I am using rtc0 for softdevice and rtc 1 for app timer I have no choice to use the counter way. It works well.

    Here is the final code!

    #define POWER_OF_PRESCALER                4                       //0..12
    #define PRESCALER                         (1<<POWER_OF_PRESCALER)-1
    #define RTC_FREQ                          32768/PRESCALER   
    
    #define NRFX_RTC_MS_TO_TICKS(ms, rtc_freq_hz)    (((ms) * (rtc_freq_hz)) / 1000U) 
    #define NRFX_RTC_SEC_TO_TICKS(sec, rtc_freq_hz)  ((sec) * (rtc_freq_hz)) 
    #define NRFX_RTC_MIN_TO_TICKS(min, rtc_freq_hz)  ((min) * (rtc_freq_hz) * 60) 
    
    #define SLEEP_OFF_TIMER_MINUTES           60 //minutes to sleep off when no user activity
    #define SLEEP_OFF_TICKS                   NRFX_RTC_MIN_TO_TICKS(SLEEP_OFF_TIMER_MINUTES, RTC_FREQ)
    
    #define PS_CAPTURE_INTERVAL_MS            20  //ms for pressure seonsor capture interval
    #define PS_TICKS                          NRFX_RTC_MS_TO_TICKS(PS_CAPTURE_INTERVAL_MS, RTC_FREQ)
    
    static uint32_t sleep_cnt = 0;
    
    /** @brief: Function for handling the RTC0 interrupts.
     * Triggered on TICK and COMPARE0 match.
     */
    static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        uint32_t err_code;
    
        if (int_type == NRFX_RTC_INT_COMPARE0) //for test ps interval only
        {
            //workaround for recurring COMPARE[x] interrupt is diabled in nrf_drv_rtc_init_handler
            err_code = nrf_drv_rtc_cc_set(&rtc, 0, PS_TICKS, true);
            APP_ERROR_CHECK(err_code); 
    
            nrf_gpio_pin_toggle(LED_3); //Test only
            sleep_cnt++;
            //printf("sleep count: %d\r\n", sleep_cnt);
            
        }
    
        if (sleep_cnt >= SLEEP_OFF_TICKS/PS_TICKS) 
        {
            nrf_gpio_pin_set(LED_3); //Test only
            sleep_mode_enter();
        }
    
    }
    
    
    /** @brief Function initialization and configuration of RTC driver instance.
     */
    static void rtc_config(void)
    {
        uint32_t err_code;
    
        //Initialize RTC instance
        nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
        config.prescaler = PRESCALER;
        err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
        APP_ERROR_CHECK(err_code);
    
        //Set compare channel 0 to trigger interrupt for pressure sensor interval for ppi
        err_code = nrf_drv_rtc_cc_set(&rtc, 0, PS_TICKS, true);
        APP_ERROR_CHECK(err_code); 
    
    }
    
    void timer_with_ppi_init(void)
    {
        ret_code_t err_code; 
    
        // Initialize the PPI 
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code); 
    
        rtc_config();
    
    
        //Enable on RTC instance
        nrf_drv_rtc_enable(&rtc);
    
        // Save the address of compare event so that it can be connected to ppi module
        uint32_t rtc_compare_event_addr = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_0);
        uint32_t rtc_clear_task_addr = nrf_drv_rtc_task_address_get(&rtc, NRF_RTC_TASK_CLEAR);
    
        // Save the task address to a variable so that it can be connected to ppi module for automatic triggering
        uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();
    
        // Allocate the ppi channel by passing it the struct created at the start
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code); // check for errors
    	
    	
        // attach the addresses to the allocated ppi channel so that its ready to trigger tasks on events
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, rtc_compare_event_addr, saadc_sample_task_addr);
        APP_ERROR_CHECK(err_code); // check for errors
    
        err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel, rtc_clear_task_addr);
        APP_ERROR_CHECK(err_code);
    
    }

  • Another option then is to create the sleep timer using app_timer.

Related