0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Apache NimBLE v1.5版本的BLE HCI層設(shè)計(jì)

RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 來(lái)源:RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 作者:Leno_yan ? 2022-07-22 10:03 ? 次閱讀

作者信息

本文主要分析了 Apache NimBLE v1.5 版本的 BLE HCI 層設(shè)計(jì),并分析了官方倉(cāng)庫(kù)自帶 UART 對(duì)接例程;關(guān)于 BLE 層次結(jié)構(gòu)可以先看一下這篇參考文檔。

NimBLE 目錄結(jié)構(gòu)

 1NimBLE
 2├───apps/*Bluetooth示例應(yīng)用程序*/
 3├───docs/*官方文檔及API說(shuō)明*/
 4├───ext
 5├───nimble
 6│├───controller/*Controller實(shí)現(xiàn)*/
 7│├───doc/*當(dāng)前包含transport層說(shuō)明文檔*/
 8│├───drivers/*Nordic系列Phy驅(qū)動(dòng)*/
 9│├───host/*HostStack(主機(jī)控制器)實(shí)現(xiàn)*/
10│├───include
11│├───src
12│└───transport/*HCI傳輸抽象層*/
13└───porting/*OS抽象層及系統(tǒng)配置*/

  • 觀察目錄,可以看出 NimBLE 是實(shí)現(xiàn)了 Host 與 Controller 分離的,在官方介紹中也有說(shuō)明。

  • nimble 的 Host 可以跑在任一芯片上;Controller 則限制比較多,需要有特殊的硬件以及驅(qū)動(dòng)支持。

  • nimble/controller 則是 Controller 相關(guān)代碼;nimble/host 對(duì)應(yīng) Host 代碼;nimble/transport 就是 HCI 層代碼;這一版本 nimble/doc 下還包含了 HCI 層的說(shuō)明文檔 transport.md。

  • 順帶一提,porting 目錄下是 OS 抽象層及系統(tǒng)配置;rt-thread 的移植就實(shí)現(xiàn)在 porting/npl 下。

NimBLE HCI 層

主機(jī)控制接口層(Host Controller Interface,簡(jiǎn)寫 HCI):HCI是可選的,主要用于2顆芯片實(shí)現(xiàn)BLE協(xié)議棧的場(chǎng)合(一個(gè)當(dāng)作 Host 一個(gè)當(dāng)作 Controller),用來(lái)規(guī)范兩者之間的通信協(xié)議和通信命令等。

NimBLE HCI 層主要是了解 nimble/transport 下的內(nèi)容。首先看一下官方文檔 nimble/doc/transport.md 中對(duì) transport 層的說(shuō)明,主要看下面這張圖:

8e5c0054-08e7-11ed-ba43-dac502259ad0.png

HCI 層包括這4接口:

Host 從 Controller 端接收接口 ble_transport_to_hs_evtble_transport_to_hs_acl;以及 Host 向 Controller 發(fā)送接口 ble_transport_to_ll_cmd ble_transport_to_ll_acl

在目錄下,官方流出的接口定義主要包含在下面幾個(gè)文件中:


					
1transport
2└───inlucde
3├───nimble
4│└───transport
5│└───monitor.h
6├───transport_impl.h
7└───transport.h

其中比較重要的是 transport_impl.h 文件,從名字也可以看出這是一個(gè)需要實(shí)現(xiàn)的接口定義文件,其主要內(nèi)容如下:


					

						
1/*InitfunctionstobeimplementedfortransportactingasHS/LLside*/
2externvoidble_transport_ll_init(void);
3externvoidble_transport_hs_init(void);
4/*APIstobeimplementedbyHS/LLsideoftransports*/
5externintble_transport_to_ll_cmd_impl(void*buf);
6externintble_transport_to_ll_acl_impl(structos_mbuf*om);
7externintble_transport_to_hs_evt_impl(void*buf);
8externintble_transport_to_hs_acl_impl(structos_mbuf*om);

這些接口在 transportinclude imble ransportmonitor.h 中被引用:


					
 1staticinlineint
 2ble_transport_to_ll_cmd(void*buf)
 3{
 4returnble_transport_to_ll_cmd_impl(buf);
 5}
 6staticinlineint
 7ble_transport_to_ll_acl(structos_mbuf*om)
 8{
 9returnble_transport_to_ll_acl_impl(om);
10}
11staticinlineint
12ble_transport_to_hs_evt(void*buf)
13{
14returnble_transport_to_hs_evt_impl(buf);
15}
16staticinlineint
17ble_transport_to_hs_acl(structos_mbuf*om)
18{
19returnble_transport_to_hs_acl_impl(om);
20}

看完這個(gè)文件,大概明白了,之前官方文檔圖中提到的 HCI 4個(gè)接口,在這里與對(duì)應(yīng)的 _impl() 接口綁定了起來(lái)。而 impl() 接口就是官方提供給開(kāi)發(fā)者具體實(shí)現(xiàn)對(duì)接的接口。

由于 Host 和 Contoller 是雙向交互的,所以發(fā)送與接收 HCI 包接口是要完整實(shí)現(xiàn)的,也就是大多數(shù)情況下上述 4 個(gè) *impl() 接口都需要全部實(shí)現(xiàn)。

分析 UART 對(duì)接 HCI 層官方例程


官方提供了一個(gè)使用 UART 對(duì)接 HCI 層的例程,源碼文件為

nimble/transport/uart/src/hci_uart.c

首先找到熟悉的接口:代碼中顯式實(shí)現(xiàn)了 ble_transport_to_hs_evt_impl 以及 ble_transport_to_hs_acl_impl ,這兩個(gè)接口中基本上就是使用 uart 向 host 發(fā)送數(shù)據(jù)包,涉及到某個(gè)板子 uart 發(fā)送數(shù)據(jù)的具體細(xì)節(jié),這里不過(guò)多關(guān)注。

看完上面兩個(gè)接口,其實(shí) HCI 中向 Host 發(fā)送數(shù)據(jù)包功能算實(shí)現(xiàn)完了,且當(dāng)前文件中沒(méi)有實(shí)現(xiàn)其他的 impl 接口,很容易能想到,這是寫的 Controller 端的 HCI 層代碼。

有了這個(gè)定性信息,可以開(kāi)始分析如何從 uart 中接收 Host 層發(fā)來(lái)的數(shù)據(jù)包。

通過(guò) uart 初始化函數(shù)找到 uart 接收回調(diào)函數(shù):


					
 
1//uart初始化函數(shù)
2rc=hal_uart_init_cbs(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT),
3hci_uart_tx_char,NULL,
4hci_uart_rx_char,NULL);

					

						

							

								
 
1//uart接收回調(diào)函數(shù)
2staticinthci_uart_rx_char(void*arg,uint8_tdata)
3{
4hci_h4_sm_rx(&hci_uart_h4sm,&data,1);
5return0;
6}

可以看到每接收一個(gè)字符,都使用 hci_h4_sm_rx 進(jìn)行接收。該函數(shù)聲明在 transportcommonhci_h4include imble ransporthci_h4.h 文件下,是關(guān)于 H4 的一個(gè)函數(shù),大概看一下具體定義,接收字符后有一個(gè)組幀判斷的過(guò)程。看一下 hci_h4.h 下比較關(guān)鍵的兩個(gè)接口:


					
 

							
1voidhci_h4_sm_init(structhci_h4_sm*h4sm,
2conststructhci_h4_allocators*allocs,
3hci_h4_frame_cb*frame_cb);
4inthci_h4_sm_rx(structhci_h4_sm*h4sm,constuint8_t*buf,uint16_tlen);

hci_h4_sm_init() 中出現(xiàn)了一個(gè) hci_h4_frame_cb *frame_cb 參數(shù),這是一個(gè)函數(shù)指針參數(shù),初步猜測(cè)用于回調(diào)函數(shù)的注冊(cè)。且在

hci_uart.c 代碼中,也找到了 hci_h4_sm_init() 相關(guān)調(diào)用:


					
 

							

								
244:hci_h4_sm_init(&hci_uart_h4sm,&hci_h4_allocs_from_hs,hci_uart_frame_cb)

這里將 hci_uart_frame_cb 注冊(cè)成了回調(diào)函數(shù),源碼中定義如下:


					
 
 1staticint
 2hci_uart_frame_cb(uint8_tpkt_type,void*data)
 3{
 4switch(pkt_type){
 5caseHCI_H4_CMD:
 6returnble_transport_to_ll_cmd(data);
 7caseHCI_H4_ACL:
 8returnble_transport_to_ll_acl(data);
 9default:
10assert(0);
11break;
12}
13return-1;
14}

hci_uart_frame_cb 基本上是對(duì)一個(gè)完整的 HCI 包的處理,根據(jù)不同的類型使用 ble_transport_to_ll_cmdble_transport_to_ll_acl 傳輸給 Link Layer 層( Link Layer 是 Controller 上的一個(gè)層次,更加確定這是 Controller 上的 HCI 層實(shí)現(xiàn) )。

結(jié)合 hci_uart.c 中對(duì) hci_h4_sm 的使用,以及對(duì) hci_h4_sm 相關(guān)接口源碼的分析,hci_h4_sm 其實(shí)是官方提供的一個(gè)類似保證包完整性的東西,用于判斷一幀完整的 HCI 數(shù)據(jù)包,并且提供組包完成回調(diào)函數(shù)的機(jī)制。

看到這里,大概脈絡(luò)應(yīng)該已經(jīng)捋清楚了。這是一個(gè) Controller 上的 UART HCI 層對(duì)接實(shí)現(xiàn):

1、向 Host 發(fā)送 HCI 包:主要通過(guò)顯式實(shí)現(xiàn) ble_transport_to_hs_evt_impl 以及 ble_transport_to_hs_acl_impl 接口實(shí)現(xiàn),具體何時(shí)被調(diào)用,協(xié)議棧已經(jīng)自動(dòng)處理好。

2、從 Host 接收 HCI 包:主要是使用的 hci_h4 中的組包接口,hci_h4_sm 即一個(gè)組包的狀態(tài)機(jī)實(shí)例,通過(guò) hci_h4_sm_rx 接收 uart 接收到的字符,在判斷 hci 包完整接收時(shí)調(diào)用提前注冊(cè)好的 回調(diào)函數(shù) hci_h4_frame_cb 。hci_h4_frame_cb 里則實(shí)現(xiàn)了將 uart 接收到的包傳遞給 LL 層,進(jìn)而 Controller 可以對(duì) Host 傳下來(lái)的命令或數(shù)據(jù)做出響應(yīng)動(dòng)作。

完整的 HCI 層實(shí)現(xiàn)


Controller 使用 UART 做 HCI 層數(shù)據(jù)傳輸

開(kāi)始說(shuō)到無(wú)論在什么情況下都要完成 HCI 層中 4個(gè)主要接口的實(shí)現(xiàn),當(dāng)前 hci_uart.c 中只找到了兩個(gè)接口的實(shí)現(xiàn),還有另外兩個(gè)接口在哪呢。使用全局搜索在 nimblecontrollersrcle_ll.c 下找到了另外兩個(gè)接口的實(shí)現(xiàn):


					

						
 1/*TransportAPIsforLLside*/
 2int
 3ble_transport_to_ll_cmd_impl(void*buf)
 4{
 5returnble_ll_hci_cmd_rx(buf,NULL);
 6}
 7int
 8ble_transport_to_ll_acl_impl(structos_mbuf*om)
 9{
10returnble_ll_hci_acl_rx(om,NULL);
11}

因?yàn)槟壳澳J(rèn)在 Controller 端實(shí)現(xiàn) HCI 層,剩下未實(shí)現(xiàn)的兩個(gè)接口在 Controller 源碼下實(shí)現(xiàn)了。

Host 使用 UART 做 HCI 層數(shù)據(jù)傳輸

那么假設(shè)當(dāng)前需要實(shí)現(xiàn) Host 端的 HCI 層,且還是需要使用 UART 對(duì)接。那么需要實(shí)現(xiàn)對(duì)接 UART 的接口應(yīng)該剛好和 hci_uart.c 中相反。

需要在對(duì)接源碼中,實(shí)現(xiàn) ble_transport_to_ll_cmd_implble_transport_to_ll_acl_impl 發(fā)送接口,具體同樣也是使用 UART 發(fā)送包。相應(yīng)的,在 nimblehostsrcle_hs.c 下找到了以下兩個(gè)接口的實(shí)現(xiàn):


					

						

							
 1/*TransportAPIsforHSside*/
 2int
 3ble_transport_to_hs_evt_impl(void*buf)
 4{
 5returnble_hs_hci_rx_evt(buf,NULL);
 6}
 7int
 8ble_transport_to_hs_acl_impl(structos_mbuf*om)
 9{
10returnble_hs_rx_data(om,NULL);
11}

也就實(shí)現(xiàn)了 Host端 HCI 層的完整搭建。

  • 在使用 UART 對(duì)接 HCI 層時(shí),是默認(rèn)當(dāng)前環(huán)境只跑了 Host 或 Controller 中的一端,因此 nimblecontrollersrcle_ll.c 和nimblehostsrcle_hs.c 不會(huì)同時(shí)參與編譯,自然也不會(huì)引起一些 *impl() 接口重復(fù)定義的錯(cuò)誤。

不需要其他接口進(jìn)行 HCI 傳輸

當(dāng) Host 與 Controller 跑在同一環(huán)境中,不需要對(duì)接具體的數(shù)據(jù)傳輸接口, nimblecontrollersrcle_ll.cnimblehostsrcle_hs.c 同時(shí)參與編譯,此時(shí)無(wú)需再編寫額外的代碼,即可完整實(shí)現(xiàn)整個(gè) HCI 層。

總結(jié)

在較早的版本中,NimBLE 對(duì)接 HCI 層需要實(shí)現(xiàn)以下接口(參考文檔):


					

						

							

								
 1intble_hci_trans_hs_cmd_tx(uint8_t*cmd);
 2intble_hci_trans_hs_acl_tx(structos_mbuf*om);
 3voidble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn*cmd_cb,
 4void*cmd_arg,
 5ble_hci_trans_rx_acl_fn*acl_cb,
 6void*acl_arg);
 7uint8_t*ble_hci_trans_buf_alloc(inttype);
 8voidble_hci_trans_buf_free(uint8_t*buf);
 9intble_hci_trans_set_acl_free_cb(os_mempool_put_fn*cb,void*arg);
10intble_hci_trans_reset(void);

可以看出來(lái)有一些麻煩,除了處理數(shù)據(jù)傳輸方向,還有處理數(shù)據(jù)包的內(nèi)存申請(qǐng)與釋放,實(shí)現(xiàn)起來(lái)較為復(fù)雜。

當(dāng)前版本中官方對(duì) HCI 層進(jìn)行了重構(gòu),在 transport.c 中統(tǒng)一處理了數(shù)據(jù)包的內(nèi)存申請(qǐng)與釋放;讓開(kāi)發(fā)者專注于處理數(shù)據(jù)的發(fā)送與接收方式,并且提供了 H4 類型的 HCI 包接收工具,更加方便開(kāi)發(fā)者的對(duì)接使用。

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • BLE
    BLE
    +關(guān)注

    關(guān)注

    12

    文章

    681

    瀏覽量

    60060
  • HCI
    HCI
    +關(guān)注

    關(guān)注

    0

    文章

    29

    瀏覽量

    13129
  • Apache
    +關(guān)注

    關(guān)注

    0

    文章

    64

    瀏覽量

    12584

原文標(biāo)題:NimBLE HCI 層分析

文章出處:【微信號(hào):RTThread,微信公眾號(hào):RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 0人收藏

    評(píng)論

    相關(guān)推薦

    如何在完成BLE Provisioning后使用Nimble?

    advertising.I (29593) NimBLE: GAP procedure initiated: terminate connection; conn_handle=1 hci_reason=19I (29593
    發(fā)表于 06-06 06:18

    使用mesh加手機(jī)ble與esp32通信時(shí)遇到一個(gè)死機(jī)問(wèn)題怎么解決?

    ble_hs_hci_cmd_send at D:/ESP-IDF/components/bt/host/nimble/nimble/nimble/host/src/
    發(fā)表于 06-17 06:15

    升級(jí)到RTOS SDK v1.5版本編譯報(bào)錯(cuò)如何解決?

    準(zhǔn)備升級(jí)到RTOS SDK v1.5版本,在進(jìn)行工程編譯的時(shí)候出現(xiàn)問(wèn)題,cJSON.c使用了floor和pow兩個(gè)方法,并且該文件#include ,但在鏈接的時(shí)候庫(kù)中找不到這兩個(gè)方法的定義,出現(xiàn)
    發(fā)表于 07-12 06:10

    主要分析Apache NimBLE v1.5版本BLE HCI設(shè)計(jì)

    HCI 代碼;這一版本 nimble/doc 下還包含了 HCI 的說(shuō)明文檔 transp
    發(fā)表于 07-11 10:27

    使用rt-thread UART對(duì)接NimBLEHCI設(shè)計(jì)實(shí)現(xiàn)

    本文主要介紹如何使用 RT-Thread UART 對(duì)接 NimBLE 中 Host 端的 HCI 傳輸,實(shí)現(xiàn) RT-Thread NimBLE Host 與
    發(fā)表于 07-27 11:27

    主要介紹NimBLE軟件包的使用說(shuō)明

    、配置 NimBLE HCI 支持,選擇實(shí)際用于連接藍(lán)牙 Control 芯片的串口,如 “uart3”。4、選擇使用相應(yīng)的藍(lán)牙例程:在 Bluetooth Samples 中選擇相應(yīng)的例程。目前
    發(fā)表于 08-18 10:22

    ble_hs_hci_cmd_send .函數(shù)重入導(dǎo)致申請(qǐng)內(nèi)存失敗死機(jī)的原因?怎么解決?

    ble_hs_hci_cmd_send at D:/ESP-IDF/components/bt/host/nimble/nimble/nimble/host/src/
    發(fā)表于 02-15 08:34

    NuTiny-EVB-NUC_QFN88 V1.4,網(wǎng)站上提供的資料是v1.5的請(qǐng)問(wèn)v1.4與v1.5的區(qū)別在哪里?

    手上有一塊 NuTiny-EVB-NUC_QFN88 V1.4,網(wǎng)站上提供的資料是v1.5的請(qǐng)問(wèn)v1.4與v1.5的區(qū)別在哪里? Sample Code 能否通用?有沒(méi)有
    發(fā)表于 06-16 08:12

    低功耗藍(lán)牙設(shè)備接入?yún)f(xié)議-血糖儀V1.5

    京東低功耗藍(lán)牙設(shè)備接入?yún)f(xié)議-血糖儀V1.5
    發(fā)表于 11-19 16:29 ?0次下載

    ps2解碼通訊手冊(cè)V1.5

    ps2解碼通訊手冊(cè)V1.5,感興趣的小伙伴們可以瞧一瞧。
    發(fā)表于 09-22 12:04 ?16次下載

    LPC2378 Erratasheet V1.5資料

    LPC2378 Erratasheet V1.5資料,感興趣的小伙伴們可以看看。
    發(fā)表于 11-08 18:13 ?0次下載

    YX5200-24SS原理圖V1.5

    語(yǔ)音芯片YX5200-24SS原理圖V1.5
    發(fā)表于 11-28 14:16 ?84次下載

    單片機(jī)小精靈v1.5資源下載

    單片機(jī)小精靈v1.5資源下載
    發(fā)表于 03-29 15:38 ?106次下載

    ATK 3.5 TFTLCD V1.5圖下載

    ATK 3.5 TFTLCD V1.5圖下載
    發(fā)表于 09-28 14:35 ?0次下載

    智聯(lián)物聯(lián)調(diào)試工具分享之串口網(wǎng)絡(luò)數(shù)據(jù)調(diào)試助手V1.5

    智聯(lián)物聯(lián)串口網(wǎng)絡(luò)數(shù)據(jù)調(diào)試助手V1.5是一款非常好用的調(diào)試工具,串口網(wǎng)絡(luò)數(shù)據(jù)調(diào)試助手V1.5顯示流暢,不容易丟數(shù)據(jù),支持字符串和十六進(jìn)制方式顯示,和加時(shí)間戳分?jǐn)?shù)據(jù)包顯示。串口網(wǎng)絡(luò)數(shù)據(jù)調(diào)試助手V1.5
    的頭像 發(fā)表于 03-27 14:19 ?2445次閱讀
    智聯(lián)物聯(lián)調(diào)試工具分享之串口網(wǎng)絡(luò)數(shù)據(jù)調(diào)試助手<b class='flag-5'>V1.5</b>

    電子發(fā)燒友

    中國(guó)電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會(huì)員交流學(xué)習(xí)
    • 獲取您個(gè)性化的科技前沿技術(shù)信息
    • 參加活動(dòng)獲取豐厚的禮品