app_timer是大家經常用到的一個庫,app_timer的功能就是定時,也就是說,你在某一時刻啟動一個app timer並設定超時時間,超時時間一到,app_timer就會回調timeout handler,然后執行你需要的工作。使用app_timer時有如下幾點需要注意:
- app_timer底層使用的是RTC1,而不是timer1/2/3/4,所以app_timer的功耗非常低:0.1uA左右。
- app_timer計時精度為1ms,也就是說,app_timer只能計時毫秒的倍數,如果你的計時精度小於1ms,請使用傳統timer1/2/3/4來做。
- app_timer計時不是很准確。app_timer庫可以創建幾十甚至上百個app timer,每次start或者stop這些timer,都會對其他timer計時精度產生一些影響。而且app_timer的中斷優先級也不高,所以timeout handler經常會被推遲執行。
- 啟動或者停止app_timer都是異步的,也就是說,當調用app_timer_start或者app_timer_stop時,系統只是把start或者stop操作入隊,然后觸發一個軟中斷,如果此時上下文環境的中斷優先級高於軟中斷,那么只有等退出了當前上下文環境后才會真正去執行軟中斷handler然后啟動或者停止timer,這也是為什么app_timer模塊需要一個operation queue,並通過APP_TIMER_CONFIG_OP_QUEUE_SIZE來配置其大小;如果此時上下文環境的優先級低於軟中斷,那么立即觸發軟中斷handler並啟動或者停止timer。
用法說明
一般按照如下步驟使用app_timer:
- 修改app_timer默認配置參數,如下:
- 創建app_timer。創建app timer時,先定義一個timer ID,用來表示這個timer,然后選擇app timer模式:single shot或者repeated。Single shot模式app timer只運行一次,timeout后執行timeout handler然后自動停止app timer。Repeated模式app timer自動循環執行,每次timeout后執行timeout handler,然后繼續計時,直到下一個timeout然后再次執行timeout handler,如此循環往復。創建app timer的時候,還需要定義timeout handler。
APP_TIMER_DEF(my_timer_id); //定義timer ID err_code = app_timer_create(&my_timer_id, APP_TIMER_MODE_REPEATED, my_timeout_handler) static void my_timeout_handler (void * p_context) { //add your code here }
- 啟動app_timer或者停止app_timer。前面也提及過,啟動或者停止timer是異步的,所以我們有一個operation queue來存放start或者stop操作。真正的start或者stop操作是通過軟中斷0來實現的。
err_code = app_timer_start(my_timer_id, APP_TIMER_TICKS(10), NULL); //啟動timer並定時10ms err_code = app_timer_stop(my_timer_id);
常見使用問題
目前看到的常見使用問題有:
- 沒有按照使用說明來使用app_timer,比如定義app timer ID的時候不使用宏APP_TIMER_DEF,超時時間不使用宏APP_TIMER_TICKS來計算。
- 多次重復調用同一個app_timer_create。app_timer_create用於創建一個timer,多次調用同一個app_timer_create,會讓系統產生多個相同ID的app timer,以致於系統出現不可知的行為。
- Stop沒有start的timer。當一個timer沒有通過app_timer_start啟動時,使用app_timer_stop停止它時,或者使用app_timer_stop停止一個不存在的timer時,會打亂app timer的正常行為,產生不可預測的結果。
- Operation queue溢出。這個需要具體問題具體分析,有時候operation queue溢出不一定是因為queue size設置太小導致的,而是系統某個地方,確切說某個中斷例程,執行時間太久,導致start和stop操作積累太多,從而產生queue溢出,這個時候就必須找出這個中斷例程執行時間太久的原因,才能從根本上解決這個問題。
- 沒有初始化app_scheduler,而直接使用app_timer的schedule模式。app_scheduler原理及使用說明見:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Flib_scheduler.html&cp=5_1_3_38,概括來說,app_scheduler的作用就是把長長的中斷代碼從中斷函數轉到main線程中來執行。