文章總列表

假日藍芽/嵌入式系統工程師: Nordic app_timer module

目的
Nordic提供心跳表的範例程式,不用多少功夫就能把它跑起來。範例用了app_timer module,因為某種潔癖使然,我花了一些時間追蹤運作機制。另外Nordic沒有太多文件描述他的運作,我也希望這篇文章能幫助社會大眾。

app_timer module使用一個RTC (Real Time Clock)模擬成多個軟體timer。這個技巧以前我在歐萊禮的書看過,當時沒花太多工夫研究,這次也想順便弄懂裡面是怎麼實作的。

架構
  • app_timer使用硬體RTC1 timer
  • app_timer支援多個軟體timer,但是底層只用RTC1硬體timer。軟體用了queue,linked-list這些資料結構堆出需要的功能
  • 程式庫支援在不同的IRQ level執行(ARM Cortex M0支援多種IRQ level)。這個比較是設計的考量,操作內部資料結構時可以不用關中斷製造critical section
  • 使用者可以創造多個SW timer,可以設定{period, one-shot/periodic, callback function}

使用方式就不寫中文了 (懶惰)
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)
個人觀點
  • 用這套library,很容易就能在當前的範例多宣告一個timer。我在藍芽的範例裡多宣告一個timer,每隔一段時間讓UART送一些資訊出來
  • 這套library的野心滿大的,提供一套event-driven的框架。設計的方法很通用,品質也不錯。
  • 不過coding style實在有點恐怖。Nordic應該宣告一個頂層的資料結構像struct {...}把東西放在裡面,這樣能清楚顯示資料結構的階層關係。不然讀起來實在有點痛苦。
  • 如果用Nordic的MCU做玩具,我覺得直接利用這個library滿好的,品質也很到位。畢竟不需要從頭到尾刻所有東西。
  • 不過嵌入式設計,我還是比較喜歡自己控制MCU的peripherals,使用library還是有點不舒服。
  • 這套library因為考量general,效率還有最佳化的空間。

留言

這個網誌中的熱門文章

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

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

CANON G3000 廢墨瓶改裝