日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅析蓝牙nrf51822程序框架

發(fā)布時(shí)間:2024/3/24 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅析蓝牙nrf51822程序框架 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這篇先分析一下提供模板的框架部分程序。
這里以模板的代碼(燈和按鍵)為例:
http://download.csdn.net/download/dfsae/9987318
1.主函數(shù)
NRF51822的框架還是采用事件驅(qū)動(dòng)框架。先從主函數(shù)進(jìn)行分析

int main(void) {// Initializeleds_init(); //led初始化,硬件配置timers_init();gpiote_init(); //中斷初始化buttons_init();ble_stack_init();scheduler_init(); gap_params_init();services_init();advertising_init();conn_params_init();sec_params_init();// Start executiontimers_start();advertising_start();// Enter main loopfor (;;){app_sched_execute();power_manage();} }

主函數(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)如下:

typedef struct {timer_alloc_state_t state; /**< 定時(shí)器分配狀態(tài) */app_timer_mode_t mode; /**< 定時(shí)器模式 */uint32_t ticks_to_expire; /**< 上一次定時(shí)器中斷到終止的ticks. */uint32_t ticks_at_start; /**< 當(dāng)前當(dāng)定時(shí)器啟動(dòng)的RTC計(jì)數(shù)值. */uint32_t ticks_first_interval; /**< 第一次定時(shí)器間隔的ticks */uint32_t ticks_periodic_interval; /**< 時(shí)間周期 */bool is_running; /**< True代表運(yùn)行, False其他. */app_timer_timeout_handler_t p_timeout_handler; /**< 指向當(dāng)定時(shí)器倒是后調(diào)用的函數(shù) */void * p_context; /**<通用目標(biāo)指針. 當(dāng)定時(shí)器到時(shí)時(shí),將進(jìn)行超時(shí)處理。 */app_timer_id_t next; /**<下一個(gè)運(yùn)行定時(shí)器的id*/ } timer_node_t;

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ā)送事件。

uint32_t app_timer_init(uint32_t prescaler,//預(yù)分頻器uint8_t max_timers,//最大時(shí)間uint8_t op_queues_size,void * p_buffer,app_timer_evt_schedule_func_t evt_schedule_func) {int i;// 檢查緩沖區(qū)是否正確字對齊if (!is_word_aligned(p_buffer)){return NRF_ERROR_INVALID_PARAM;}if (p_buffer == NULL)// 檢查空緩沖區(qū){return NRF_ERROR_INVALID_PARAM;}rtc1_stop(); // RTC停止防止定時(shí)器時(shí)間移除后重新初始化m_evt_schedule_func = evt_schedule_func;//如果有調(diào)度則:app_timer_evt_schedule// Initialize timer node array初始化定時(shí)器節(jié)點(diǎn)數(shù)組APP_TIMER_BUFm_node_array_size = max_timers;mp_nodes = p_buffer;for (i = 0; i < max_timers; i++){mp_nodes[i].state = STATE_FREE;mp_nodes[i].is_running = false;}// Skip timer node arrayp_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)];// Initialize users arraym_user_array_size = APP_TIMER_INT_LEVELS;mp_users = p_buffer;// Skip user arrayp_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)];// 初始化 operation隊(duì)列for (i = 0; i < APP_TIMER_INT_LEVELS; i++){timer_user_t * p_user = &mp_users[i];p_user->first = 0;p_user->last = 0;p_user->user_op_queue_size = op_queues_size;p_user->p_user_op_queue = p_buffer;// Skip operation queuep_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)];}m_timer_id_head = TIMER_NULL;m_ticks_elapsed_q_read_ind = 0;m_ticks_elapsed_q_write_ind = 0;NVIC_ClearPendingIRQ(SWI0_IRQn);NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI);NVIC_EnableIRQ(SWI0_IRQn);rtc1_init(prescaler);m_ticks_latest = rtc1_counter_get();return NRF_SUCCESS; }

定時(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ù)
從中斷處理這里開始說起。

void RTC1_IRQHandler(void) {// 清除事件NRF_RTC1->EVENTS_COMPARE[0] = 0;NRF_RTC1->EVENTS_COMPARE[1] = 0;NRF_RTC1->EVENTS_COMPARE[2] = 0;NRF_RTC1->EVENTS_COMPARE[3] = 0;NRF_RTC1->EVENTS_TICK = 0;NRF_RTC1->EVENTS_OVRFLW = 0;timer_timeouts_check();// 檢測是否有到時(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í)器更新

void SWI0_IRQHandler(void) {timer_list_handler(); }static void timer_list_handler(void) {app_timer_id_t restart_list_head = TIMER_NULL;uint32_t ticks_elapsed;uint32_t ticks_previous;bool ticks_have_elapsed;bool compare_update;app_timer_id_t timer_id_head_old;// 備份上一次已知的tick和List頭ticks_previous = m_ticks_latest;timer_id_head_old = m_timer_id_head;// 獲得過去的ticks數(shù)ticks_have_elapsed = elapsed_ticks_acquire(&ticks_elapsed);// 處理鏈表缺失compare_update = list_deletions_handler();//處理到時(shí)間的定時(shí)器if (ticks_have_elapsed){expired_timers_handler(ticks_elapsed, ticks_previous, &restart_list_head);compare_update = true;}// 處理插入列表if (list_insertions_handler(restart_list_head)){compare_update = true;}// 必要時(shí)更新比較寄存器if (compare_update){compare_reg_update(timer_id_head_old);} }

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ù)。

static void buttons_init(void) {// Note: Array must be static because a pointer to it will be saved in the Button handler// module.static app_button_cfg_t buttons[] ={{WAKEUP_BUTTON_PIN, false, BUTTON_PULL, NULL},{LEDBUTTON_BUTTON_PIN_NO, false, BUTTON_PULL, button_event_handler}};APP_BUTTON_INIT(buttons, sizeof(buttons) / sizeof(buttons[0]), BUTTON_DETECTION_DELAY, true); } #define APP_BUTTON_INIT(BUTTONS, BUTTON_COUNT, DETECTION_DELAY, USE_SCHEDULER) \do \{ \uint32_t ERR_CODE = app_button_init((BUTTONS), \(BUTTON_COUNT), \(DETECTION_DELAY), \(USE_SCHEDULER) ? app_button_evt_schedule : NULL); \APP_ERROR_CHECK(ERR_CODE); \} while (0)

同樣,初始化中設(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, &current_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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。