Nrf52-timer-config
Contents
Overview . . .
These notes started to support work with nRF52832 Bluetooth System On Chip (SoC) device. First task involves utilizing an available timer along side implemented and active "soft device" of type S132. A good reference for nRF52 SoC:
Looking for the definition of Nordic demo code routine
nrf_drv_timer_enable
:
./integration/nrfx/legacy/nrf_drv_timer.h:76:#define nrf_drv_timer_enable nrfx_timer_enable
This routine is defined in $(SDK)/./modules/nrfx/drivers/src/nrfx_timer.c
:
void nrfx_timer_enable(nrfx_timer_t const * const p_instance) { NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED); nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START); m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON; NRFX_LOG_INFO("Enabled instance: %d.", p_instance->instance_id); }
QUESTION / ISSUE: I think the timer2 provisioning code is breaking in this routine, that somehow we've missed a proper declaring and instantiating of a handle for TIMER2.
2021-04-25
Looking at some example code in nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/components/app_simple_timer.c
, there are routines to init, start, stop (pause) and to uninitialize. Given a task to assure that a Nordic chip goes into lower or lowest possible power mode, we'll first examine the 'stop' and 'uninit' routines in this Nordic demo code.
The routine uint32_t app_simple_timer_stop(void)
is a wrapper to nrf_drv_timer_pause(nrfx_timer_t const * const p_instance)
. Going through reverse engineering steps this routine is first defined via a macro:
./integration/nrfx/legacy/nrf_drv_timer.h:82:#define nrf_drv_timer_pause nrfx_timer_pause
The definition of nrfx_timer_pause()
is in the following .c file,
./modules/nrfx/drivers/include/nrfx_timer.h:186:void nrfx_timer_pause(nrfx_timer_t const * const p_instance); ./modules/nrfx/drivers/src/nrfx_timer.c:170:void nrfx_timer_pause(nrfx_timer_t const * const p_instance)
First Nordic demo code directories in which we're reviewing files:
nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/components/libraries/simple_timer nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/modules nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/modules/nrfx/drivers/include nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/modules/nrfx/drivers/src nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/integration/nrfx/legacy
Something interesting in file nrfx_timer.c, looks like a compile time evaluation may determine the size of an array of time_control_block_t
types:
64 static timer_control_block_t m_cb[NRFX_TIMER_ENABLED_COUNT];
. . . sure enough, in ../include/nrfx_timer.h
there is an enum whose members are conditionally compiled. Clever!:
enum { #if NRFX_CHECK(NRFX_TIMER0_ENABLED) NRFX_TIMER0_INST_IDX, #endif #if NRFX_CHECK(NRFX_TIMER1_ENABLED) NRFX_TIMER1_INST_IDX, #endif #if NRFX_CHECK(NRFX_TIMER2_ENABLED) NRFX_TIMER2_INST_IDX, #endif #if NRFX_CHECK(NRFX_TIMER3_ENABLED) NRFX_TIMER3_INST_IDX, #endif #if NRFX_CHECK(NRFX_TIMER4_ENABLED) NRFX_TIMER4_INST_IDX, #endif NRFX_TIMER_ENABLED_COUNT };
So the first demo routine of interest, to pause or equivalently stop a timer is:
void nrfx_timer_pause(nrfx_timer_t const * const p_instance) { NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED); nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP); NRFX_LOG_INFO("Paused instance: %d.", p_instance->instance_id); }
We're looking at this to determine whether stopping a timer removes power from the timer peripheral in the nRF52 SoC. Looks like there is another layer of routine nesting with the call here to nrf_timer_task_trigger()
. There may be some clue as to how this routine works, and possibly one of the parameters it accepts will power off a given timer. Here are all calls to it in the nrfx/drivers directory:
ted@localhost$ ./nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/modules/nrfx/drivers $ grep -nr nrf_timer_task_trigger ./* ./src/nrfx_timer.c:144: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START); ./src/nrfx_timer.c:152: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN); ./src/nrfx_timer.c:166: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START); ./src/nrfx_timer.c:173: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP); ./src/nrfx_timer.c:180: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR); ./src/nrfx_timer.c:188: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_COUNT); ./src/nrfx_timer.c:197: nrf_timer_task_trigger(p_instance->p_reg,
From the file ./hal/nrf_timer.h
looks also like nrf_timer_task_trigger() is normally an in-lined routine:
#ifndef SUPPRESS_INLINE_IMPLEMENTATION __STATIC_INLINE void nrf_timer_task_trigger(NRF_TIMER_Type * p_reg, nrf_timer_task_t task) { *((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)task)) = 0x1UL; }
So this routine writes a timer peripheral control register with the value 1. Seems a little odd, is there some syntax I am misreading here? . . .
^ nRF5x shutting down timer
Well here is something interesting and promising, the comment along side the following definition of NRF_TIMER_TASK_SHUTDOWN
implies that this action powers off the timer:
/nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345/modules/nrfx $ grep -nr NRF_TIMER_TASK_SHUTDOWN ./* ./drivers/src/nrfx_timer.c:152: nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN); ./hal/nrf_timer.h:108: NRF_TIMER_TASK_SHUTDOWN = offsetof(NRF_TIMER_Type, TASKS_SHUTDOWN), ///< Task for powering off the timer.
So then where and how is offsetof()
defined?
^ offsetof() routine definition
Looks like this offsetof()
is defined in components/libraries/util/app_util.h:
./components/libraries/util/app_util.h:190:#undef offsetof ./components/libraries/util/app_util.h:191:#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
Here are the lines and some conditional directives to activate or disable the possible redefinition of offsetof()
:
187 /*Segger embedded studio originally has offsetof macro which cannot be used in macros (like STATIC_ASSERT). 188 This redefinition is to allow using that. */ 189 #if defined(__SES_ARM) && defined(__GNUC__) 190 #undef offsetof 191 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 192 #endif
A couple of references to explain offsetof() purpose:
- https://gcc.gnu.org/onlinedocs/gcc/Offsetof.html
- https://stackoverflow.com/questions/400116/what-is-the-purpose-and-return-type-of-the-builtin-offsetof-operator#:~:text=The%20purpose%20of%20a%20built-in%20__offsetof%20operator%20is,when%20%28%26lvalue%29%20returned%20the%20address%20of%20that%20rvalue.
^ SIMPLE_TIMER definition
The symbol SIMPLE_TIMER
we encounter first in _59ac345/components/libraries/simple_timer/app_simple_timer.c on line 64:
excerpt 1: const nrf_drv_timer_t SIMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(SIMPLE_TIMER_CONFIG_INSTANCE);
nrf_drv_timer_t
is a typedef of nrfx_timer_t
, defined in ./modules/nrfx/drivers/include/nrfx_timer.h:
97 /** 98 * @brief Timer driver instance configuration structure. 99 */ 100 typedef struct 101 { 102 nrf_timer_frequency_t frequency; ///< Frequency. 103 nrf_timer_mode_t mode; ///< Mode of operation. 104 nrf_timer_bit_width_t bit_width; ///< Bit width. 105 uint8_t interrupt_priority; ///< Interrupt priority. 106 void * p_context; ///< Context passed to interrupt handler. 107 } nrfx_timer_config_t;
...345/integration/nrfx/legacy/nrf_drv_timer.h:
63 /** @brief Macro for forwarding the new implementation. */ 64 #define NRF_DRV_TIMER_INSTANCE NRFX_TIMER_INSTANCE
Ok so the big question of where is the timer instance expressed and used in these SDK library code is about to be answered. Symbol NRFX_TIMER_INSTANCE
is defined in file [unzip_location]/nRF5_SDK_15.3.0_59ac345/modules/nrfx/drivers/include/nrfx_timer.h:
68 /** 69 * @brief Macro for creating a timer driver instance. 70 */ 71 #define NRFX_TIMER_INSTANCE(id) \ 72 { \ 73 .p_reg = NRFX_CONCAT_2(NRF_TIMER, id), \ 74 .instance_id = NRFX_CONCAT_3(NRFX_TIMER, id, _INST_IDX), \ 75 .cc_channel_count = NRF_TIMER_CC_CHANNEL_COUNT(id), \ 76 }
and,
./modules/nrfx/hal/nrf_timer.h:96:#define NRF_TIMER_CC_CHANNEL_COUNT(id) NRFX_CONCAT_3(TIMER, id, _CC_NUM)
Given these the above line _excerpt_1_ expands to:
const nrf_drv_timer_t SIMPLE_TIMER = { .p_reg = NRF_TIMER#id, .instance_id = NRFX_TIMER#id#_INST_IDX, .cc_channel_count = TIMER#id#_CC_NUM, }
Finally symbol SIMPLE_TIMER_CONFIG_INSTANCE is, relative to the app_simple_timer.c source file, defined in the following sdk_config.h file:
./examples/peripheral/simple_timer/pca10040/blank/config/sdk_config.h:622:#ifndef SIMPLE_TIMER_CONFIG_INSTANCE ./examples/peripheral/simple_timer/pca10040/blank/config/sdk_config.h:623:#define SIMPLE_TIMER_CONFIG_INSTANCE 1
A few more definitions:
-
./modules/nrfx/hal/nrf_timer.h:166:} nrf_timer_short_mask_t;
/** * @brief Types of timer shortcuts. */ typedef enum { NRF_TIMER_SHORT_COMPARE0_STOP_MASK = TIMER_SHORTS_COMPARE0_STOP_Msk, ///< Shortcut for stopping the timer based on compare 0. NRF_TIMER_SHORT_COMPARE1_STOP_MASK = TIMER_SHORTS_COMPARE1_STOP_Msk, ///< Shortcut for stopping the timer based on compare 1. NRF_TIMER_SHORT_COMPARE2_STOP_MASK = TIMER_SHORTS_COMPARE2_STOP_Msk, ///< Shortcut for stopping the timer based on compare 2. NRF_TIMER_SHORT_COMPARE3_STOP_MASK = TIMER_SHORTS_COMPARE3_STOP_Msk, ///< Shortcut for stopping the timer based on compare 3. #if defined(TIMER_INTENSET_COMPARE4_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_SHORT_COMPARE4_STOP_MASK = TIMER_SHORTS_COMPARE4_STOP_Msk, ///< Shortcut for stopping the timer based on compare 4. #endif #if defined(TIMER_INTENSET_COMPARE5_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_SHORT_COMPARE5_STOP_MASK = TIMER_SHORTS_COMPARE5_STOP_Msk, ///< Shortcut for stopping the timer based on compare 5. #endif NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK = TIMER_SHORTS_COMPARE0_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 0. NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK = TIMER_SHORTS_COMPARE1_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 1. NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK = TIMER_SHORTS_COMPARE2_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 2. NRF_TIMER_SHORT_COMPARE3_CLEAR_MASK = TIMER_SHORTS_COMPARE3_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 3. #if defined(TIMER_INTENSET_COMPARE4_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_SHORT_COMPARE4_CLEAR_MASK = TIMER_SHORTS_COMPARE4_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 4. #endif #if defined(TIMER_INTENSET_COMPARE5_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_SHORT_COMPARE5_CLEAR_MASK = TIMER_SHORTS_COMPARE5_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 5. #endif } nrf_timer_short_mask_t;
^ app_simple_timer.c event handler
So in initializing its timer, the nRF example code we're following here references an event handler for TIMERn type events. This code specifically is located in nRF5_SDK_15.3.0_59ac345/components/libraries/simple_timer/app_simple_timer.c
, where 'nRF5_SDK_15.3.0_59ac345' is the top level directory which unpacks from nRF's SDK 15.3.0 code bundle. It is this code bundle which provides the S132 soft device code for the nRF52832 SoC.
Here is the event handler, which only gives us some initial idea how to proceed with our own event handler. Of note is the first and only named case in the switch statement. Be worth looking for an enumeration of TIMERn type events among SDK sources . . .
/** * @brief Handler for timer events. */ static void app_simple_timer_event_handler(nrf_timer_event_t event_type, void * p_context) { switch (event_type) { case NRF_TIMER_EVENT_COMPARE0: if (m_mode == APP_SIMPLE_TIMER_MODE_SINGLE_SHOT) { m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED; } //@note: No NULL check required as performed in timer_start(...). m_timeout_handler(mp_timeout_handler_context); break; default: //Do nothing. break; } }
A search reveals a definition in ./modules/nrfx/hal/nrf_timer.h
:
- 128: NRF_TIMER_EVENT_COMPARE0 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[0]), ///< Event from compare channel 0.
/** * @brief Timer events. */ typedef enum { /*lint -save -e30*/ NRF_TIMER_EVENT_COMPARE0 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[0]), ///< Event from compare channel 0. NRF_TIMER_EVENT_COMPARE1 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[1]), ///< Event from compare channel 1. NRF_TIMER_EVENT_COMPARE2 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[2]), ///< Event from compare channel 2. NRF_TIMER_EVENT_COMPARE3 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[3]), ///< Event from compare channel 3. #if defined(TIMER_INTENSET_COMPARE4_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_EVENT_COMPARE4 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[4]), ///< Event from compare channel 4. #endif #if defined(TIMER_INTENSET_COMPARE5_Msk) || defined(__NRFX_DOXYGEN__) NRF_TIMER_EVENT_COMPARE5 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[5]), ///< Event from compare channel 5. #endif /*lint -restore*/ } nrf_timer_event_t;
Definition of EVENTS_COMPARE appears in multiple files . . .
THavelka@ENG-TMH MINGW64 ~/Documents/nRF5SDK153059ac345/nRF5_SDK_15.3.0_59ac345 $ grep -nr EVENTS_COMPARE ./* | grep uint ./components/ble/ble_dtm/ble_dtm.c:1065: NRF_PPI->CH[0].EEP = (uint32_t)&mp_timer->EVENTS_COMPARE[0]; ./components/libraries/timer/experimental/drv_rtc.c:60: ((nrf_rtc_event_t)(offsetof(NRF_RTC_Type, EVENTS_COMPARE[0]) + sizeof(uint32_t)*_cc)) ./components/proprietary_rf/esb/nrf_esb.c:543: NRF_PPI->CH[NRF_ESB_PPI_RX_TIMEOUT].EEP = (uint32_t)&NRF_ESB_SYS_TIMER->EVENTS_COMPARE[0]; ./components/proprietary_rf/esb/nrf_esb.c:546: NRF_PPI->CH[NRF_ESB_PPI_TX_START].EEP = (uint32_t)&NRF_ESB_SYS_TIMER->EVENTS_COMPARE[1]; ./modules/nrfx/mdk/nrf51.h:684: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Compare event on CC[n] match. */ ./modules/nrfx/mdk/nrf51.h:723: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Compare event on CC[n] match. */ ./modules/nrfx/mdk/nrf52.h:1568: __IOM uint32_t EVENTS_COMPARE[6]; /*!< (@ 0x00000140) Description collection[0]: Compare event on CC[0] ./modules/nrfx/mdk/nrf52.h:1605: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Description collection[0]: Compare event on CC[0] ./modules/nrfx/mdk/nrf52810.h:1339: __IOM uint32_t EVENTS_COMPARE[6]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf52810.h:1376: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf52811.h:1409: __IOM uint32_t EVENTS_COMPARE[6]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf52811.h:1446: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf52840.h:1815: __IOM uint32_t EVENTS_COMPARE[6]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf52840.h:1852: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf9160.h:1457: __IOM uint32_t EVENTS_COMPARE[6]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n] ./modules/nrfx/mdk/nrf9160.h:1502: __IOM uint32_t EVENTS_COMPARE[4]; /*!< (@ 0x00000140) Description collection: Compare event on CC[n]
^ app_simple_timer_timeout_handler_t
Info on the function or callback pointer type associated with nRF52xxx timer demo codes:
* https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Fgroup__app__simple__timer.html
^ Sources and leads to review
Things to review:
- Following code seems to reference timer task counting, might this relate to using two or more capture/compare registers with a given nRF timer?
./components/libraries/experimental_libuarte/nrf_libuarte.c:183: nrfx_timer_task_address_get(&p_libuarte->timer, NRF_TIMER_TASK_COUNT),
- Let's look into this as well, code to obtain address of a timer task:
./integration/nrfx/legacy/nrf_drv_timer.h:107:#define nrf_drv_timer_task_address_get nrfx_timer_task_address_get
^ References
-
Some Nordic nRF SDK notes at Sky Blue Trades:
Nordic DFU bootloader how-to :