性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

一口Linux
認(rèn)證:優(yōu)質(zhì)創(chuàng)作者
所在專題目錄 查看專題
藍(lán)牙BLE詳解,有這一篇就夠了
Modbus入門
SATA、mSATA、M.2、PCIe!SSD接口那點(diǎn)事
深入解析什么是磁盤陣列技術(shù)?常用的磁盤陣列有哪幾種?
家庭用電都是交流電,為何會(huì)有零線和火線的區(qū)別?
一分鐘讓你了解芯片工作的心臟——晶振
作者動(dòng)態(tài) 更多
linux系統(tǒng)監(jiān)控工具小神器:btop
21小時(shí)前
有沒有權(quán)貴開后門讓子女做軟件開發(fā)人員?
1星期前
一文包你學(xué)會(huì)網(wǎng)絡(luò)數(shù)據(jù)抓包
03-15 09:26
C語言初學(xué)者編程水平上不來?不妨嘗試這10個(gè)C語言例子
03-14 20:31
Linux rootfs:如何開機(jī)就自動(dòng)添加某個(gè)用戶?
03-09 22:18

藍(lán)牙BLE詳解,有這一篇就夠了

一、什么是藍(lán)牙?

藍(lán)牙是一種短距的無線通訊技術(shù),可實(shí)現(xiàn)固定設(shè)備、移動(dòng)設(shè)備之間的數(shù)據(jù)交換。一般將藍(lán)牙3.0之前的BR/EDR藍(lán)牙稱為傳統(tǒng)藍(lán)牙,而將藍(lán)牙4.0規(guī)范下的LE藍(lán)牙稱為低功耗藍(lán)牙。

很多人對(duì)藍(lán)牙的認(rèn)識(shí)還很局限于手機(jī)領(lǐng)域,其實(shí)藍(lán)牙的應(yīng)用已經(jīng)遠(yuǎn)遠(yuǎn)不止于此。過去幾年里,藍(lán)牙的增長(zhǎng)量就達(dá)到了80%,當(dāng)然,低功耗藍(lán)牙的出現(xiàn)也起到關(guān)鍵的作用,相信未來藍(lán)牙會(huì)開創(chuàng)一個(gè)可交互的物聯(lián)世界。

藍(lán)牙4.0標(biāo)準(zhǔn)包括傳統(tǒng)藍(lán)牙模塊部分和低功耗藍(lán)牙模塊部分,是一個(gè)雙模標(biāo)準(zhǔn)。低功耗藍(lán)牙也是建立在傳統(tǒng)藍(lán)牙基礎(chǔ)之上發(fā)展起來的,并區(qū)別于傳統(tǒng)模塊,最大的特點(diǎn)就是成本和功耗降低,應(yīng)用于實(shí)時(shí)性要求比較高。

BLE(Bluetooh Low Energy)藍(lán)牙低能耗技術(shù)是短距離、低成本、可互操作性的無線技術(shù),它利用許多智能手段最大限度地降低功耗。 BLE技術(shù)的工作模式非常適合用于從微型無線傳感器(每半秒交換一次數(shù)據(jù))或使用完全異步通信的遙控器等其它外設(shè)傳送數(shù)據(jù)。這些設(shè)備發(fā)送的數(shù)據(jù)量非常少(通常幾個(gè)字節(jié)),而且發(fā)送次數(shù)也很少(例如每秒幾次到每分鐘一次,甚至更少)。

二、BLE協(xié)議棧的結(jié)構(gòu)和配置

1、協(xié)議有兩個(gè)部分組成:Controller和Host 2、Profiles和應(yīng)用總是基于GAP和GATT之上 3、在單芯片方案中,Controller和Host,profiles,和應(yīng)用層都在同一片芯片中 4、在網(wǎng)絡(luò)控制器模式中,Host和Controller是在一起運(yùn)行的,但是應(yīng)用和profiles在另外一個(gè)器件上,比如PC或者其他微控制器,可以通過UART,USB進(jìn)行操作 5、在雙芯片模式中,Controller運(yùn)行在一個(gè)控制器,而應(yīng)用層,profiles和Host是運(yùn)行在另外一個(gè)控制器上

BLE設(shè)備連接狀態(tài)流程圖

三、BLE協(xié)議棧各層功能機(jī)制

如上圖所述,要實(shí)現(xiàn)一個(gè)BLE應(yīng)用,首先需要一個(gè)支持BLE射頻的芯片,然后還需要提供一個(gè)與此芯片配套的BLE協(xié)議棧,最后在協(xié)議棧上開發(fā)自己的應(yīng)用??梢钥闯鯞LE協(xié)議棧是連接芯片和應(yīng)用的橋梁,是實(shí)現(xiàn)整個(gè)BLE應(yīng)用的關(guān)鍵。那BLE協(xié)議棧具體包含哪些功能呢?簡(jiǎn)單來說,BLE協(xié)議棧主要用來對(duì)你的應(yīng)用數(shù)據(jù)進(jìn)行層層封包,以生成一個(gè)滿足BLE協(xié)議的空中數(shù)據(jù)包,也就是說,把應(yīng)用數(shù)據(jù)包裹在一系列的幀頭(header)和幀尾(tail)中。具體來說,BLE協(xié)議棧主要由如下幾部分組成:

  1. PHY層(Physical layer物理層)。 PHY層用來指定BLE所用的無線頻段,調(diào)制解調(diào)方式和方法等。PHY層做得好不好,直接決定整個(gè)BLE芯片的功耗,靈敏度以及selectivity等射頻指標(biāo)。

  2. LL層(Link Layer鏈路層)。 LL層是整個(gè)BLE協(xié)議棧的核心,也是BLE協(xié)議棧的難點(diǎn)和重點(diǎn)。像Nordic的BLE協(xié)議棧能同時(shí)支持20個(gè)link(連接),就是LL層的功勞。LL層要做的事情非常多,比如具體選擇哪個(gè)射頻通道進(jìn)行通信,怎么識(shí)別空中數(shù)據(jù)包,具體在哪個(gè)時(shí)間點(diǎn)把數(shù)據(jù)包發(fā)送出去,怎么保證數(shù)據(jù)的完整性,ACK如何接收,如何進(jìn)行重傳,以及如何對(duì)鏈路進(jìn)行管理和控制等等。LL層只負(fù)責(zé)把數(shù)據(jù)發(fā)出去或者收回來,對(duì)數(shù)據(jù)進(jìn)行怎樣的解析則交給上面的GAP或者ATT。

  3. HCI(Host controller interface)。 HCI是可選的,HCI主要用于2顆芯片實(shí)現(xiàn)BLE協(xié)議棧的場(chǎng)合,用來規(guī)范兩者之間的通信協(xié)議和通信命令等。

  4. GAP層(Generic access profile)。 GAP是對(duì)LL層payload(有效數(shù)據(jù)包)如何進(jìn)行解析的兩種方式中的一種,而且是最簡(jiǎn)單的那一種。GAP簡(jiǎn)單的對(duì)LL payload進(jìn)行一些規(guī)范和定義,因此GAP能實(shí)現(xiàn)的功能極其有限。GAP目前主要用來進(jìn)行廣播,掃描和發(fā)起連接等。

  5. L2CAP層(Logic link control and adaptation protocol)。 L2CAP對(duì)LL進(jìn)行了一次簡(jiǎn)單封裝,LL只關(guān)心傳輸?shù)臄?shù)據(jù)本身,L2CAP就要區(qū)分是加密通道還是普通通道,同時(shí)還要對(duì)連接間隔進(jìn)行管理。

  6. SMP(Secure manager protocol)。 SMP用來管理BLE連接的加密和安全的,如何保證連接的安全性,同時(shí)不影響用戶的體驗(yàn),這些都是SMP要考慮的工作。

  7. ATT(Attribute protocol)。 簡(jiǎn)單來說,ATT層用來定義用戶命令及命令操作的數(shù)據(jù),比如讀取某個(gè)數(shù)據(jù)或者寫某個(gè)數(shù)據(jù)。BLE協(xié)議棧中,開發(fā)者接觸最多的就是ATT。BLE引入了attribute概念,用來描述一條一條的數(shù)據(jù)。Attribute除了定義數(shù)據(jù),同時(shí)定義該數(shù)據(jù)可以使用的ATT命令,因此這一層被稱為ATT層。

  8. GATT(Generic attribute profile )。 GATT用來規(guī)范attribute中的數(shù)據(jù)內(nèi)容,并運(yùn)用group(分組)的概念對(duì)attribute進(jìn)行分類管理。沒有GATT,BLE協(xié)議棧也能跑,但互聯(lián)互通就會(huì)出問題,也正是因?yàn)橛辛薌ATT和各種各樣的應(yīng)用profile,BLE擺脫了ZigBee等無線協(xié)議的兼容性困境,成了出貨量最大的2.4G無線通信產(chǎn)品。

四、BLE藍(lán)牙模塊主要應(yīng)用領(lǐng)域

  1. 移動(dòng)擴(kuò)展設(shè)備
  2. 汽車電子設(shè)備
  3. 健康醫(yī)療用品:心跳帶、血壓計(jì)等
  4. 定位應(yīng)用:室內(nèi)定位、井下定位等
  5. 近距離數(shù)據(jù)采集:無線抄表、無線遙測(cè)等
  6. 數(shù)據(jù)傳輸:智能家居室內(nèi)控制、藍(lán)牙調(diào)光、打印機(jī)等

五、 BLE協(xié)議棧詳解

協(xié)議概述

所謂協(xié)議,即將指定的字節(jié)按照一定的順序排列起來,以便他人使用自己的設(shè)備時(shí),能通過該協(xié)議同其他設(shè)備進(jìn)行通信。協(xié)議一特點(diǎn),就是有固定的幀格式,通過該格式發(fā)送,接收者通過解讀幀格式,進(jìn)而得到新息內(nèi)容;

BLE連接過程

一般通信協(xié)議,一類通信是直接發(fā)生數(shù)據(jù),當(dāng)設(shè)備接送到數(shù)據(jù)時(shí),直接對(duì)數(shù)據(jù)進(jìn)行解析,當(dāng)接受到的數(shù)據(jù)合法時(shí),即為有效數(shù)據(jù),該類型的通信協(xié)議,主要用在有線通信協(xié)議中,比如Modbus,Can通常采用的即為該類型的通信方式。

另一類通信協(xié)議,則需要新建立連接,當(dāng)雙方連接建立成功了方可通信,例如TCP、BLE;BLE協(xié)議在需要進(jìn)行通信時(shí),即需要向外發(fā)送廣播信號(hào),告訴接收者,即將和它進(jìn)行通信,接受者接收到廣播內(nèi)容后,確認(rèn)是與自己通信,于是向廣播者發(fā)送一響應(yīng)信息,這樣當(dāng)廣播者和接受者都有了對(duì)方的身份信息時(shí),即表示雙方連接成功。

因此,在連接過程中,必定有相應(yīng)的廣播幀格式。在BLE通信過程中,假設(shè)設(shè)備A需要連其他設(shè)備假設(shè)為B,則A需要不斷地發(fā)送廣播信號(hào)(此過程一般有一個(gè)時(shí)間間隔,在沒發(fā)送廣播數(shù)據(jù)時(shí)間內(nèi),芯片處于低功耗狀態(tài)),每發(fā)送一次廣播包,稱之為一次廣播事件。

前導(dǎo): 是一個(gè)8比特的交替序列 接入地址的第一個(gè)比特為0:01010101 接入地址的第一個(gè)比特為1:10101010 接入地址:廣播幀為固定地址:0x8E89BED6(低字節(jié)在前)

廣播報(bào)文的報(bào)頭: 包含4bit廣播報(bào)文類型、2bit保留位、1bit發(fā)送地址類型和1bit接收地址類型。

廣播報(bào)文類型:

發(fā)送地址類型: 0: 公共地址 1:隨機(jī)地址 長(zhǎng)度:廣播報(bào)文的長(zhǎng)度域包含8個(gè)比特,有效值的范圍是6~37 數(shù)據(jù): 廣播者地址(6個(gè)字節(jié))+廣播數(shù)據(jù)(31個(gè)字節(jié)) 校驗(yàn): 3個(gè)字節(jié),為CRC校驗(yàn)。 廣播數(shù)據(jù): 分為有效數(shù)據(jù)和無效數(shù)據(jù)

有效數(shù)據(jù)部分: 包含N個(gè)AD Structure,每個(gè)AD Structure由Length,AD Type和AD Data組成。其中: Length: AD Type和AD Data的長(zhǎng)度。 AD Type: 指示AD Data數(shù)據(jù)的含義。 詳見https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/

六、 BLE連接建立過程

1. BLE廣播與掃描

  設(shè)備B不斷發(fā)送廣播信號(hào)給手機(jī)(Observer),如果手機(jī)不開啟掃描窗口,手機(jī)是收不到設(shè)備B的廣播的,如下圖所示,不僅手機(jī)要開啟射頻接收窗口,而且只有手機(jī)的射頻接收窗口跟廣播發(fā)送的發(fā)射窗口匹配成功,而且廣播射頻通道和手機(jī)掃描射頻通道是同一個(gè)通道,手機(jī)才能收到設(shè)備B的廣播信號(hào)。也就是說,如果設(shè)備B在37通道發(fā)送廣播包,而手機(jī)在掃描38通道,那么即使他們倆的射頻窗口匹配,兩者也是無法進(jìn)行通信的。由于這種匹配成功是一個(gè)概率事件,因此手機(jī)掃到設(shè)備B也是一個(gè)概率事件,也就是說,手機(jī)有時(shí)會(huì)很快掃到設(shè)備B,比如只需要一個(gè)廣播事件,手機(jī)有時(shí)又會(huì)很慢才能掃到設(shè)備B,比如需要10個(gè)廣播事件甚至更多。

2. 建立連接(connection establishment)

 根據(jù)藍(lán)牙spec規(guī)定,advertiser發(fā)送完一個(gè)廣播包之后150us(T_IFS),advertiser必須開啟一段時(shí)間的射頻Rx窗口,以接收來自observer的數(shù)據(jù)包。Observer就可以在這段時(shí)間里給advertiser發(fā)送連接請(qǐng)求。如下圖所示,手機(jī)在第三個(gè)廣播事件的時(shí)候掃到了設(shè)備B,并發(fā)出了連接請(qǐng)求CONN_REQ(CONN_REQ又稱為CONNECT_IND)。

注:圖中M代表手機(jī),S代表設(shè)備B,M->S表示手機(jī)將數(shù)據(jù)包發(fā)給設(shè)備B,即手機(jī)開啟Tx窗口,設(shè)備B開啟Rx窗口;S->M正好相反,表示設(shè)備B將數(shù)據(jù)包發(fā)給手機(jī),即設(shè)備B開啟Tx窗口,手機(jī)開啟Rx窗口。

如圖所示,手機(jī)在收到A1廣播包ADV_IND后,以此為初始錨點(diǎn)(這個(gè)錨點(diǎn)不是連接的錨點(diǎn)),T_IFS時(shí)間后給Advertiser發(fā)送一個(gè)connection request命令,即A2數(shù)據(jù)包,告訴advertiser我將要過來連你,請(qǐng)做好準(zhǔn)備。Advertiser根據(jù)connect_req命令信息做好接收準(zhǔn)備,connect_req包含如下關(guān)鍵信息:

  • Transmit window offset,定義如上圖示
  • Transmit window size,定義如上圖所示
  • connect_req數(shù)據(jù)包完整定義如下所示

connect_req其實(shí)是在告訴advertiser,手機(jī)將在Transmit Window期間發(fā)送第一個(gè)同步包(P1)給你,請(qǐng)?jiān)谶@段時(shí)間里把你的射頻接收窗口打開。設(shè)備B收到P1后,T_IFS時(shí)間后將給手機(jī)回復(fù)數(shù)據(jù)包P2(ACK包)。

一旦手機(jī)收到數(shù)據(jù)包P2,連接即可認(rèn)為建立成功。當(dāng)然,實(shí)際情況會(huì)比較復(fù)雜,手機(jī)有可能收不到P2,這個(gè)時(shí)候手機(jī)將持續(xù)發(fā)送同步包直到超時(shí)時(shí)間(supervision timeout)到,在此期間只要設(shè)備B回過一次ACK包,連接即算成功。所以一旦P1包發(fā)出,主機(jī)(手機(jī))即認(rèn)為連接成功,而不管有沒有收到設(shè)備的ACK包。

這也是為什么在Android或者iOS系統(tǒng)中,應(yīng)用經(jīng)常收到連接成功的回調(diào)事件(該回調(diào)事件就是基于P1包有沒有發(fā)出,只要P1包發(fā)出,手機(jī)即認(rèn)為連接成功,而不管有沒有收到設(shè)備的ACK包),但實(shí)際上手機(jī)和設(shè)備并沒有成功建立連接。

后續(xù)手機(jī)將以P1為錨點(diǎn)(原點(diǎn)),Connection Interval為周期,周期性地給設(shè)備B發(fā)送數(shù)據(jù)包(Packet),Packet除了充當(dāng)數(shù)據(jù)傳送功能,它還有如下兩個(gè)非常重要的功能: 同步手機(jī)和設(shè)備的時(shí)鐘,也就是說,設(shè)備每收到手機(jī)發(fā)來的一個(gè)包,都會(huì)把自己的時(shí)序原點(diǎn)重新設(shè)置,以跟手機(jī)同步。

告訴設(shè)備你現(xiàn)在可以傳數(shù)據(jù)給我了。連接成功后,BLE通信將變成主從模式,因此把連接發(fā)起者(手機(jī))稱為Master或者Central,把被連接者(之前的Advertiser)稱為Slave或者Peripheral。

BLE通信之所以為主從模式,是因?yàn)镾lave不能“隨性”給Master發(fā)信息,它只有等到Master給它發(fā)了一個(gè)packet后,然后才能在規(guī)定的時(shí)間把自己的數(shù)據(jù)回傳給Master。

3. 連接失敗

有如下幾種典型的連接失敗情況:

  • 如步驟2圖所示,如果slave在transmit window期間沒有收到master發(fā)過來的P1,那么連接將會(huì)失敗。此時(shí)應(yīng)該排查master那邊的問題,看看master為什么沒有在約定的時(shí)間把P1發(fā)出來。

  • 如果master在transmit window期間把P1發(fā)出來了,也就是說master按照connect_req約定的時(shí)序把P1發(fā)出來了,但slave沒有把P2回過去或者沒有在超時(shí)時(shí)間內(nèi)把P2回過去,那么連接也會(huì)失敗。此時(shí)應(yīng)該排查slave這邊的問題,看一看slave為什么沒有把P2回過去

  • 如果master把P1發(fā)出來了,slave也把P2回過去了,此時(shí)主機(jī)或者從機(jī)還是報(bào)連接失敗,這種情況有可能是軟件有問題,需要仔細(xì)排查master或者slave的軟件。

  • 還有一種比較常見的連接失敗情況:空中射頻干擾太大。此時(shí)應(yīng)該找一個(gè)干凈的環(huán)境,比如屏蔽室,排除干擾后再去測(cè)試連接是否正常。

七、幀格式

1、數(shù)據(jù)鏈路層報(bào)文結(jié)構(gòu)

報(bào)文的基礎(chǔ)是數(shù)據(jù)鏈路層的報(bào)文,其它報(bào)文都是從此展開的,BLE數(shù)據(jù)鏈路層數(shù)據(jù)格式如下:

在最新的core spec 5.2中,有1M PHY和2M的PHY,對(duì)應(yīng)前導(dǎo)符變?yōu)?-2個(gè)字節(jié)。前導(dǎo)符用于頻率同步、時(shí)序評(píng)估和自動(dòng)增益控制訓(xùn)練。前導(dǎo)符第一bit應(yīng)該與接入地址的LSB相同。

字段解析:

報(bào)文類型(低 4 個(gè) bit):

ADV_IND(0000) ——通用廣播

ADV_DIRECT_IND(0001) ——定向連接廣播

ADV_NONCONN_IND(0010) ——不可連接廣播

ADV_SCAN_IND(0110) ——可掃描廣播

SCAN_REQ( 0011) ——主動(dòng)掃描請(qǐng)求

SCAN_RSP( 0100) ——主動(dòng)掃描應(yīng)答

CONNECT_REQ( 0101) ——連接請(qǐng)求

發(fā)送地址( TXADD)和接收地址( RXADD): 當(dāng)此位為“1”時(shí)表示 Random Add(隨機(jī)地址),當(dāng)此位為“0”時(shí)表示 Public Add(公共地址)。 這個(gè)地址指的是數(shù)據(jù)凈荷中最初的幾個(gè)地址字節(jié)。

凈荷長(zhǎng)度: 這個(gè)長(zhǎng)度是指在 PDU 中的數(shù)據(jù)除去報(bào)頭和長(zhǎng)度之外的有效凈荷數(shù) 據(jù)長(zhǎng)度。

2、 廣播通道與數(shù)據(jù)通道 PDU 區(qū)別:

廣播通道的 PDU 格式:

字段解析:

報(bào)文類型(低 4 個(gè) bit):

ADV_IND(0000) ——通用廣播

ADV_DIRECT_IND(0001) ——定向連接廣播

ADV_NONCONN_IND(0010) ——不可連接廣播

ADV_SCAN_IND(0110) ——可掃描廣播

SCAN_REQ( 0011) ——主動(dòng)掃描請(qǐng)求

SCAN_RSP( 0100) ——主動(dòng)掃描應(yīng)答

CONNECT_REQ( 0101) ——連接請(qǐng)求

發(fā)送地址( TXADD)和接收地址( RXADD): 當(dāng)此位為“1”時(shí)表示 Random Add (隨機(jī)地址),當(dāng)此位為“0”時(shí)表示 Public Add(公共地址)。 這個(gè)地址指的是數(shù)據(jù)凈荷中最初的幾個(gè)地址字節(jié)。

凈荷長(zhǎng)度: 這個(gè)長(zhǎng)度是指在 PDU 中的數(shù)據(jù)除去報(bào)頭和長(zhǎng)度之外的有效凈荷數(shù)據(jù)長(zhǎng)度。

數(shù)據(jù)通道的 PDU 格式:

字段釋義: LLID: 表示此包數(shù)據(jù)是 LL Date PDU 還是 LL Control PDU

00b: Reserved

01b: LL Date PDU:Continuation fragment of L2CAP message, or an Empty PDU.

10b: LL Date PDU:Start of an L2CAP message or a complete L2CAP message with no fragmentation.

11b: LL Control PDU

MIC( Message Integrity Check): 信息完整性檢測(cè)。涉及到加密操作,上圖中是用虛線表示的,并不是一定要有此項(xiàng)。

MD: 這個(gè)標(biāo)志位是用來通知對(duì)方設(shè)備自己還有其他數(shù)據(jù)準(zhǔn)備發(fā)送。0 表示沒有更多數(shù)據(jù)發(fā)送, 1 表示有更多數(shù)據(jù)準(zhǔn)備發(fā)送。這樣,只要還有數(shù)據(jù)需要發(fā)送,連接事件會(huì)自動(dòng)擴(kuò)展。一旦不再有數(shù)據(jù)發(fā)送,連接事件立即關(guān)閉。

Note:如何區(qū)分是確定包、新包還是重發(fā)包?

SN: 只有一個(gè) bit 位,所以值是在 0 和 1 之間進(jìn)行切換。如果序列號(hào)與之前的一樣,則為重傳報(bào)文,如果序列號(hào)和之間的不同,則為新報(bào)文。

NESN: 預(yù)期序列號(hào),它是接收方希望接到的下一包的序列號(hào),也就是數(shù)據(jù)包的確認(rèn)標(biāo)志。當(dāng)設(shè)備接收到序列(SN)為 0 的報(bào)文后,在發(fā)送給對(duì)方的數(shù)據(jù)包中,應(yīng)將 NESN 設(shè)為 1,這樣對(duì)方接收到這個(gè)包后,會(huì)發(fā)送一個(gè)新的數(shù)據(jù)包過來,否則就會(huì)重發(fā)上一次序列號(hào)為 0 的包。這個(gè)標(biāo)志可以用來判斷數(shù)據(jù)包是否被正確接收還是需要重傳。

3、 BLE 報(bào)文格式

4、 AD Structure 解析

(1) AD type

(2) AD data 簡(jiǎn)述

Flags:

SERVICE:

Local Name:

TX Power Level:

八、代碼示例

本例以O(shè)SAL下BLE代碼為例做講解。

什么是OSAL?

OSAL為:Operating System Abstraction Layer,即操作系統(tǒng)抽象層,支持多任務(wù)運(yùn)行,它并不是一個(gè)傳統(tǒng)意義上的操作系統(tǒng),但是實(shí)現(xiàn)了部分類似操作系統(tǒng)的功能。

OSAL概念是由TI公司在ZIGBEE協(xié)議棧引入,他的意思是”模擬操作系統(tǒng)”,此OS,并非一個(gè)真正的OS,而是模擬OS的一些方法為廣大編程者提供一種寫MCU程序的方法。當(dāng)有一個(gè)事件發(fā)生的時(shí)候,OSAL負(fù)責(zé)將此事件分配給能夠處理此事件的任務(wù),然后此任務(wù)判斷事件的類型,調(diào)用相應(yīng)的事件處理程序進(jìn)行處理。

實(shí)驗(yàn)平臺(tái)

1、藍(lán)牙協(xié)議棧:1.3.2

2、軟件平臺(tái):IAR For 8051 8.10.3

3、硬件平臺(tái):Smart RF開發(fā)板(從機(jī)),Android_Lightblue(主機(jī))

代碼解析

  1. int main(void)
int main(void)
{
  /* Initialize hardware */
  HAL_BOARD_INIT();//初始化時(shí)鐘和使能緩存預(yù)取模式

  // Initialize board I/O
  InitBoard( OB_COLD );//冷啟動(dòng),關(guān)閉了led燈與中斷,避免接下來的各種初始化受干擾

  /* Initialze the HAL driver */
  HalDriverInit();//各種驅(qū)動(dòng)的初始化、如按鍵、lcd、adc、usb、uart等8

  /* Initialize NV system */
  osal_snv_init();//snv 內(nèi)部用于保存配對(duì)數(shù)據(jù)或你的用戶自定義數(shù)據(jù)的一段flash,4kB空間

  /* Initialize LL */  
  /* Initialize the operating system */
  osal_init_system();//oasl 操作系統(tǒng)初始化, 包含內(nèi)存分配、消息隊(duì)列、定時(shí)器、電源管理和任務(wù)等

  /* Enable interrupts */
  HAL_ENABLE_INTERRUPTS();// 開啟全局中斷

  // Final board initialization
  InitBoard( OB_READY );  //設(shè)置標(biāo)志標(biāo)示系統(tǒng)初始化完畢
}
  1. osal_init_system()
uint8 osal_init_system( void )
{
  // Initialize the Memory Allocation System
  osal_mem_init();//初始化內(nèi)存分配系統(tǒng)

  // Initialize the message queue
  osal_qHead = NULL;//初始化消息隊(duì)列

  // Initialize the timers
  osalTimerInit();//初始化定時(shí)器

  // Initialize the Power Management System
  osal_pwrmgr_init();//初始化電源管理系統(tǒng)

  // Initialize the system tasks.
  osalInitTasks();//初始化系統(tǒng)任務(wù), 這一個(gè)任務(wù)初始花非常關(guān)鍵

  // Setup efficient search for the first free block of heap.
  osal_mem_kick();
 
  return ( SUCCESS );
}
  1. osalInitTasks()
void osalInitTasks( void )
{
  /* L2CAP Task */
  L2CAP_Init( taskID++ );

  /* GAP Task */
  GAP_Init( taskID++ );

  /* GATT Task */
  GATT_Init( taskID++ );

  /* SM Task */
  SM_Init( taskID++ );

  /* Profiles */
  GAPRole_Init( taskID++ );    //鏈路角色初始化
  GAPBondMgr_Init( taskID++ );  //鏈路綁定初始化
  GATTServApp_Init( taskID++ );
  /* Application */
  SimpleBLEPeripheral_Init( taskID );
}
  1. GAPRole_Init( taskID++ )
void GAPRole_Init( uint8 task_id )
{
  gapRole_TaskID = task_id;  //定義任務(wù)地址

  gapRole_state = GAPROLE_INIT;  //鏈路狀態(tài)設(shè)置為GAPROLE_INIT
  gapRole_ConnectionHandle = INVALID_CONNHANDLE;  //設(shè)置鏈路連接句柄為0xFFFF
  GAP_RegisterForHCIMsgs( gapRole_TaskID );//注冊(cè)控制接口的任務(wù)ID

  // Initialize the Profile Advertising and Connection Parameters
  gapRole_profileRole = GAP_PROFILE_PERIPHERAL;  //鏈路配置角色為從機(jī)
  VOID osal_memset( gapRole_IRK, 0, KEYLEN );    //密鑰緩沖器清零
  VOID osal_memset( gapRole_SRK, 0, KEYLEN );
  gapRole_signCounter = 0;                       //密鑰計(jì)數(shù)標(biāo)志位清零
  gapRole_AdvEventType = GAP_ADTYPE_ADV_IND; //廣播類型為可連接無定向廣播
  gapRole_AdvDirectType = ADDRTYPE_PUBLIC; //廣播方式為通過廣播(可被發(fā)現(xiàn)掃描連接)
  gapRole_AdvChanMap = GAP_ADVCHAN_ALL ;  //廣播所有通道37、38、39
  gapRole_AdvFilterPolicy = GAP_FILTER_POLICY_ALL;  //允許掃描,允許連接
  // Restore Items from NV
  VOID osal_snv_read( BLE_NVID_IRK, KEYLEN, gapRole_IRK ); //讀出存儲(chǔ)的密鑰和密鑰計(jì)數(shù)標(biāo)志位
  VOID osal_snv_read( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );

  VOID osal_snv_read( BLE_NVID_SIGNCOUNTER, sizeof( uint32 ), &gapRole_signCounter );
}	
  1. SimpleBLEPeripheral_Init
//初始化完成后

void SimpleBLEPeripheral_Init( uint8 task_id )
{
	osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );              //啟動(dòng)設(shè)備開始事件
}
  1. SimpleBLEPeripheral_ProcessEvent
uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{     
  if ( events & SBP_START_DEVICE_EVT )// 初始化后就執(zhí)行這個(gè)啦
      {              
            // Start the Device
            VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs ); //配置鏈路事件通知回調(diào)函數(shù)
        
            // Start Bond Manager
            VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs ); //配置配對(duì)消息回調(diào)函數(shù)
            
            // Set timer for first periodic event
            osal_start_timerEx( simpleBLEPeripheral_TaskID, POWER_DETECT_EVT, DetectPowerPeriod );   
      
            return ( events ^ SBP_START_DEVICE_EVT );
      }
}
  1. GAPRole_StartDevice
Status_t GAPRole_StartDevice( gapRolesCBs_t *pAppCallbacks )
{
  if ( gapRole_state == GAPROLE_INIT ) //如果鏈路狀態(tài)是初始化狀態(tài)
  {
    // Clear all of the Application callbacks
    if ( pAppCallbacks )
    {
      pGapRoles_AppCGs = pAppCallbacks;//設(shè)置回調(diào)函數(shù)
    }

    // Start the GAP
    gapRole_SetupGAP();//開始建立鏈路
    return ( SUCCESS );
  }
  else     //否則返回已經(jīng)在請(qǐng)求模式狀態(tài)
  {
    return ( bleAlreadyInRequestedMode );
  }
}
  1. gapRole_SetupGAP
static void gapRole_SetupGAP( void )
{
  VOID GAP_DeviceInit( gapRole_TaskID,
          gapRole_profileRole, 0,
          gapRole_IRK, gapRole_SRK,
          &gapRole_signCounter );
}
  1. GAP_DeviceInit
bStatus_t GAP_DeviceInit(  uint8 taskID,
                           uint8 profileRole,
                           uint8 maxScanResponses,
                           uint8 *pIRK,
                           uint8 *pSRK,
                           uint32 *pSignCounter )
{
    // Setup the device configuration parameters
stat = GAP_ParamsInit( taskID, profileRole );  //設(shè)置設(shè)備配置參數(shù)
 
    #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
    {
      GAP_SecParamsInit( pIRK, pSRK, pSignCounter );
    }
#endif 
#if ( HOST_CONFIG & ( PERIPHERAL_CFG | BROADCASTER_CFG ) )
{
   // Initialize GAP Peripheral Device Manager
   VOID GAP_PeriDevMgrInit();  //初始化從機(jī)設(shè)備管理
   #if ( HOST_CONFIG & PERIPHERAL_CFG )
   {
     // Initialize SM Responder
     VOID SM_ResponderInit();  //回應(yīng)者初始化
   }
   #endif
 }
 #endif
}
  1. 當(dāng)GAP_DeviceInit初始化完成后,將產(chǎn)生GAP_DEVICE_INIT_DONE_EVENT事件; uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events ) //鏈路處理事件

  2. static void gapRole_ProcessOSALMsg( osal_event_hdr_t *pMsg ) //鏈路系統(tǒng)消息事件

  3. gapRole_ProcessGAPMsg

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )
  {
      case GAP_DEVICE_INIT_DONE_EVENT: //當(dāng)GAP_DeviceInit初始化完成后,將產(chǎn)生此事件
      {
        gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
        bStatus_t stat = pPkt->hdr.status;

        if ( stat == SUCCESS )
        {
          // Save off the generated keys
          VOID osal_snv_write( BLE_NVID_IRK, KEYLEN, gapRole_IRK );//保存生成的密鑰
          VOID osal_snv_write( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );


          // Save off the information
          VOID osal_memcpy( gapRole_bdAddr, pPkt->devAddr, B_ADDR_LEN );//保存設(shè)備地址
          gapRole_state = GAPROLE_STARTED;  //鏈路開始

          // Update the advertising data
          stat = GAP_UpdateAdvertisingData( gapRole_TaskID,//更新廣播數(shù)據(jù)
                              TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );
        }
            notify = TRUE;  //通知回調(diào)函數(shù)鏈路的狀態(tài)
      }

      break;
      if ( notify == TRUE )
      {
          // Notify the application with the new state change
          if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判斷是否設(shè)置了回調(diào)函數(shù)
          {
               pGapRoles_AppCGs->pfnStateChange( gapRole_state );//調(diào)用設(shè)置的回調(diào)函數(shù),通知gapRole_state當(dāng)前狀態(tài)
          }
      }
}
  1. stat=GAP_UpdateAdvertisingData( gapRole_TaskID,TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );//更新廣播數(shù)據(jù)后,將產(chǎn)生GAP_ADV_DATA_UPDATE_DONE_EVENT事件;

  2. gapRole_ProcessGAPMsg

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)
  switch ( pMsg->opcode )
  {
      case GAP_ADV_DATA_UPDATE_DONE_EVENT:
      {
        gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;
        if ( pPkt->hdr.status == SUCCESS )
        {
          if ( pPkt->adType )
          {
            // Setup the Response Data
            pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,
                              FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新掃描回應(yīng)數(shù)據(jù)
          }
          else
          {
            // Start advertising
            VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT );  //啟動(dòng)廣播事件
          }
        }
        if ( pPkt->hdr.status != SUCCESS ) //如果不成功將通知回調(diào)函數(shù),否則不通知
        {
          // Set into Error state
          gapRole_state = GAPROLE_ERROR;
          notify = TRUE;
        }
      }
      break;
  1. gapRole_ProcessGAPMsg
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)
  switch ( pMsg->opcode )
  {
      case GAP_ADV_DATA_UPDATE_DONE_EVENT:
      {
        gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;

        if ( pPkt->hdr.status == SUCCESS )
        {
          if ( pPkt->adType )
          {
            // Setup the Response Data
            pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,
                              FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新掃描回應(yīng)數(shù)據(jù)
          }
          else
          {
            // Start advertising
           VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT );  //啟動(dòng)廣播事件
          }
        }

        if ( pPkt->hdr.status != SUCCESS ) //如果不成功將通知回調(diào)函數(shù),否則不通知
        {
          // Set into Error state
          gapRole_state = GAPROLE_ERROR;
          notify = TRUE;
        }
      }
      break;
  1. 執(zhí)行廣播事件
uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events )
{
  VOID task_id; // OSAL required parameter that isn't used in this function

  if ( events & START_ADVERTISING_EVT )
  {
    if ( gapRole_AdvEnabled )
    {
      gapAdvertisingParams_t params;
 
      // Setup advertisement parameters
      params.eventType = gapRole_AdvEventType; //GAP_ADTYPE_ADV_IND; 廣播類型為可連接無定向廣播
      params.initiatorAddrType = gapRole_AdvDirectType; //ADDRTYPE_PUBLIC; 廣播方式為通用廣播
      VOID osal_memcpy( params.initiatorAddr, gapRole_AdvDirectAddr, B_ADDR_LEN ); //發(fā)起者地址配置
      params.channelMap = gapRole_AdvChanMap;  //廣播通道配置:廣播所有通道37、38、39
      params.filterPolicy = gapRole_AdvFilterPolicy;//過濾策略GAP_FILTER_POLICY_ALL;允許掃描,允許連接
 
      if ( GAP_MakeDiscoverable( gapRole_TaskID, ¶ms ) != SUCCESS ) //配置廣播參數(shù),并產(chǎn)生一個(gè)GAP_MakeDiscoverable      消息事件
      {
       gapRole_state = GAPROLE_ERROR;//如果不成功將通知回調(diào)函數(shù)-鏈路錯(cuò)誤   

        // Notify the application with the new state change
        if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange )
        {
          pGapRoles_AppCGs->pfnStateChange( gapRole_state );
        }
      }
    }
    return ( events ^ START_ADVERTISING_EVT );
  }
  1. 處理GAP_MakeDiscoverable消息事件
 static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)
 
  switch ( pMsg->opcode )
  {
      case GAP_MAKE_DISCOVERABLE_DONE_EVENT:  //使能可被發(fā)現(xiàn)完成事件即開始廣播了
      case GAP_END_DISCOVERABLE_DONE_EVENT:   //結(jié)束可被發(fā)現(xiàn)完成事件即停止廣播了
      {
        gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;
        if ( pPkt->hdr.status == SUCCESS )
        {
          if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
          {
           gapRole_state = GAPROLE_ADVERTISING;  //設(shè)置當(dāng)前鏈路狀態(tài)
          }
          else // GAP_END_DISCOVERABLE_DONE_EVENT//結(jié)束可被發(fā)現(xiàn)完成事件即停止廣播了
          {
            if ( gapRole_AdvertOffTime != 0 )  //如果gapRole_AdvertOffTime等于0,將不再?gòu)V播,否則啟動(dòng)定時(shí)廣播件
            {
              if ( ( gapRole_AdvEnabled ) )//如果使能廣播
              {
            VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//啟動(dòng)周期廣播事件
              }
            }
            else
            {
              // Since gapRole_AdvertOffTime is set to 0, the device should not
              // automatically become discoverable again after a period of time.
              // Set enabler to FALSE; device will become discoverable again when
              // this value gets set to TRUE
              gapRole_AdvEnabled = FALSE;
            }

            // In the Advertising Off period
            gapRole_state = GAPROLE_WAITING;//如果GAP_END_DISCOVERABLE_DONE_EVENT,鏈路當(dāng)前狀態(tài)為等待狀態(tài)
          }
        }
        else
        {
          gapRole_state = GAPROLE_ERROR;
        }
        notify = TRUE;//通知回調(diào)函數(shù)
      }
      break;

  if ( notify == TRUE )
  {
      // Notify the application with the new state change
      if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判斷是否設(shè)置了回調(diào)函數(shù)
      {
           pGapRoles_AppCGs->pfnStateChange( gapRole_state );//調(diào)用設(shè)置的回調(diào)函數(shù),通知gapRole_state當(dāng)前狀態(tài)
      }
  }
  1. 這時(shí)候底層已經(jīng)使能硬件在廣播了,要么廣播超時(shí)產(chǎn)生一個(gè)GAP_END_DISCOVERABLE_DONE_EVENT消息,要么被連接事件 GAP_LINK_ESTABLISHED_EVENT;

  2. 廣播超時(shí)產(chǎn)生一個(gè)GAP_END_DISCOVERABLE_DONE_EVENT消息

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)
  switch ( pMsg->opcode )
  {  
  case GAP_MAKE_DISCOVERABLE_DONE_EVENT:  //使能可被發(fā)現(xiàn)完成事件即開始廣播了
      case GAP_END_DISCOVERABLE_DONE_EVENT:   //結(jié)束可被發(fā)現(xiàn)完成事件即停止廣播了
      {
        gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;

        if ( pPkt->hdr.status == SUCCESS )
        {
          if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
          {
            gapRole_state = GAPROLE_ADVERTISING;  //設(shè)置當(dāng)前鏈路狀態(tài)
          }
          else // GAP_END_DISCOVERABLE_DONE_EVENT//結(jié)束可被發(fā)現(xiàn)完成事件即停止廣播了
          {
            if ( gapRole_AdvertOffTime != 0 )  //如果gapRole_AdvertOffTime不等于0,啟動(dòng)定時(shí)廣播事件,否則將關(guān)閉廣播
            {
              if ( ( gapRole_AdvEnabled ) )//如果使能廣播
              {
            VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//啟動(dòng)周期廣播事件
              }
            }
            else
            {
              // Since gapRole_AdvertOffTime is set to 0, the device should not
            // automatically become discoverable again after a period of time.
              // Set enabler to FALSE; device will become discoverable again when
              // this value gets set to TRUE
              gapRole_AdvEnabled = FALSE; //關(guān)閉廣播
            }
           // In the Advertising Off period
             gapRole_state = GAPROLE_WAITING;//如果GAP_END_DISCOVERABLE_DONE_EVENT,鏈路當(dāng)前狀態(tài)為等待狀態(tài),或不再?gòu)V播或等待周期廣播          
            }
        }
        else
        {
          gapRole_state = GAPROLE_ERROR;
        }
        notify = TRUE;//通知回調(diào)函數(shù)
      }
      break;
  if ( notify == TRUE )
  {
      // Notify the application with the new state change
      if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判斷是否設(shè)置了回調(diào)函數(shù)
      {
           pGapRoles_AppCGs->pfnStateChange( gapRole_state );//調(diào)用設(shè)置的回調(diào)函數(shù),通知gapRole_state當(dāng)前狀態(tài)
      }
  }
  1. 廣播時(shí)產(chǎn)生一個(gè)GAP_LINK_ESTABLISHED_EVENT消息
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //鏈路處理連接消息
{
  uint8 notify = FALSE;   // State changed notify the app? (default no)
  switch ( pMsg->opcode )
  {  
    case GAP_LINK_ESTABLISHED_EVENT:
      {
        gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
        if ( pPkt->hdr.status == SUCCESS )
        {
          VOID osal_memcpy( gapRole_ConnectedDevAddr, pPkt->devAddr, B_ADDR_LEN );//保存主機(jī)的地址
          gapRole_ConnectionHandle = pPkt->connectionHandle; //保存主機(jī)連接句柄
          gapRole_state = GAPROLE_CONNECTED;  //通知鏈路狀態(tài):連接成功
              notify = TRUE;
        }
      }
 }
聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場(chǎng)。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請(qǐng)聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 2
收藏 4
關(guān)注 181
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧