浅析蓝牙nrf51822程序框架
這篇先分析一下提供模板的框架部分程序。
這里以模板的代碼(燈和按鍵)為例:
http://download.csdn.net/download/dfsae/9987318
1.主函數(shù)
NRF51822的框架還是采用事件驅(qū)動(dòng)框架。先從主函數(shù)進(jìn)行分析
主函數(shù)里做一些初始化,再啟動(dòng)定時(shí)器和廣播,在主循環(huán)里實(shí)現(xiàn)任務(wù)調(diào)度和電源管理power_manage();
1.1.定時(shí)器
NRF51822的定時(shí)器由隊(duì)列進(jìn)行多個(gè)定時(shí)器的管理。
1.1.1.數(shù)據(jù)結(jié)構(gòu)
定時(shí)器主要放在timer_node_t結(jié)構(gòu)體組成數(shù)組中進(jìn)行集中管理,存儲(chǔ)的方式具體看timers_init中的解析。
timer_node_t的結(jié)構(gòu)如下:
app_timer.c中為定時(shí)器隊(duì)列提供了基礎(chǔ)的添加移除操作。
1.1.2.初始化函數(shù)
主函數(shù)中調(diào)用timers_init實(shí)現(xiàn)定時(shí)器的初始化
static void timers_init(void) {// Initialize timer module, making it use the schedulerAPP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true); }#define APP_TIMER_INIT(PRESCALER, MAX_TIMERS, OP_QUEUES_SIZE, USE_SCHEDULER) \do \{ \static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE((MAX_TIMERS), \(OP_QUEUES_SIZE) + 1), \sizeof(uint32_t))]; \uint32_t ERR_CODE = app_timer_init((PRESCALER), \(MAX_TIMERS), \(OP_QUEUES_SIZE) + 1, \APP_TIMER_BUF, \(USE_SCHEDULER) ? app_timer_evt_schedule : NULL); \APP_ERROR_CHECK(ERR_CODE); \} while (0)該初始化調(diào)用了app_timer.c中的app_timer_init,同時(shí)根據(jù)USE_SCHEDULER來設(shè)置回調(diào)函數(shù)app_timer_evt_schedule。
static __INLINE uint32_t app_timer_evt_schedule(app_timer_timeout_handler_t timeout_handler,void * p_context) {app_timer_event_t timer_event;timer_event.timeout_handler = timeout_handler;timer_event.p_context = p_context;return app_sched_event_put(&timer_event, sizeof(timer_event), app_timer_evt_get); }app_timer_evt_schedule中做了:
1>.生成一個(gè)事件。
2>.通過事件調(diào)度的API(app_sched_event_put)發(fā)送事件。
定時(shí)器初始化主要做了以下:
1>.設(shè)置事件回調(diào)函數(shù)(如果有),綁定的是app_timer_evt_schedule函數(shù)。
2>.初始化分配傳進(jìn)來數(shù)目的定時(shí)器,并分配好對應(yīng)的空間
在app_timer.c中,定義了一些內(nèi)部變量來管理整個(gè)定時(shí)器系統(tǒng),一些參數(shù)放在傳入的內(nèi)存中保存。內(nèi)存中的存放如下:
最開始的內(nèi)存中保存每個(gè)定時(shí)器的分配狀態(tài)(state)以及是否運(yùn)行狀態(tài)(is_running),這兩個(gè)都是timer_node_t 結(jié)構(gòu)體中的參數(shù)。
中間存放timer_user_t結(jié)構(gòu)體的數(shù)據(jù)
最后一塊存放對應(yīng)的timer_user_op_t結(jié)構(gòu)體數(shù)據(jù)。
3>.設(shè)置開啟中斷
1.1.2.創(chuàng)建定時(shí)器函數(shù)
在一開始分配完成定時(shí)器后,后續(xù)定時(shí)器在使用之前可以由用戶自定義進(jìn)行分配。分配只需調(diào)用app_timer_create函數(shù)即可。用戶傳入該定時(shí)器的工作模式和回調(diào)函數(shù),正常情況下會(huì)找到空閑的定時(shí)器放在p_timer_id中返回,即用戶得到當(dāng)前分配的定時(shí)器id。過程中只修改了對應(yīng)ID的mp_nodes的值。比如在該例程中初始化button的最后就分配了一個(gè)定時(shí)器。
uint32_t app_timer_create(app_timer_id_t * p_timer_id,app_timer_mode_t mode,app_timer_timeout_handler_t timeout_handler) {int i;if (mp_nodes == NULL){return NRF_ERROR_INVALID_STATE;}if (timeout_handler == NULL){return NRF_ERROR_INVALID_PARAM;}if (p_timer_id == NULL){return NRF_ERROR_INVALID_PARAM;} // 尋找看空閑的定時(shí)器for (i = 0; i < m_node_array_size; i++){if (mp_nodes[i].state == STATE_FREE){mp_nodes[i].state = STATE_ALLOCATED;mp_nodes[i].mode = mode;mp_nodes[i].p_timeout_handler = timeout_handler;*p_timer_id = i;return NRF_SUCCESS;}}return NRF_ERROR_NO_MEM; }1.1.3.定時(shí)器中斷
app_timer.c中提供了一些對底層RTC進(jìn)行操作的函數(shù):
rtc1_init —— 初始化
rtc1_start —— 啟動(dòng)定時(shí)器
rtc1_stop —— 終止定時(shí)器
rtc1_counter_get —— 獲得定時(shí)器的計(jì)數(shù)值
rtc1_compare0_set —— 設(shè)置過零比較器
RTC1_IRQHandler —— 定時(shí)器中斷處理函數(shù)
從中斷處理這里開始說起。
timer_timeouts_check函數(shù)負(fù)責(zé)會(huì)設(shè)定的對應(yīng)的應(yīng)用是否到時(shí)間的定時(shí)器的檢測。
static void timer_timeouts_check(void) {if (m_timer_id_head != TIMER_NULL) //處理到時(shí)間的定時(shí)器{app_timer_id_t timer_id;uint32_t ticks_elapsed;uint32_t ticks_expired;// 初始化實(shí)際經(jīng)過的ticks為0ticks_expired = 0;// ticks_elapsed(到期時(shí)間)在這里被得到, 現(xiàn)在的計(jì)數(shù)和上次計(jì)數(shù)的差值ticks_elapsed = ticks_diff_get(rtc1_counter_get(), m_ticks_latest);// Auto variable containing the head of timers expiring timer_id = m_timer_id_head;// 到時(shí)所有定時(shí)器 ticks_elapsed 并且獲得ticks_expired (到期時(shí)間)while (timer_id != TIMER_NULL){timer_node_t * p_timer;p_timer = &mp_nodes[timer_id]; //獲得當(dāng)前定時(shí)器節(jié)點(diǎn)// 未超時(shí)則什么都不做if (ticks_elapsed < p_timer->ticks_to_expire){break;}// 遞減ticks_elapsed(經(jīng)過時(shí)間)值并獲得expired ticks (到期時(shí)間)ticks_elapsed -= p_timer->ticks_to_expire;ticks_expired += p_timer->ticks_to_expire;// 檢測下一個(gè)定時(shí)器timer_id = p_timer->next;//回調(diào)timeout_handler_exec(p_timer);}// 準(zhǔn)備向m_ticks_elapsed隊(duì)列中加ticks過期的隊(duì)列 if (m_ticks_elapsed_q_read_ind == m_ticks_elapsed_q_write_ind){// 讀需要等于寫序號(hào)。這意味著ticks_expired新值需要被存儲(chǔ)在新的地址// 在m_ticks_elapsed隊(duì)列(作為雙緩沖區(qū)實(shí)現(xiàn)的。)// 檢測是否有隊(duì)列溢出if (++m_ticks_elapsed_q_write_ind == CONTEXT_QUEUE_SIZE_MAX){// 隊(duì)列溢出. 因此,寫索引指向隊(duì)列的開始m_ticks_elapsed_q_write_ind = 0;}}// 隊(duì)列的ticks到時(shí).m_ticks_elapsed[m_ticks_elapsed_q_write_ind] = ticks_expired;timer_list_handler_sched();} } static void timeout_handler_exec(timer_node_t * p_timer) {if (m_evt_schedule_func != NULL){uint32_t err_code = m_evt_schedule_func(p_timer->p_timeout_handler, p_timer->p_context);APP_ERROR_CHECK(err_code);}else{p_timer->p_timeout_handler(p_timer->p_context);} }它這里的ticks_elapsed和ticks_expired我也被繞的暈乎乎的。但是拋開這個(gè)。這個(gè)函數(shù)的本意是對超時(shí)的定時(shí)器用他們一開始設(shè)置的回調(diào)函數(shù)的回調(diào)。下面的按鍵可以參考。調(diào)用的回調(diào)函數(shù)有兩個(gè)m_evt_schedule_func 和 p_timer->p_timeout_handler。當(dāng)有調(diào)度機(jī)制的時(shí)候調(diào)用前者,發(fā)送給調(diào)度內(nèi)核,最后在主循環(huán)中來進(jìn)行timer_create時(shí)綁定的回調(diào)函數(shù)調(diào)度。在這個(gè)例子中默認(rèn)調(diào)用的都是app_timer_evt_schedule。如果沒有調(diào)度機(jī)制則直接調(diào)用timer_create時(shí)綁定的回調(diào)函數(shù)。
還有一個(gè)SWI0中斷,軟件中斷。
SWI0_IRQHandler ——SWI0中斷,程序里很多地方會(huì)置位這個(gè)中斷。比如前面提到的timer_timeouts_check。
SWI0中斷中執(zhí)行所有定時(shí)器更新
1.1.4.啟動(dòng)定時(shí)器
app_timer_start函數(shù)來啟動(dòng)某個(gè)定時(shí)器。這個(gè)函數(shù)里面有調(diào)用timer_start_op_schedule函數(shù)。這里分配函數(shù)為什么有個(gè)參數(shù)是mp_users
uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) {uint32_t timeout_periodic;// Schedule timer start operationtimeout_periodic = (mp_nodes[timer_id].mode == APP_TIMER_MODE_REPEATED) ? timeout_ticks : 0;return timer_start_op_schedule(user_id_get(),timer_id,timeout_ticks,timeout_periodic,p_context); }static uint32_t timer_start_op_schedule(timer_user_id_t user_id,app_timer_id_t timer_id,uint32_t timeout_initial,uint32_t timeout_periodic,void * p_context) {app_timer_id_t last_index;//分配一個(gè)操作隊(duì)列timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index);if (p_user_op == NULL){return NRF_ERROR_NO_MEM;}p_user_op->op_type = TIMER_USER_OP_TYPE_START;p_user_op->timer_id = timer_id;p_user_op->params.start.ticks_at_start = rtc1_counter_get();p_user_op->params.start.ticks_first_interval = timeout_initial;p_user_op->params.start.ticks_periodic_interval = timeout_periodic;p_user_op->params.start.p_context = p_context;user_op_enque(&mp_users[user_id], last_index); timer_list_handler_sched();return NRF_SUCCESS; }1.2.按鍵
按鍵初始化,在buttons數(shù)組中定義了所有的用到的按鍵及其配置。具體意思參考app_button_cfg_t 結(jié)構(gòu)體。
按鍵這里變量:
m_detection_delay_timer_id定時(shí)器。這個(gè)定時(shí)器用來計(jì)算延時(shí),它在初始化中被創(chuàng)建,并設(shè)置計(jì)時(shí)時(shí)間到后回調(diào)detection_delay_timeout_handler函數(shù)。
同樣,初始化中設(shè)置事件回調(diào)函數(shù)(如果有),綁定的是app_button_evt_schedule函數(shù)。這個(gè)函數(shù)里面的操作和定時(shí)器里面的操作差不多。
uint32_t app_button_init(app_button_cfg_t * p_buttons,uint8_t button_count,uint32_t detection_delay,app_button_evt_schedule_func_t evt_schedule_func) {uint32_t err_code;if (detection_delay < APP_TIMER_MIN_TIMEOUT_TICKS){return NRF_ERROR_INVALID_PARAM;}//保存配置.mp_buttons = p_buttons;m_button_count = button_count;m_detection_delay = detection_delay;m_evt_schedule_func = evt_schedule_func;uint32_t pins_transition_mask = 0; while (button_count--){app_button_cfg_t * p_btn = &p_buttons[button_count];nrf_gpio_cfg_input(p_btn->pin_no, p_btn->pull_cfg); //硬件配置pins_transition_mask |= (1 << p_btn->pin_no); //創(chuàng)建用戶中斷注冊屏蔽位}// Register button module as a GPIOTE user.err_code = app_gpiote_user_register(&m_gpiote_user_id,pins_transition_mask,pins_transition_mask,gpiote_event_handler);if (err_code != NRF_SUCCESS){return err_code;}// Create polling timer.return app_timer_create(&m_detection_delay_timer_id,APP_TIMER_MODE_SINGLE_SHOT,detection_delay_timeout_handler); }按鍵初始化中的操作主要是對按鍵部分管理的變量做了個(gè)初始化,然后配置了硬件和中斷部分,并且設(shè)置了中斷回調(diào)函數(shù)gpiote_event_handler,標(biāo)記了引腳電平狀態(tài)。
當(dāng)按鍵被按下后,系統(tǒng)首先會(huì)回調(diào)gpiote_event_handler函數(shù)。同時(shí)設(shè)置對應(yīng)的延時(shí)參數(shù)后啟動(dòng)的定時(shí)器計(jì)時(shí)。
static void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low) {uint32_t err_code;// 開始檢測計(jì)時(shí)器。如果定時(shí)器正在運(yùn)行,檢測周期重新開始//注意: 使用app_timer_start()中的p_context參數(shù)來向定時(shí)器句柄傳遞引腳狀態(tài) STATIC_ASSERT(sizeof(void *) == sizeof(uint32_t));err_code = app_timer_stop(m_detection_delay_timer_id); //停止定時(shí)器if (err_code != NRF_SUCCESS){// The impact in app_button of the app_timer queue running full is losing a button press.// The current implementation ensures that the system will continue working as normal. return;}m_pin_transition.low_to_high = event_pins_low_to_high;m_pin_transition.high_to_low = event_pins_high_to_low;err_code = app_timer_start(m_detection_delay_timer_id,m_detection_delay,(void *)(event_pins_low_to_high | event_pins_high_to_low));if (err_code != NRF_SUCCESS){// The impact in app_button of the app_timer queue running full is losing a button press.// The current implementation ensures that the system will continue working as normal. } }當(dāng)檢測延時(shí)時(shí)間達(dá)到后調(diào)用detection_delay_timeout_handler回調(diào)函數(shù),這個(gè)函數(shù)里面又會(huì)調(diào)用button_handler_execute按鍵按下的執(zhí)行函數(shù)。在這個(gè)函數(shù)中會(huì)調(diào)用前面的回調(diào)函數(shù)app_button_evt_schedule發(fā)送事件給調(diào)度內(nèi)核。當(dāng)下次內(nèi)核調(diào)度這個(gè)事件的時(shí)候,就會(huì)調(diào)度按鍵響應(yīng)事件了,在這個(gè)例子中LEDBUTTON_BUTTON_PIN_NO按下調(diào)用button_event_handler,這個(gè)是修改服務(wù)中特性的值,這里先不講。
static void detection_delay_timeout_handler(void * p_context) {uint32_t err_code;uint32_t current_state_pins;//獲得當(dāng)前引腳狀態(tài)err_code = app_gpiote_pins_state_get(m_gpiote_user_id, ¤t_state_pins);if (err_code != NRF_SUCCESS){return;}uint8_t i;// 按下按鍵檢測,執(zhí)行按鍵句柄for (i = 0; i < m_button_count; i++){app_button_cfg_t * p_btn = &mp_buttons[i];if (((m_pin_transition.high_to_low & (1 << p_btn->pin_no)) != 0) && (p_btn->button_handler != NULL)){//如果對應(yīng)按鍵有效為高電平,然后從高到低跳變的釋放過程if(p_btn->active_state == APP_BUTTON_ACTIVE_HIGH){button_handler_execute(p_btn, APP_BUTTON_RELEASE);}//如果對應(yīng)按鍵有效為低電平,然后從高到低跳變的按下過程else{button_handler_execute(p_btn, APP_BUTTON_PUSH);}}else if (((m_pin_transition.low_to_high & (1 << p_btn->pin_no)) != 0) && (p_btn->button_handler != NULL)){//如果對應(yīng)按鍵有效為高電平,然后從低到高跳變的按下過程if(p_btn->active_state == APP_BUTTON_ACTIVE_HIGH){button_handler_execute(p_btn,APP_BUTTON_PUSH);}//如果對應(yīng)按鍵有效為低電平,然后從低到高跳變的釋放過程else{button_handler_execute(p_btn,APP_BUTTON_RELEASE);}}} }static void button_handler_execute(app_button_cfg_t * p_btn, uint32_t transition) {if (m_evt_schedule_func != NULL){uint32_t err_code = m_evt_schedule_func(p_btn->button_handler, p_btn->pin_no,transition);APP_ERROR_CHECK(err_code);}else{if(transition == APP_BUTTON_PUSH){p_btn->button_handler(p_btn->pin_no, APP_BUTTON_PUSH);}else if(transition == APP_BUTTON_RELEASE){p_btn->button_handler(p_btn->pin_no, APP_BUTTON_RELEASE);}} }有明白的可以加群:805601459(備注CSDN)進(jìn)行交流。本文僅限于內(nèi)部交流,禁止商用!
總結(jié)
以上是生活随笔為你收集整理的浅析蓝牙nrf51822程序框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NRF51822芯片简介和软硬件开发简介
- 下一篇: 接口大师v3.8.0版本更新,首页改版+