一、前言背景
爲了精確控制led週期閃爍,通過引入定時器中斷來處理。看了數據手冊和例程,發現Nordic有提供了兩種截然不同的定時器應用方案
二、定時器歸類
TIMER: 定時器外設,可以理解爲硬件定時器(類似STM32的通用定時器),運行在高頻時鐘源上(HFCLK),兩種工作模式,定時與計數(捕捉與比較);Nordic52810總共有三路定時器外設,TIMER0
TIMER1
TIMER1
- 優點: 功能豐富,可配置高中斷優先級,精確高
- 缺點: 功耗較高
app_timer:定時模塊,可以理解爲軟件定時器(軟件模擬的定時器),基於低頻時鐘(32kHZ)RTC1,配合協議棧使用,當定時時間溢出時(timeout),會通過藍牙協議棧回調預先配置好的函數接口
- 優點:功耗極低(uA),可配置多個定時器中斷(理論上RAM充裕的話,可以一直往隊列裏面插入)
- 缺點:精度較低,最小計時精度爲1ms,由於是軟定時器,會被其他高優先級任務搶佔,多多少少會被影響計時精度
三、接口講解
考慮到功耗至上的原則,選擇app_timer中斷來控制LED燈光閃爍(這個精度也不需要太高)
協議棧運行前需要初始化
app_timer
模塊
ret_code_t app_timer_init(void)
{
// Stop RTC to prevent any running timers from expiring (in case of reinitialization)
rtc1_stop();//先停止rtc1
// Initialize operation queue
m_op_queue.first = 0;
m_op_queue.last = 0;
m_op_queue.size = APP_TIMER_CONFIG_OP_QUEUE_SIZE+1;//最大支持的隊列大小,可根據實際應用需要調整宏
mp_timer_id_head = NULL;
m_ticks_elapsed_q_read_ind = 0;
m_ticks_elapsed_q_write_ind = 0;
#if APP_TIMER_WITH_PROFILER
m_max_user_op_queue_utilization = 0;
#endif
NVIC_ClearPendingIRQ(SWI_IRQn);//通過swi異常中斷後來觸發定時回調
NVIC_SetPriority(SWI_IRQn, SWI_IRQ_PRI);
NVIC_EnableIRQ(SWI_IRQn);
rtc1_init(APP_TIMER_CONFIG_RTC_FREQUENCY);
m_ticks_latest = rtc1_counter_get();
return NRF_SUCCESS;
}
初始化軟定時器模塊後,需要進行定時器的創建,通過接口
app_timer_create
ret_code_t app_timer_create(app_timer_id_t const * p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler)
{
// Check state and parameters
VERIFY_MODULE_INITIALIZED();
if (timeout_handler == NULL)//定時中斷回調的函數,爲空返回無效參數
{
return NRF_ERROR_INVALID_PARAM;
}
if (p_timer_id == NULL)//句柄,爲空返回無效參數
{
return NRF_ERROR_INVALID_PARAM;
}
if (((timer_node_t*)*p_timer_id)->is_running)
{
return NRF_ERROR_INVALID_STATE;
}
timer_node_t * p_node = (timer_node_t *)*p_timer_id;//保存到隊列中
p_node->is_running = false;
p_node->mode = mode;
p_node->p_timeout_handler = timeout_handler;
return NRF_SUCCESS;
}
軟定時器創建完成後,可通過模塊啓動接口
app_timer_start
使能定時,也可以通過app_timer_stop
停止定時
四、代碼實例
通過配置1秒重複觸發回調進行led燈的翻轉閃爍
APP_TIMER_DEF(led_timer_id); //定義句柄
//定時器觸發回調
static void led_callback(void * p_context)
{
bsp_LedToggle(BSP_LED0);//翻轉led
}
int main(void)
{
app_timer_init();//初始化app_timer模塊
// Initialize.
ble_stack_init();//初始化協議棧
bsp_InitLED();//初始化led
app_timer_create(&led_timer_id,APP_TIMER_MODE_REPEATED,led_callback);//註冊句柄,重複觸發,回調函數
app_timer_start(led_timer_id,APP_TIMER_TICKS(1000), NULL); //1秒觸發一次
while(1);
}
五、備註
- 未初始化app_timer的情況下進行創建定時器將會導致非法訪問空指針,導致系統異常復位
- 未創建定時器任務,調用開啓、停止接口也會導致訪問空指針,導致系統異常復位
- 重複調用開啓、停止接口,將會出現產生不可預知的結果