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 timerapp_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 timeruint32_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 timeruint32_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.
留言