BLE-NRF51822教程4-串口BLE解析
本講逐行代碼解析官方串口BLE例程demo
PS:?基于SDK5.1
?
主要分一下幾個部分:
1?:Main函數的整體注釋
2?:函數單獨解析。
3?:接收串口數據并發送給對端設備
4?:接收手機數據并通過串口打印
?
Ps?:第一和第二部分我在教程工程初始化流程中已經詳細說明這里直接復制過來,做了一些修改以及添加了關于添加服務和添加特征值的講解,如果之前看過可以直接看下?2函數單獨解析中的 服務初始化后面添加的內容即可
一:main函數整體注釋:
int main(void)
{
?//初始化LED指示燈,用來指示廣播和連接狀態
leds_init();
//初始化軟件定時器模塊
timers_init();
//設置按鍵作為?DETECT signal 用來喚醒system off模式,具體參看數據手冊power?章節
buttons_init();
//主要設置uart的引腳,波特率。接收,發送中斷等。并開啟uart模塊中斷
uart_init();
//協議棧初試化,設置時鐘,demo里面設置為外部時鐘。并且注冊事件派發函數
ble_stack_init();
//GAP一些參數的設置,設置設備名,設置PPCP(外圍設備首選鏈接參數)。(手機連上某個藍牙設備后可以從Generic Access Service中看到設置的這些參數)
gap_params_init();
//服務初始化。添加uart的串口服務。主要提供兩個特征值來供手機和板子以及電腦的通信
services_init();
//設置廣播數據以及掃描響應數據
advertising_init();
//鏈接參數設置。主要設置什么時候發起更新鏈接參數請求以及間隔和最大嘗試次數。
conn_params_init();
//安全參數初始化。
sec_params_init();
? simple_uart_putstring(START_STRING);
//設置廣播類型,白名單,間隔,超時等特性。并開始廣播。
advertising_start();
for (;;)
{
//電源管理,調用arm0的指令__WFE();進入睡眠
power_manage();
}
}
二:函數單獨解析:
1 leds_init
static void leds_init(void)
{
nrf_gpio_cfg_output(ADVERTISING_LED_PIN_NO);
nrf_gpio_cfg_output(CONNECTED_LED_PIN_NO);
}
設置的PIN_CONFIG寄存器使能兩個引腳的作為輸出功能。用來當做指示燈指示廣播和鏈接的狀態。
2 timers_init
static void timers_init(void)
{
// Initialize timer module
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, ? APP_TIMER_OP_QUEUE_SIZE,?false);
}
初始化軟件定時器模塊,該定時器模塊并不是使用timer0-2來實現定時功能。而是使用51822中的RTC1?來軟件模擬出定時器模塊。RTC1使用32.768K時鐘經過分頻后是時鐘來作為時鐘源。所以該函數內部實現就是設置RTC1相關的寄存器和做一些初始化。其原理和timer?定時/計數器模塊類似。具體細節參考芯片數據手冊。
?
APP_TIMER_PRESCALER:設置分頻系數。(以32.768K來分頻)
APP_TIMER_MAX_TIMERS:設置可以創建的最大定時器個數
APP_TIMER_OP_QUEUE_SIZE:定時器操作隊列,因為是用RTC模擬的軟件定時器,因此內部?????????????????????????????????????????????????????? ?????????是維護了一個軟件定時器的操作隊列
False:不使用調度,調度模塊沒有細看。51822關于調度的很多都是傳False不使用調?????????????????????度。??????????????????
3buttons_init
static void buttons_init(void)
{
nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
???????????????????????????? BUTTON_PULL,
???????????????????????????? NRF_GPIO_PIN_SENSE_LOW);???
}
這里的按鍵設置比較簡單,主要通過PIN_CNF寄存器來設置一個IO口來作為來作為sensing mechanism機制的引腳。這里是設置了WAKEUP_BUTTON_PIN這個引腳來作為這個功能,設置成低電平時觸發這個機制。而這個機制類似一個wakeup機制,當其被觸發時會產生一個DETECT signal而這個信號會將cpu從system off模式中喚醒。
?
4? uart_init
static void uart_init(void)
{
simple_uart_config(RTS_PIN_NUMBER, TX_PIN_NUMBER, CTS_PIN_NUMBER, RX_PIN_NUMBER, HWFC);
?
??? NRF_UART0->INTENSET = UART_INTENSET_RXDRDY_Enabled<<uart_intenset_rxdrdy_pos; </uart_intenset_rxdrdy_pos;<>
?
NVIC_SetPriority(UART0_IRQn, APP_IRQ_PRIORITY_LOW);
NVIC_EnableIRQ(UART0_IRQn);
??? /**@snippet [UART Initialization] */
}
初始化uart設置輸入輸出引腳,是否關閉流控。一般使用官方例子的時候都要先將流控關掉,HWFC為False。然后打開uart的接收中斷,打開uart模塊的中斷功能,以及設置優先級。?????????波特率在simple_uart_config中設置,該函數設置完引腳后使能uart,開啟uart的接收和發送功能。
?
5?? ble_stack_init
static void ble_stack_init(void)
{
??? // Initialize SoftDevice.
??? SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
?
??? // Subscribe for BLE events.
??? uint32_t err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
??? APP_ERROR_CHECK(err_code);
}
設置LFCLK(32.768K)的時鐘源(協議棧需要使用),這里設置為外部晶振。False為不使用調度。softdevice_ble_evt_handler_set(ble_evt_dispatch);注冊事件派發程序,基礎1-協議棧概述說明過,當BLE收到廣播,鏈接請求,對端設備數據等后底層處理完會上拋給上冊app一個事件,這個事件的上拋過程是協議棧觸發SWI中斷,在中斷內部將事件放入隊列,然后調用app中的SWI中斷。App中的SWI中斷會get隊列中的事件,并最終會調用注冊的ble_evt_dispatch函數,這個函數再將事件發給各個服務以及模塊的事件處理函數來處理各個服務及模塊自己感興趣的事件。相關原理基礎1-協議棧概述視頻教程中有說明。
?
6gap_params_init
設置必要的設備的GAP參數。
static void gap_params_init(void)
{
??? uint32_t??????????????? err_code;
????ble_gap_conn_params_tgap_conn_params;
????ble_gap_conn_sec_mode_tsec_mode;
?
//設置設備名的寫權限為普通模式,則手機掃描到設備連接上后可以在第一個服務Geneic Access Service(有的只顯示UUID為1800)中改寫Device name.(有的app可能本身未實現改寫功能)
? ? ?BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
//設置設備名,該設備名就是在手機app掃描藍牙設備時顯示的名字。
????err_code = sd_ble_gap_device_name_set(&sec_mode,(const uint8_t *) DEVICE_NAME,strlen(DEVICE_NAME));
??? APP_ERROR_CHECK(err_code);
?
????memset(&gap_conn_params, 0, sizeof(gap_conn_params));
//設置外圍設備連接首選參數。同device name一樣,手機連上某個藍牙設備后可以從Generic Access Service中看到設置的這些參數。這個參數主要是讓中央設備在首次連接外設時可以讀取他們以及時調整連接參數。或者當中央設備以后重連該外設,并且之前保留了這些參數那么就免去了連接后可能需要的修改連接參數的麻煩。
//當然,外圍設備也可以之后通過sd_ble_gap_ppcp_get來獲取之前設置的參數然后通過連接參數跟新請求函數向中央設備請求更改連接參數。
????gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
????gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
????gap_conn_params.slave_latency???? = SLAVE_LATENCY;
????gap_conn_params.conn_sup_timeout? = CONN_SUP_TIMEOUT;
?
????err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
??? APP_ERROR_CHECK(err_code);
}
?
?
?
7 services_init
static void services_init(void)
{
??? uint32_t???????? err_code;
ble_nus_init_tnus_init;
?
memset(&nus_init, 0, sizeof(nus_init));
????????
//注冊數據處理函數,這里處理的數據是收到手機發來的數據
// nus_data_handler就是將板子收到的數據通過串口打印到電腦上
//實現了手機->開發板->電腦方向的數據流傳輸。
nus_init.data_handler = nus_data_handler;
?
err_code =ble_nus_init(&m_nus, &nus_init);
??? APP_ERROR_CHECK(err_code);
}
7.1?ble_nus_init該函數中實現添加服務以及添加特征值
uint32_t ble_nus_init(ble_nus_t * p_nus, constble_nus_init_t * p_nus_init)
{
uint32_t??????? err_code;
ble_uuid_tble_uuid;
//設置基準uuid
ble_uuid128_t?? nus_base_uuid = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, ??????????????????????????????????????????????? ?? 0xE0,0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E};
?
if ((p_nus == NULL) || (p_nus_init == NULL))
??? {
return NRF_ERROR_NULL;
??? }
?
?//?初始化連接句柄,因為現在并未與手機連接所以先賦值無效。
?? //賦值數據處理函數,就是上面剛提到的打印收到的手機數據
?? //設置notify是否使能的標志量,該標志量在手機連上板子并且使能了具?????????????????????????????? //有notfify的特征值時(這里是rx特征值后面會講到),該標志會被設??????????????????????????????????? //????置。這個標志量僅僅只是一個類似flag的作用,甚至可能并未被
??????????? //?用到。
p_nus->conn_handle????????????? = BLE_CONN_HANDLE_INVALID;
p_nus->data_handler???????????? = p_nus_init->data_handler;
p_nus->is_notification_enabled? ??? = false;
?
??? //?因為是自己定義的uuid,所以需要調用該函數來賦值p_nus->uuid_type
??????????? //該函數會將這個nus_base_uuid放到協議棧內部的表中
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);
if (err_code != NRF_SUCCESS)
??? {
returnerr_code;
??? }
?
??????????? //設置服務uuid以及uuid_type(就是上面調用的函數或得的)
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
?
??? //?到這里就添加服務到協議棧內部表中了
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_nus->service_handle);
if (err_code != NRF_SUCCESS)
?{
??????????? returnerr_code;
?}
?
??? //?一個服務通常有幾個特征值
??????????? //這里在上面注冊的服務中添加了兩個特征值。
err_code =?rx_char_add(p_nus, p_nus_init);
if (err_code != NRF_SUCCESS)
??? {
returnerr_code;
??? }
?
??? // Add TX Characteristic.
err_code = tx_char_add(p_nus, p_nus_init);
if (err_code != NRF_SUCCESS)
??? {
returnerr_code;
??? }
return NRF_SUCCESS;
}
7.1.1? rx_char_add
這個特征用來將板子從串口收到的數據通過該特征值使用notify方式發送給手機
代碼太長截圖注釋:
PS:后面標記寫的有點問題。是設置讀寫不需要加密或MITM(其實就是設置安全模式和等級)
7.1.2tx_char_add
這個添加的特征值用來接收手機發送給板子的數據。
和Rx?特征值的設置基本一致,只是將notify?功能的設置去掉了改成了設置成可寫。其他的代碼基本是一樣的。這里就不貼代碼了。
?
8 advertising_init
廣播參數的初始化
static void advertising_init(void)
{
??? uint32_t????? err_code;
????ble_advdata_tadvdata;
????ble_advdata_tscanrsp;
//該標志主要設置廣播類型為有限可發現模式,并且設置不支持經典藍牙
//相比于一般可發現模式的廣播,有限可發現模式的廣播平率更快,但是只能最多維持?//30s
????uint8_t?????? flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
?
???????? //設置需要廣播的uuid,就是上面主測的服務uuid
????ble_uuid_tadv_uuids[] = {{BLE_UUID_NUS_SERVICE, m_nus.uuid_type}};
?
?
???????? //這里設置廣播的名字為全名,設置標志,就是上面提到的。
???????? //appearance為”外觀”,他就是一個整形值,代表設備是一個手環,手機什么的。
????memset(&advdata, 0, sizeof(advdata));
????advdata.name_type?????????????? = BLE_ADVDATA_FULL_NAME;
????advdata.include_appearance????? = false;
????advdata.flags.size????????????? = sizeof(flags);
????advdata.flags.p_data??????????? = &flags;
?
???????? //這里設置的是掃描響應數據。該數據在設備收到掃描請求的時候才會發出去。
???????? //有時候需要廣播的數據可能太多,廣播包中放不下,那么就可以放在掃描響應
???????? //數據中,這樣對端設備便可以通過掃描請求來或得剩下的數據。
????memset(&scanrsp, 0, sizeof(scanrsp));
????scanrsp.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
????scanrsp.uuids_complete.p_uuids? =adv_uuids;
?
????err_code = ble_advdata_set(&advdata, &scanrsp);
??? APP_ERROR_CHECK(err_code);
}
?
9 conn_params_init
設置連接參數
static void conn_params_init(void)
{
??? uint32_t?????????????? err_code;
????ble_conn_params_init_tcp_init;
?
????memset(&cp_init, 0, sizeof(cp_init));
?
???????? //這里連接參數設置為NULL的原因是前面的gap_params_init函數中已經設置了連接?????? //參數并調用了sd_ble_gap_ppcp_set將參數設置到了協議棧中。所以這里既是不設置,
???????? //下面的ble_conn_params_init會自動判斷是否為空,為空就調用提取函數,從協議棧
???????? //中提取之前注冊的參數。
cp_init.p_conn_params????????????????? = NULL;
//下面主要是設置一些連接參數更新的事件,以及更新周期和最大最大嘗試更新次數。
//部分參數不好描述,視頻中會說明。
????cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
????cp_init.next_conn_params_update_delay? = NEXT_CONN_PARAMS_UPDATE_DELAY;
????cp_init.max_conn_params_update_count?? = MAX_CONN_PARAMS_UPDATE_COUNT;
????cp_init.start_on_notify_cccd_handle??? = BLE_GATT_HANDLE_INVALID;
????cp_init.disconnect_on_fail???????????? = false;
????cp_init.evt_handler??????????????????? = on_conn_params_evt;
????cp_init.error_handler????????????????? = conn_params_error_handler;
?
????err_code =ble_conn_params_init(&cp_init);
??? APP_ERROR_CHECK(err_code);
}
10 sec_params_init
安全參數的初始化。主要設置
超時時間:比如配對過程中某一步的確認超過這個時間還未收到那么便是超時。APP會收到SD上拋的狀態事件,狀態為超時
Bond:?是否綁定。如果需要綁定,配對過程會有第三步的秘鑰分發,然后app將秘鑰存儲在falsh這樣下次就可以避免了下次重復配對的過程。
MITM:?是否需要中間人保護。
Io_caps:本設備的I/O能力。比如有顯示屏,有鍵盤。
?
:當使能了MITM?并且兩端設備一個有鍵盤,一個有顯示屏時,配對過程中就會顯示一個配對碼,對端設備通過鍵盤再輸入。
如果沒有MITM保護配對過程中的信息是很容易被監聽到的。但是如果有了MITM因為這個配對碼信息是一端顯示一端輸入,并不會通過鏈路傳輸。因為除了兩端設備不會有第三個設備知道。因此后續的鏈路加密就很難被破解。
OOB:與MITM類似,只是配對碼不是通過鍵盤輸入而是通過兩端設備別的通信通道傳輸,比如NFC,當然前提是該通信鏈路是安全的。不如也沒必要繞個彎而不直接用BLE來傳輸了。
后面就是設置加密秘鑰的最大和最小值。加密秘鑰的大小在7-16字節之間
?
配對的過程相對比較復雜,這里不做理論解釋。后期需要的話會單獨做一片配對的詳細教程,群文件中有我上傳了一個作為從機的配對歷程也是基于uart,當主機在使能有第一個特征值的notify時便會觸發配對,配對碼是通過串口打印的。使用的隨機產生的。當然也可以設置為靜態的。
?
void sec_params_init(void)
{
??? m_sec_params.timeout????? = SEC_PARAM_TIMEOUT;
??? m_sec_params.bond???????? = SEC_PARAM_BOND;
??? m_sec_params.mitm???????? = SEC_PARAM_MITM;
??? m_sec_params.io_caps ?????= SEC_PARAM_IO_CAPABILITIES;
??? m_sec_params.oob????????? = SEC_PARAM_OOB;?
??? m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
??? m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}
?
11 advertising_start
static void advertising_start(void)
{
??? uint32_t???????????? err_code;
??? ble_gap_adv_params_t adv_params;
?
??? memset(&adv_params, 0, sizeof(adv_params));
?
???????? //設置廣播類型為通用廣播.
????????廣播類型有四種:
通用廣播:用途最廣的廣播方式。可以被掃描到,以及可以被連接
定向廣播:用來快速建立和目標設備建立連接。報文中包含自己以及目標地址。
不可連接廣播:只廣播數據,不可以被掃描以及連接。
可發現廣播;可以被掃描(回復掃描響應數據),不可以被連接。
?
adv_params.type??????? = BLE_GAP_ADV_TYPE_ADV_IND;
//如果廣播方式為定向廣播,這里添目標設備的地址
adv_params.p_peer_addr = NULL;?????????
//設置過濾規則。
//可設置為是否過濾掉非白名單中的掃描請以及非白名單中的連接請求或者兩者都過濾。
??? adv_params.fp????????? = BLE_GAP_ADV_FP_ANY;
//設置廣播間隔和廣播超時,超時時間到期如果設備還未連接那么app會收到協議棧上
//拋的廣播超時時間。App可以做自己想做的處理,比如讓設備進入睡眠。
adv_params.interval??? = APP_ADV_INTERVAL;
??? adv_params.timeout???? = APP_ADV_TIMEOUT_IN_SECONDS;
//開啟廣播
??? err_code = sd_ble_gap_adv_start(&adv_params);
??? APP_ERROR_CHECK(err_code);
?
??? nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);
}
?
三接收串口數據并發送給對端設備
上面介紹的整個初始化完成后,設備便進入睡眠模式,每當廣播間隔到期會發送一次廣播。直到有設備發來連接請求,當設備連接上手機后邊繼續處于睡眠狀態等待”事件”的發生
?
先來分析電腦à開發板à手機方向的數據流
在main?函數的串口初始化程序uart_init的最后打開了串口的接收中斷。
那么這個方向的數據流的起點就是在串口中斷中收到電腦上發來的數據為起點
?
Uart中斷函數在main函數上方
void UART0_IRQHandler(void)
{
??? static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
??? static uint8_t index = 0;
??? uint32_t err_code;
???????? uint8_t temp;
????????
???????? //取得電腦串口發過來的數據
??? data_array[index] = simple_uart_get();
??? index++;
???????? //判斷串口發送給來的數據是否達到20的字節,或者是不是發送了字母’q’。如果滿足
???????? //調用發送函數將收到的串口數據發送給手機。否則不發送等待知道滿足條件。
??? // (這里通常新手說手機收不到數據的原因,因為沒輸入達到20個字節)
??? if ((data_array[index - 1] == 'q') || (index >= (BLE_NUS_MAX_DATA_LEN - 1)))
??? {
??????? err_code =?ble_nus_send_string(&m_nus, data_array, index + 1);
??????? if (err_code != NRF_ERROR_INVALID_STATE)
??????? {
??????????? APP_ERROR_CHECK(err_code);
??????? }
//發送了數據后清零數組下標。以繼續緩存后續的串口數據。
??????? index = 0;
??? }
}
?
再來看看發送數據給手機的函數ble_nus_send_string
?
uint32_t ble_nus_send_string(ble_nus_t * p_nus, uint8_t * string, uint16_t length)
{
??? ble_gatts_hvx_params_t hvx_params;
?
??? if (p_nus == NULL)
??? {
??????? return NRF_ERROR_NULL;
??? }
//這里是檢測參數是否正確。是否是已經連接上了手機?(只有連接后,conn_handle才會
?? //?被賦值為有效值),檢查手機是否使能了開發板的通知,因為開發板作為服務端向手機
???????? //發送數據時通過通知或指示兩種方式,這兩種方式都需要手機先使能開發板。
?? if((p_nus->conn_handle==BLE_CONN_HANDLE_INVALID)||(!p_nus->is_notification_enabled))
??? {
??????? return NRF_ERROR_INVALID_STATE;
??? }
???????? //一次發送的長度不能超過限定值20
??? if (length > BLE_NUS_MAX_DATA_LEN)
??? {
??????? return NRF_ERROR_INVALID_PARAM;
??? }
?
??? memset(&hvx_params, 0, sizeof(hvx_params));
????????
???????? //以為是通過Rx這個參數來發送數據給手機的,所以句柄要填rx的句柄
???????? //這個句柄是在上面的服務初始化函數中的添加特征值函數調用完畢后或得的(最后一???? //?個參數為返回的句柄)
???????? //然后就是賦值要發送的數據,并且設置為notify方式
??? hvx_params.handle = p_nus->rx_handles.value_handle;
??? hvx_params.p_data = string;
??? hvx_params.p_len? = &length;
??? hvx_params.type?? = BLE_GATT_HVX_NOTIFICATION;
//發送函數
??? return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
}
?
?
?
看到這里應該對電腦-》板子-》手機的數據流有一個認識。在討論另一個方向的數據傳輸過程。我們先來看一個關于連接的問題。
我們調用sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);發送數據給手機的時候,第二個參數是上面賦值的,那第一個參數這個連接句柄是怎么回事?在哪里設置過他?
連接句柄你可以看做是信道標志一樣(實際數據接入地址),每兩個連接的設備都會具有這個連接句柄。他們后續的通信都是通過這個連接句柄來進行(可以理解是信道標志,兩個設備的通信標志必須一樣,這代表他們是在同樣的信道上通信才能正確進行通信)
?
上面我們說過,板子整個初始化流程走完后就是睡眠和廣播等待手機連接。那么這個conn_handle就一定是手機發來連接,板子中協議棧處理完后上拋給app的連接事件中賦值的。從而記錄下后續板子和手機通信的”信道”。 在?程序框架剖析??那一講中介紹過,協議棧拋上來的事件結構體最終是由dispatch這個派發程序發給再發給各個服務的事件處理函數和模塊的事件處理函數的。
?
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
???????? //將事件交給連接管理模塊的事件處理函數
ble_conn_params_on_ble_evt(p_ble_evt);
//將事件交給uart服務的事件處理函數
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
//處理一些一般的事件
??? on_ble_evt(p_ble_evt);
}
再進入?uart服務的事件處理函數中看下發生連接時是如何記下 后續通信所用的連接句柄的
這里只截取部分相關代碼
?
說完了連接句柄下面來說最后一個問題
手機-》板子—》電腦方向的數據處理過程。
四:接收手機數據并通過串口打印:
其實看完了上面關于連接句柄的記錄。再來理解怎么收到手機的數據就容易了。
因為我們說過,手機發送數據過來也是一個事件!
既然都是事件,那么傳遞流程一定是一樣的,只是在最后的處理上不同的事件不同的處理。
?
那么第一步一定是協議棧處理完收到的數據打包一個?“寫事件”?然后上拋給app。其實就是上拋給dispatch。然后在由它繼續分發事件
?
再進入函數內部:
再進入on_write函數內部一看究竟。
這里最終是調用了一個回調函數來數理最終的數據,那么這個回調函數是什么時候注冊的。在 第二部分 函數單獨解析的?services_init講解中說明過。
再來看看注冊的這個?nus_data_handler?到底干了什么
?
到這里手機->板子->電腦方向的數據流也理清了
整體的流程就是 手機發送數據給板子后,板子中低層的協議棧將收到的數據打包成一個寫事件結構體,然后上拋給app,最終由app種的diapatch再分發給各個服務或模塊的事件處理函數,而uart的事件處理函數收到寫事件后判斷是不是要打印到電腦上的”普通數據”,如果是就調用server_init中注冊的回調函數。該回調函數最終將數據打印到電腦上
總結
以上是生活随笔為你收集整理的BLE-NRF51822教程4-串口BLE解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BLE-NRF51822教程11-手机动
- 下一篇: BLE-NRF51822教程5-静态密码