Nrf52-timer-config

From Wiki at Neela Nurseries
Revision as of 17:00, 28 April 2021 by Ted (talk | contribs)
Jump to: navigation, search

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:

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:


^ SIMPLE_TIMER definition

The symbol SIMPLE_TIMER we encounter first in _59ac345/components/libraries/simple_timer/app_simple_timer.c on line 64:

 64    const nrf_drv_timer_t SIMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(SIMPLE_TIMER_CONFIG_INSTANCE);:w


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 }

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


^ 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