Hello, I'm making a timekeeping library which uses the real time counter in the nRF9160. This works well. However, I want to update the time when a certain amount of time has passed. To do this I wanted to use the Real-Time Counters counter compare interrupt. My problem is that this interrupt does run, but trying to add work to a work queue or giving a semaphore to another thread does not result in the intended behavior. The thread or the work does not execute unless a k_sleep is introduced somewhere in the program, be that in the rtc handler or in an infinite loop in the main function.
So my question is the following: Why does the work or thread not get executed when there is no sleep command in either of the two places?
btw, I'm using Zephyr OS build v3.2.99-ncs1
I have placed a small program in the attachments which shows my problem. This code blinks an LED in the rtc handler, thus showing that the handler is called at the correct interval.
#include <nrf9160.h> #include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/logging/log.h> #include "date_time_SNTP.h" LOG_MODULE_REGISTER(main); // Setting this to 0 breaks the program // - if ENABLE_K_SLEEP_IN_RTC_HANDLER is also 0, the work or thread is never called // - if ENABLE_K_SLEEP_IN_RTC_HANDLER is 1, it works as expected (the work or thread is called once every 2 seconds) #define USE_WHILE_LOOP_IN_MAIN 0 void main(void) { int err; err = date_time_sntp_init(); if (err != 0){ LOG_ERR("date_time_sntp_init failed, error code: %d", err); return; } #if USE_WHILE_LOOP_IN_MAIN while (1) { k_sleep(K_SECONDS(3)); } #endif }
#include <zephyr/kernel.h> #include <zephyr/logging/log.h> #include <nrfx_rtc.h> #include "date_time_SNTP.h" #include <nrfx_gpiote.h> #define LED_0 2 #define LED_1 3 // Setting this to 0 breaks the program // - if USE_WHILE_LOOP_IN_MAIN is also 0 the work or thread is never called // - if USE_WHILE_LOOP_IN_MAIN is 1 the work or thread is called but only at the frequency of the while loop // The strange this is that the led is still blinking at the correct frequency, which means that the rtc handler is still being called #define ENABLE_K_SLEEP_IN_RTC_HANDLER 1 // This setting doesn't make a difference in the outcome #define USE_WORK_QUEUE 0 // else use thread #define RTC_FREQ 512 #define COMPARE_COUNTERTIME_SEC 2 #define COMPARE_COUNTER_TICKS RTC_FREQ*COMPARE_COUNTERTIME_SEC LOG_MODULE_REGISTER(rtc_test, LOG_LEVEL_INF); const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(0); static volatile uint32_t previous_counter_compare = COMPARE_COUNTER_TICKS; int set_rtc_cc(){ previous_counter_compare += COMPARE_COUNTER_TICKS; if(previous_counter_compare > NRF_RTC_COUNTER_MAX){ // overflow previous_counter_compare -= NRF_RTC_COUNTER_MAX; } int err = nrfx_rtc_cc_set(&rtc, 0, previous_counter_compare, true); if(err != NRFX_SUCCESS){ LOG_ERR("error in cc set: %d", err); return err; } return 0; } #if USE_WORK_QUEUE static void update_time_work_handler(struct k_work *work){ LOG_INF("entering work handler"); nrf_gpio_pin_toggle(LED_0); } K_WORK_DEFINE(update_time_work, update_time_work_handler); #else static K_SEM_DEFINE(time_update_pending, 0, 1); static void update_time_thread_handler(void){ while(1){ LOG_INF("entering thread handler"); k_sem_take(&time_update_pending, K_FOREVER); LOG_INF("sem time_update_pending taken"); nrf_gpio_pin_toggle(LED_0); } } K_THREAD_DEFINE(date_time_thread, 1024, update_time_thread_handler, NULL, NULL, NULL, K_PRIO_COOP(7), 0, 0); #endif // USE_WORK_QUEUE static void rtc_handler(nrfx_rtc_int_type_t int_type){ if (int_type == NRFX_RTC_INT_COMPARE0) { set_rtc_cc(); nrf_gpio_pin_toggle(LED_1); #if USE_WORK_QUEUE k_work_submit(&update_time_work); #else k_sem_give(&time_update_pending); #endif #if ENABLE_K_SLEEP_IN_RTC_HANDLER k_sleep(K_MSEC(1)); #endif } } static int rtc_config(void){ nrfx_err_t err_code; LOG_INF("entering rtc_config"); nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG; LOG_INF("prescaler: %u, interupt_priority: %u, tick_latency: %u, reliable: %s", config.prescaler, config.interrupt_priority, config.tick_latency, config.reliable ? "true" : "false"); k_sleep(K_MSEC(1)); config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQ); err_code = nrfx_rtc_init(&rtc, &config, rtc_handler); if (err_code != NRFX_SUCCESS) { if (err_code == NRFX_ERROR_INVALID_STATE){ return -ENOTSUP; } return -ENOMSG; } nrfx_rtc_counter_clear(&rtc); err_code = nrfx_rtc_cc_set(&rtc, 0, COMPARE_COUNTER_TICKS, true); if (err_code != NRFX_SUCCESS) { if (err_code == NRFX_ERROR_TIMEOUT){ return -ETIMEDOUT; } return -ENOMSG; } nrfx_rtc_enable(&rtc); return 0; } static void manual_isr_setup(){ IRQ_DIRECT_CONNECT(RTC0_IRQn, 0, nrfx_rtc_0_irq_handler, 0); irq_enable(RTC0_IRQn); } int date_time_sntp_init(){ nrf_gpio_cfg_output(LED_0); nrf_gpio_cfg_output(LED_1); int ret = rtc_config(); if (ret != 0){ LOG_ERR("rtc_config failed"); return ret; } manual_isr_setup(); return 0; }
# # Copyright (c) 2020 Nordic Semiconductor # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(real_time_counter_test) # NORDIC SDK APP START FILE(GLOB app_sources main.c date_time_SNTP.c ) target_sources(app PRIVATE ${app_sources}) # NORDIC SDK APP END