文章總列表

Holiday Bluetooth Engineer: Nordic's app_timer module

Purpose
I was tracking the heart rate sensor example, and try to figure out how to use the library. I found app_timer module's usage is a bit weird, so I tried to read source code, and find underlying mechanism. The note is intended to help people who want to understand app_timer library in Nordic's library, since Nordic doesn't have good document of this library. The module uses a single HW timer to implement several SW timers. I once read the trick from O'Reilly book, and I'm interested in how to implement it.

Architecture
  • The library uses RTC1 timer as underlying hardware timer.
  • The library supports multiple SW timers through using single HW timer (RTC1). It mainly uses queue/ linked-list to achieve the target.
  • The library also supports schedule timer at different IRQ level. This is implementation consideration, which allows multiple execution context running at the same time without creating critical section explicitly.
  • Users can create several timers, which specified different {period, one-shot/ periodic mode, call back function}

How to use it 
Initialization
#define APP_TIMER_PRESCALER (0)
#define APP_TIMER_MAX_TIMERS (5) // # of timers
#define APP_TIMER_OP_QUEUE_SIZE (5) // timer operation queue
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
  • In my board, a 32768hz oscillator is placed. I set the prescalar of RTC1 to 0, whose actual divider value is (PRESCALER + 1)
    RTC1's internal timer is 24-bit wide, and setting prescaler to 0 results timer overflow after 512 second
    It's the embedded designer's job to determine the prescaler value. Larger prescaler, worse accuracy, but longer period and vice versa.
  • Max_timers indicating how many SW timers is created. It affects internal storage size. 
  • OP_QUEUE_SIZE indicates how many operations are queued.
    Users' operation is not affecting the app_timer's data structure directly. Instead, it's queued in its data structure. Then the library triggered SWI IRQ, and do them asynchronously.
    The # of queued operation would affects the internal storage size.
  • The macro would declare static memory required for the timer (calculated with user specified parameters)
  • The macro would also initialize internal storage
    • For example, each timer structure has a field "state", and it will declare as STATE_FREE standing for idle stagus
    • Each timer had operation queue, which stores pending operations

Create timer
app_timer_create(*timer_id, SINGLE_SHOT/REPEATED, handler_function)
  • The API goes through timer_node_t[] array, find the 1st STATE_FREE node
  • It will set the status as STATE_ALLOCATED and return timer-id to user (via *timer_id)
  • Users shall keep timer_id (something like a handler) for later timer-library operation

Start timer
uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context)
  • Users shall specify timer-id returned by app_timer_create() here
  • The library provided a macro to convert "mini-second" to timeout_ticks here
    • APP_TIMER_TICKS(ms, PRE_SCALER)  -> mini-second to TICK
    • If the timer is created in one-shot mode, then timeout_ticks is set to 0
  • 3rd parameter "p_context" is used with another library component "scheduler". Ignore it temporally and set to NULL now.
  • The API would queue user's intention, and tirgger IRQ to do remaining operation in IRQ context.
  • Function call flow:
    app_timer_start() -> timer_start_op_schedule() -> timer_list_handler_sched() ->IRQ -> SWI0_IRQHandler -> timer_list_handler()

Stop timer
uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context)

Personal View
  • Using APIs above, I could declare an additional timer easily. I saw my own timer through some mesaages to UART periodically.
  • The module is ambitious which provides event-driven core to embedded design. It's designed in general approach with good quality.
  • However, the coding style of this module is aweful. Nordic should declarea top-level struct {...} which reveals their internal data structure's hierachy. The internal data structure also avoids people to understand internal operation easily
  • If you were to develop something with Nordic's MCU, I personally think it's OK to leverage Nordic's work to build users' application. I think it's unnecessary to build everything from scratch.
  • In embedded system design, I still prefer controlling all MCU's peripheral rather than using existing library.

留言

這個網誌中的熱門文章

幼犬書桌椅選擇心得 升降桌 兒童桌椅

STM32 UART + DMA,使用HAL實作TX/RX,以及不定長度接收

CANON G3000 廢墨瓶改裝