日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

BLE-NRF51822教程4-串口BLE解析

發(fā)布時(shí)間:2025/3/21 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BLE-NRF51822教程4-串口BLE解析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本講逐行代碼解析官方串口BLE例程demo

PS:?基于SDK5.1

?

主要分一下幾個(gè)部分:

1?:Main函數(shù)的整體注釋

2?:函數(shù)單獨(dú)解析。

3?:接收串口數(shù)據(jù)并發(fā)送給對(duì)端設(shè)備

4?:接收手機(jī)數(shù)據(jù)并通過(guò)串口打印

?

Ps?:第一和第二部分我在教程工程初始化流程中已經(jīng)詳細(xì)說(shuō)明這里直接復(fù)制過(guò)來(lái),做了一些修改以及添加了關(guān)于添加服務(wù)和添加特征值的講解,如果之前看過(guò)可以直接看下?2函數(shù)單獨(dú)解析中的 服務(wù)初始化后面添加的內(nèi)容即可

一:main函數(shù)整體注釋:

int main(void)

{

?//初始化LED指示燈,用來(lái)指示廣播和連接狀態(tài)

leds_init();

//初始化軟件定時(shí)器模塊

timers_init();

//設(shè)置按鍵作為?DETECT signal 用來(lái)喚醒system off模式,具體參看數(shù)據(jù)手冊(cè)power?章節(jié)

buttons_init();

//主要設(shè)置uart的引腳,波特率。接收,發(fā)送中斷等。并開(kāi)啟uart模塊中斷

uart_init();

//協(xié)議棧初試化,設(shè)置時(shí)鐘,demo里面設(shè)置為外部時(shí)鐘。并且注冊(cè)事件派發(fā)函數(shù)

ble_stack_init();

//GAP一些參數(shù)的設(shè)置,設(shè)置設(shè)備名,設(shè)置PPCP(外圍設(shè)備首選鏈接參數(shù))。(手機(jī)連上某個(gè)藍(lán)牙設(shè)備后可以從Generic Access Service中看到設(shè)置的這些參數(shù))

gap_params_init();

//服務(wù)初始化。添加uart的串口服務(wù)。主要提供兩個(gè)特征值來(lái)供手機(jī)和板子以及電腦的通信

services_init();

//設(shè)置廣播數(shù)據(jù)以及掃描響應(yīng)數(shù)據(jù)

advertising_init();

//鏈接參數(shù)設(shè)置。主要設(shè)置什么時(shí)候發(fā)起更新鏈接參數(shù)請(qǐng)求以及間隔和最大嘗試次數(shù)。

conn_params_init();

//安全參數(shù)初始化。

sec_params_init();

? simple_uart_putstring(START_STRING);

//設(shè)置廣播類(lèi)型,白名單,間隔,超時(shí)等特性。并開(kāi)始廣播。

advertising_start();

for (;;)

{

//電源管理,調(diào)用arm0的指令__WFE();進(jìn)入睡眠

power_manage();

}

}

二:函數(shù)單獨(dú)解析:

1 leds_init

static void leds_init(void)

{

nrf_gpio_cfg_output(ADVERTISING_LED_PIN_NO);

nrf_gpio_cfg_output(CONNECTED_LED_PIN_NO);

}

設(shè)置的PIN_CONFIG寄存器使能兩個(gè)引腳的作為輸出功能。用來(lái)當(dāng)做指示燈指示廣播和鏈接的狀態(tài)。

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);

}

初始化軟件定時(shí)器模塊,該定時(shí)器模塊并不是使用timer0-2來(lái)實(shí)現(xiàn)定時(shí)功能。而是使用51822中的RTC1?來(lái)軟件模擬出定時(shí)器模塊。RTC1使用32.768K時(shí)鐘經(jīng)過(guò)分頻后是時(shí)鐘來(lái)作為時(shí)鐘源。所以該函數(shù)內(nèi)部實(shí)現(xiàn)就是設(shè)置RTC1相關(guān)的寄存器和做一些初始化。其原理和timer?定時(shí)/計(jì)數(shù)器模塊類(lèi)似。具體細(xì)節(jié)參考芯片數(shù)據(jù)手冊(cè)。

?

APP_TIMER_PRESCALER:設(shè)置分頻系數(shù)。(以32.768K來(lái)分頻)

APP_TIMER_MAX_TIMERS:設(shè)置可以創(chuàng)建的最大定時(shí)器個(gè)數(shù)

APP_TIMER_OP_QUEUE_SIZE:定時(shí)器操作隊(duì)列,因?yàn)槭怯肦TC模擬的軟件定時(shí)器,因此內(nèi)部?????????????????????????????????????????????????????? ?????????是維護(hù)了一個(gè)軟件定時(shí)器的操作隊(duì)列

False:不使用調(diào)度,調(diào)度模塊沒(méi)有細(xì)看。51822關(guān)于調(diào)度的很多都是傳False不使用調(diào)?????????????????????度。??????????????????

3buttons_init

static void buttons_init(void)

{

nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,

???????????????????????????? BUTTON_PULL,

???????????????????????????? NRF_GPIO_PIN_SENSE_LOW);???

}

這里的按鍵設(shè)置比較簡(jiǎn)單,主要通過(guò)PIN_CNF寄存器來(lái)設(shè)置一個(gè)IO口來(lái)作為來(lái)作為sensing mechanism機(jī)制的引腳。這里是設(shè)置了WAKEUP_BUTTON_PIN這個(gè)引腳來(lái)作為這個(gè)功能,設(shè)置成低電平時(shí)觸發(fā)這個(gè)機(jī)制。而這個(gè)機(jī)制類(lèi)似一個(gè)wakeup機(jī)制,當(dāng)其被觸發(fā)時(shí)會(huì)產(chǎn)生一個(gè)DETECT signal而這個(gè)信號(hào)會(huì)將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設(shè)置輸入輸出引腳,是否關(guān)閉流控。一般使用官方例子的時(shí)候都要先將流控關(guān)掉,HWFC為False。然后打開(kāi)uart的接收中斷,打開(kāi)uart模塊的中斷功能,以及設(shè)置優(yōu)先級(jí)。?????????波特率在simple_uart_config中設(shè)置,該函數(shù)設(shè)置完引腳后使能uart,開(kāi)啟uart的接收和發(fā)送功能。

?

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);

}

設(shè)置LFCLK(32.768K)的時(shí)鐘源(協(xié)議棧需要使用),這里設(shè)置為外部晶振。False為不使用調(diào)度。softdevice_ble_evt_handler_set(ble_evt_dispatch);注冊(cè)事件派發(fā)程序,基礎(chǔ)1-協(xié)議棧概述說(shuō)明過(guò),當(dāng)BLE收到廣播,鏈接請(qǐng)求,對(duì)端設(shè)備數(shù)據(jù)等后底層處理完會(huì)上拋給上冊(cè)app一個(gè)事件,這個(gè)事件的上拋過(guò)程是協(xié)議棧觸發(fā)SWI中斷,在中斷內(nèi)部將事件放入隊(duì)列,然后調(diào)用app中的SWI中斷。App中的SWI中斷會(huì)get隊(duì)列中的事件,并最終會(huì)調(diào)用注冊(cè)的ble_evt_dispatch函數(shù),這個(gè)函數(shù)再將事件發(fā)給各個(gè)服務(wù)以及模塊的事件處理函數(shù)來(lái)處理各個(gè)服務(wù)及模塊自己感興趣的事件。相關(guān)原理基礎(chǔ)1-協(xié)議棧概述視頻教程中有說(shuō)明。

?

6gap_params_init

設(shè)置必要的設(shè)備的GAP參數(shù)。

static void gap_params_init(void)

{

??? uint32_t??????????????? err_code;

????ble_gap_conn_params_tgap_conn_params;

????ble_gap_conn_sec_mode_tsec_mode;

?

//設(shè)置設(shè)備名的寫(xiě)權(quán)限為普通模式,則手機(jī)掃描到設(shè)備連接上后可以在第一個(gè)服務(wù)Geneic Access Service(有的只顯示UUID為1800)中改寫(xiě)Device name.(有的app可能本身未實(shí)現(xiàn)改寫(xiě)功能)

? ? ?BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

//設(shè)置設(shè)備名,該設(shè)備名就是在手機(jī)app掃描藍(lán)牙設(shè)備時(shí)顯示的名字。

????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));

//設(shè)置外圍設(shè)備連接首選參數(shù)。同device name一樣,手機(jī)連上某個(gè)藍(lán)牙設(shè)備后可以從Generic Access Service中看到設(shè)置的這些參數(shù)。這個(gè)參數(shù)主要是讓中央設(shè)備在首次連接外設(shè)時(shí)可以讀取他們以及時(shí)調(diào)整連接參數(shù)。或者當(dāng)中央設(shè)備以后重連該外設(shè),并且之前保留了這些參數(shù)那么就免去了連接后可能需要的修改連接參數(shù)的麻煩。

//當(dāng)然,外圍設(shè)備也可以之后通過(guò)sd_ble_gap_ppcp_get來(lái)獲取之前設(shè)置的參數(shù)然后通過(guò)連接參數(shù)跟新請(qǐng)求函數(shù)向中央設(shè)備請(qǐng)求更改連接參數(shù)。

????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));

????????

//注冊(cè)數(shù)據(jù)處理函數(shù),這里處理的數(shù)據(jù)是收到手機(jī)發(fā)來(lái)的數(shù)據(jù)

// nus_data_handler就是將板子收到的數(shù)據(jù)通過(guò)串口打印到電腦上

//實(shí)現(xiàn)了手機(jī)->開(kāi)發(fā)板->電腦方向的數(shù)據(jù)流傳輸。

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該函數(shù)中實(shí)現(xiàn)添加服務(wù)以及添加特征值

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;

//設(shè)置基準(zhǔn)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;

??? }

?

?//?初始化連接句柄,因?yàn)楝F(xiàn)在并未與手機(jī)連接所以先賦值無(wú)效。

?? //賦值數(shù)據(jù)處理函數(shù),就是上面剛提到的打印收到的手機(jī)數(shù)據(jù)

?? //設(shè)置notify是否使能的標(biāo)志量,該標(biāo)志量在手機(jī)連上板子并且使能了具?????????????????????????????? //有notfify的特征值時(shí)(這里是rx特征值后面會(huì)講到),該標(biāo)志會(huì)被設(shè)??????????????????????????????????? //????置。這個(gè)標(biāo)志量?jī)H僅只是一個(gè)類(lèi)似flag的作用,甚至可能并未被

??????????? //?用到。

p_nus->conn_handle????????????? = BLE_CONN_HANDLE_INVALID;

p_nus->data_handler???????????? = p_nus_init->data_handler;

p_nus->is_notification_enabled? ??? = false;

?

??? //?因?yàn)槭亲约憾x的uuid,所以需要調(diào)用該函數(shù)來(lái)賦值p_nus->uuid_type

??????????? //該函數(shù)會(huì)將這個(gè)nus_base_uuid放到協(xié)議棧內(nèi)部的表中

err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);

if (err_code != NRF_SUCCESS)

??? {

returnerr_code;

??? }

?

??????????? //設(shè)置服務(wù)uuid以及uuid_type(就是上面調(diào)用的函數(shù)或得的)

ble_uuid.type = p_nus->uuid_type;

ble_uuid.uuid = BLE_UUID_NUS_SERVICE;

?

??? //?到這里就添加服務(wù)到協(xié)議棧內(nèi)部表中了

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;

?}

?

??? //?一個(gè)服務(wù)通常有幾個(gè)特征值

??????????? //這里在上面注冊(cè)的服務(wù)中添加了兩個(gè)特征值。

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

這個(gè)特征用來(lái)將板子從串口收到的數(shù)據(jù)通過(guò)該特征值使用notify方式發(fā)送給手機(jī)

代碼太長(zhǎng)截圖注釋:


PS:后面標(biāo)記寫(xiě)的有點(diǎn)問(wèn)題。是設(shè)置讀寫(xiě)不需要加密或MITM(其實(shí)就是設(shè)置安全模式和等級(jí))

7.1.2tx_char_add

這個(gè)添加的特征值用來(lái)接收手機(jī)發(fā)送給板子的數(shù)據(jù)。

和Rx?特征值的設(shè)置基本一致,只是將notify?功能的設(shè)置去掉了改成了設(shè)置成可寫(xiě)。其他的代碼基本是一樣的。這里就不貼代碼了。

?

8 advertising_init

廣播參數(shù)的初始化

static void advertising_init(void)

{

??? uint32_t????? err_code;

????ble_advdata_tadvdata;
????ble_advdata_tscanrsp;

//該標(biāo)志主要設(shè)置廣播類(lèi)型為有限可發(fā)現(xiàn)模式,并且設(shè)置不支持經(jīng)典藍(lán)牙

//相比于一般可發(fā)現(xiàn)模式的廣播,有限可發(fā)現(xiàn)模式的廣播平率更快,但是只能最多維持?//30s

????uint8_t?????? flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;

?

???????? //設(shè)置需要廣播的uuid,就是上面主測(cè)的服務(wù)uuid

????ble_uuid_tadv_uuids[] = {{BLE_UUID_NUS_SERVICE, m_nus.uuid_type}};

?

?

???????? //這里設(shè)置廣播的名字為全名,設(shè)置標(biāo)志,就是上面提到的。

???????? //appearance為”外觀”,他就是一個(gè)整形值,代表設(shè)備是一個(gè)手環(huán),手機(jī)什么的。

????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;

?

???????? //這里設(shè)置的是掃描響應(yīng)數(shù)據(jù)。該數(shù)據(jù)在設(shè)備收到掃描請(qǐng)求的時(shí)候才會(huì)發(fā)出去。

???????? //有時(shí)候需要廣播的數(shù)據(jù)可能太多,廣播包中放不下,那么就可以放在掃描響應(yīng)

???????? //數(shù)據(jù)中,這樣對(duì)端設(shè)備便可以通過(guò)掃描請(qǐng)求來(lái)或得剩下的數(shù)據(jù)。

????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

設(shè)置連接參數(shù)

static void conn_params_init(void)

{

??? uint32_t?????????????? err_code;

????ble_conn_params_init_tcp_init;

?

????memset(&cp_init, 0, sizeof(cp_init));

?

???????? //這里連接參數(shù)設(shè)置為NULL的原因是前面的gap_params_init函數(shù)中已經(jīng)設(shè)置了連接?????? //參數(shù)并調(diào)用了sd_ble_gap_ppcp_set將參數(shù)設(shè)置到了協(xié)議棧中。所以這里既是不設(shè)置,

???????? //下面的ble_conn_params_init會(huì)自動(dòng)判斷是否為空,為空就調(diào)用提取函數(shù),從協(xié)議棧

???????? //中提取之前注冊(cè)的參數(shù)。

cp_init.p_conn_params????????????????? = NULL;

//下面主要是設(shè)置一些連接參數(shù)更新的事件,以及更新周期和最大最大嘗試更新次數(shù)。

//部分參數(shù)不好描述,視頻中會(huì)說(shuō)明。

????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

安全參數(shù)的初始化。主要設(shè)置

超時(shí)時(shí)間:比如配對(duì)過(guò)程中某一步的確認(rèn)超過(guò)這個(gè)時(shí)間還未收到那么便是超時(shí)。APP會(huì)收到SD上拋的狀態(tài)事件,狀態(tài)為超時(shí)

Bond:?是否綁定。如果需要綁定,配對(duì)過(guò)程會(huì)有第三步的秘鑰分發(fā),然后app將秘鑰存儲(chǔ)在falsh這樣下次就可以避免了下次重復(fù)配對(duì)的過(guò)程。

MITM:?是否需要中間人保護(hù)。

Io_caps:本設(shè)備的I/O能力。比如有顯示屏,有鍵盤(pán)。

?

:當(dāng)使能了MITM?并且兩端設(shè)備一個(gè)有鍵盤(pán),一個(gè)有顯示屏?xí)r,配對(duì)過(guò)程中就會(huì)顯示一個(gè)配對(duì)碼,對(duì)端設(shè)備通過(guò)鍵盤(pán)再輸入。

如果沒(méi)有MITM保護(hù)配對(duì)過(guò)程中的信息是很容易被監(jiān)聽(tīng)到的。但是如果有了MITM因?yàn)檫@個(gè)配對(duì)碼信息是一端顯示一端輸入,并不會(huì)通過(guò)鏈路傳輸。因?yàn)槌藘啥嗽O(shè)備不會(huì)有第三個(gè)設(shè)備知道。因此后續(xù)的鏈路加密就很難被破解。

OOB:與MITM類(lèi)似,只是配對(duì)碼不是通過(guò)鍵盤(pán)輸入而是通過(guò)兩端設(shè)備別的通信通道傳輸,比如NFC,當(dāng)然前提是該通信鏈路是安全的。不如也沒(méi)必要繞個(gè)彎而不直接用BLE來(lái)傳輸了。

后面就是設(shè)置加密秘鑰的最大和最小值。加密秘鑰的大小在7-16字節(jié)之間

?

配對(duì)的過(guò)程相對(duì)比較復(fù)雜,這里不做理論解釋。后期需要的話會(huì)單獨(dú)做一片配對(duì)的詳細(xì)教程,群文件中有我上傳了一個(gè)作為從機(jī)的配對(duì)歷程也是基于uart,當(dāng)主機(jī)在使能有第一個(gè)特征值的notify時(shí)便會(huì)觸發(fā)配對(duì),配對(duì)碼是通過(guò)串口打印的。使用的隨機(jī)產(chǎn)生的。當(dāng)然也可以設(shè)置為靜態(tài)的。

?

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));

?

???????? //設(shè)置廣播類(lèi)型為通用廣播.

????????廣播類(lèi)型有四種:

通用廣播:用途最廣的廣播方式。可以被掃描到,以及可以被連接

定向廣播:用來(lái)快速建立和目標(biāo)設(shè)備建立連接。報(bào)文中包含自己以及目標(biāo)地址。

不可連接廣播:只廣播數(shù)據(jù),不可以被掃描以及連接。

可發(fā)現(xiàn)廣播;可以被掃描(回復(fù)掃描響應(yīng)數(shù)據(jù)),不可以被連接。

?

adv_params.type??????? = BLE_GAP_ADV_TYPE_ADV_IND;

//如果廣播方式為定向廣播,這里添目標(biāo)設(shè)備的地址

adv_params.p_peer_addr = NULL;?????????

//設(shè)置過(guò)濾規(guī)則。

//可設(shè)置為是否過(guò)濾掉非白名單中的掃描請(qǐng)以及非白名單中的連接請(qǐng)求或者兩者都過(guò)濾。

??? adv_params.fp????????? = BLE_GAP_ADV_FP_ANY;

//設(shè)置廣播間隔和廣播超時(shí),超時(shí)時(shí)間到期如果設(shè)備還未連接那么app會(huì)收到協(xié)議棧上

//拋的廣播超時(shí)時(shí)間。App可以做自己想做的處理,比如讓設(shè)備進(jìn)入睡眠。

adv_params.interval??? = APP_ADV_INTERVAL;

??? adv_params.timeout???? = APP_ADV_TIMEOUT_IN_SECONDS;

//開(kāi)啟廣播

??? err_code = sd_ble_gap_adv_start(&adv_params);

??? APP_ERROR_CHECK(err_code);

?

??? nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);

}

?

三接收串口數(shù)據(jù)并發(fā)送給對(duì)端設(shè)備

上面介紹的整個(gè)初始化完成后,設(shè)備便進(jìn)入睡眠模式,每當(dāng)廣播間隔到期會(huì)發(fā)送一次廣播。直到有設(shè)備發(fā)來(lái)連接請(qǐng)求,當(dāng)設(shè)備連接上手機(jī)后邊繼續(xù)處于睡眠狀態(tài)等待”事件”的發(fā)生

?

先來(lái)分析電腦à開(kāi)發(fā)板à手機(jī)方向的數(shù)據(jù)流

在main?函數(shù)的串口初始化程序uart_init的最后打開(kāi)了串口的接收中斷。

那么這個(gè)方向的數(shù)據(jù)流的起點(diǎn)就是在串口中斷中收到電腦上發(fā)來(lái)的數(shù)據(jù)為起點(diǎn)

?

Uart中斷函數(shù)在main函數(shù)上方


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;

????????

???????? //取得電腦串口發(fā)過(guò)來(lái)的數(shù)據(jù)

??? data_array[index] = simple_uart_get();

??? index++;

???????? //判斷串口發(fā)送給來(lái)的數(shù)據(jù)是否達(dá)到20的字節(jié),或者是不是發(fā)送了字母’q’。如果滿(mǎn)足

???????? //調(diào)用發(fā)送函數(shù)將收到的串口數(shù)據(jù)發(fā)送給手機(jī)。否則不發(fā)送等待知道滿(mǎn)足條件。

??? // (這里通常新手說(shuō)手機(jī)收不到數(shù)據(jù)的原因,因?yàn)闆](méi)輸入達(dá)到20個(gè)字節(jié))

??? 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);

??????? }

//發(fā)送了數(shù)據(jù)后清零數(shù)組下標(biāo)。以繼續(xù)緩存后續(xù)的串口數(shù)據(jù)。

??????? index = 0;

??? }

}

?


再來(lái)看看發(fā)送數(shù)據(jù)給手機(jī)的函數(shù)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;

??? }

//這里是檢測(cè)參數(shù)是否正確。是否是已經(jīng)連接上了手機(jī)?(只有連接后,conn_handle才會(huì)

?? //?被賦值為有效值),檢查手機(jī)是否使能了開(kāi)發(fā)板的通知,因?yàn)殚_(kāi)發(fā)板作為服務(wù)端向手機(jī)

???????? //發(fā)送數(shù)據(jù)時(shí)通過(guò)通知或指示兩種方式,這兩種方式都需要手機(jī)先使能開(kāi)發(fā)板。

?? if((p_nus->conn_handle==BLE_CONN_HANDLE_INVALID)||(!p_nus->is_notification_enabled))

??? {

??????? return NRF_ERROR_INVALID_STATE;

??? }

???????? //一次發(fā)送的長(zhǎng)度不能超過(guò)限定值20

??? if (length > BLE_NUS_MAX_DATA_LEN)

??? {

??????? return NRF_ERROR_INVALID_PARAM;

??? }

?

??? memset(&hvx_params, 0, sizeof(hvx_params));

????????

???????? //以為是通過(guò)Rx這個(gè)參數(shù)來(lái)發(fā)送數(shù)據(jù)給手機(jī)的,所以句柄要填rx的句柄

???????? //這個(gè)句柄是在上面的服務(wù)初始化函數(shù)中的添加特征值函數(shù)調(diào)用完畢后或得的(最后一???? //?個(gè)參數(shù)為返回的句柄)

???????? //然后就是賦值要發(fā)送的數(shù)據(jù),并且設(shè)置為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;

//發(fā)送函數(shù)

??? return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);

}

?

?

?

看到這里應(yīng)該對(duì)電腦-》板子-》手機(jī)的數(shù)據(jù)流有一個(gè)認(rèn)識(shí)。在討論另一個(gè)方向的數(shù)據(jù)傳輸過(guò)程。我們先來(lái)看一個(gè)關(guān)于連接的問(wèn)題。

我們調(diào)用sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);發(fā)送數(shù)據(jù)給手機(jī)的時(shí)候,第二個(gè)參數(shù)是上面賦值的,那第一個(gè)參數(shù)這個(gè)連接句柄是怎么回事?在哪里設(shè)置過(guò)他?

連接句柄你可以看做是信道標(biāo)志一樣(實(shí)際數(shù)據(jù)接入地址),每?jī)蓚€(gè)連接的設(shè)備都會(huì)具有這個(gè)連接句柄。他們后續(xù)的通信都是通過(guò)這個(gè)連接句柄來(lái)進(jìn)行(可以理解是信道標(biāo)志,兩個(gè)設(shè)備的通信標(biāo)志必須一樣,這代表他們是在同樣的信道上通信才能正確進(jìn)行通信)

?

上面我們說(shuō)過(guò),板子整個(gè)初始化流程走完后就是睡眠和廣播等待手機(jī)連接。那么這個(gè)conn_handle就一定是手機(jī)發(fā)來(lái)連接,板子中協(xié)議棧處理完后上拋給app的連接事件中賦值的。從而記錄下后續(xù)板子和手機(jī)通信的”信道”。 在?程序框架剖析??那一講中介紹過(guò),協(xié)議棧拋上來(lái)的事件結(jié)構(gòu)體最終是由dispatch這個(gè)派發(fā)程序發(fā)給再發(fā)給各個(gè)服務(wù)的事件處理函數(shù)和模塊的事件處理函數(shù)的。

?

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)

{

???????? //將事件交給連接管理模塊的事件處理函數(shù)

ble_conn_params_on_ble_evt(p_ble_evt);

//將事件交給uart服務(wù)的事件處理函數(shù)

ble_nus_on_ble_evt(&m_nus, p_ble_evt);

//處理一些一般的事件

??? on_ble_evt(p_ble_evt);

}

再進(jìn)入?uart服務(wù)的事件處理函數(shù)中看下發(fā)生連接時(shí)是如何記下 后續(xù)通信所用的連接句柄的

這里只截取部分相關(guān)代碼

?

說(shuō)完了連接句柄下面來(lái)說(shuō)最后一個(gè)問(wèn)題

手機(jī)-》板子—》電腦方向的數(shù)據(jù)處理過(guò)程。


四:接收手機(jī)數(shù)據(jù)并通過(guò)串口打印:

其實(shí)看完了上面關(guān)于連接句柄的記錄。再來(lái)理解怎么收到手機(jī)的數(shù)據(jù)就容易了。

因?yàn)槲覀冋f(shuō)過(guò),手機(jī)發(fā)送數(shù)據(jù)過(guò)來(lái)也是一個(gè)事件!

既然都是事件,那么傳遞流程一定是一樣的,只是在最后的處理上不同的事件不同的處理。

?

那么第一步一定是協(xié)議棧處理完收到的數(shù)據(jù)打包一個(gè)?“寫(xiě)事件”?然后上拋給app。其實(shí)就是上拋給dispatch。然后在由它繼續(xù)分發(fā)事件

?

再進(jìn)入函數(shù)內(nèi)部:


再進(jìn)入on_write函數(shù)內(nèi)部一看究竟。


這里最終是調(diào)用了一個(gè)回調(diào)函數(shù)來(lái)數(shù)理最終的數(shù)據(jù),那么這個(gè)回調(diào)函數(shù)是什么時(shí)候注冊(cè)的。在 第二部分 函數(shù)單獨(dú)解析的?services_init講解中說(shuō)明過(guò)。


再來(lái)看看注冊(cè)的這個(gè)?nus_data_handler?到底干了什么

?


到這里手機(jī)->板子->電腦方向的數(shù)據(jù)流也理清了

整體的流程就是 手機(jī)發(fā)送數(shù)據(jù)給板子后,板子中低層的協(xié)議棧將收到的數(shù)據(jù)打包成一個(gè)寫(xiě)事件結(jié)構(gòu)體,然后上拋給app,最終由app種的diapatch再分發(fā)給各個(gè)服務(wù)或模塊的事件處理函數(shù),而uart的事件處理函數(shù)收到寫(xiě)事件后判斷是不是要打印到電腦上的”普通數(shù)據(jù)”,如果是就調(diào)用server_init中注冊(cè)的回調(diào)函數(shù)。該回調(diào)函數(shù)最終將數(shù)據(jù)打印到電腦上

總結(jié)

以上是生活随笔為你收集整理的BLE-NRF51822教程4-串口BLE解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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