nrf51822-提高nordic ble数据发送速率
講解2點:
?????? 為什么?nordic的4.0協議棧中ble只能發送20字節的應用負載數據。
?????? 大量數據發送時如何提高發送速率
?
1:為何上層應用負載每次最多20字節
?
首先了解 4.0中鏈路層的包格式如下:
?????? PDU即協議數據單元,即鏈路層的負載數據。應用層用戶發送的數據就是在這里面,但是并不全是用戶數據。
Ble有分廣播態和連接態。 所以上面的這個鏈路層幀可能是廣播數據也可能是連接后的數據。 所以就有兩種情況,一種為 廣播通道中的PDU,另一種為數據通道中的PDU。我們主要討論的是連接態下的數據通道中的數據幀,這里廣播通道下簡單介紹下。
廣播態下,廣播幀中的PDU如下圖所示,包含2字節的頭,其后的payload即為廣播數據,比如通常我們設置的 設備名,廠商自定義數據等都在這里面,廣播數據肯定包含設備的地址,所以payload中的前6字節為設備地址
再看下 連接態下 數據通道中 鏈路層幀中的PDU組成,與廣播通道幀中的PDU類似,也是有2字節頭,隨后為payload即鏈路層的真實負載數據。
MIC為4字節,只有在鏈路加密的情況下才會存在,為 消息完整性校驗,防止消息被篡改。
PS:加密鏈路中的空包不會存在MIC
協議都是分層的,ble也一樣,那么鏈路層的負載數據payload即為上層協議的數據幀,鏈路層的上一層協議為L2CAP,而L2CAP的幀格式如下如所示前4字節分別為長度和信道值。
PS:如果上圖Header中的LLID為3,則其后的負載為鏈路層控制報文而不是L2CAP層幀,這里不介紹。
同樣,L2CAP層的負載數據information payload為上層協議的數據幀,對于傳輸用戶數據而言,設備作為主機時用write寫數據到從機,設備作為從機是用notify或indication 發送數據給主機,這時候l2CAP層的負載中包含的就是 上層ATT的協議幀。
這里討論的是用戶發送數據為什么是限制為最大20字節,所以了解下ATT協議中的write,notify,indication的命令格式就可以了。
如上圖所示,包含1字節opcode用來指示 write,notify,indication。2字節handle為句柄用來標識是操作哪個特性值的。 之后就是真正用戶發送的數據了。
所以最終限制能一次發送多少數據就是這個 ATT_MTU 為多少了。
規范中默認這個MTU最小為23字節,這個值其實是可以通過命令來協商的,而nordic的4.0協議棧中默認只支持默認值即23,所以也就限制了最終上層一次發送的數據限制在 20字節。
nrf52832使用的最新的s132協議棧中已經開始支持MTU的協商了,這樣就可以一次傳輸更多數據了。
?
綜上,鏈路層的PDU中的數據如下圖所示:
PS:回顧最開始的鏈路層 幀結構可以看到 PDU中允許的長度為2-39,即最少有2字節頭,有效負載數據最多37字節。
但是從ATT協議往鏈路層看,ATT 最多20字節用戶數據,加上3字節頭,加上L2CAP的4字節頭,也就27字節,為什么會有差額10字節?
原因在于 PDU因為分情況有廣播通道的PDU,和數據通道的PDU,PDU除了2字節頭,有效負載為37字節,在廣播數據中PDU需要包含6字節的廣播地址,其他廣播數據也就只有31字節了。但是數據通道中并不需要,但是為了簡單起見,也就限制了數據通道中有效負載數據最多31字節。 另一方面 如果鏈路加密了,數據通道中的PDU,最后會包含4字節的MIC,那么加密的有效負載數據就變成27字節了,這里又為了方便起見,也就讓即使不加密的鏈路發送的有效負載數據也為27。這就是差額的原因。
?
第二個問題:既然每次發送數據最多才20字節,如果發送較多數據時如何提高發送速率?
以 ble_app_uart例子來說明,該例子中設備作為從機,已經實現了一個以notify方式向手機發送數據的函數。這里就直接利用這個發送函數。
一些簡單的應用中通常可能很久才發送一次數據,數據的發送量也沒有達到20字節,這種情況下 直接調用該函數發送數據就可以了。
?
另一種情況,發送的數據比較多,但是對發送的速率并沒有要求。這種情況最簡單的可以直接用一個循環發送就可以了
While(沒發送完){
?????? ble_nus_string_send(數據);
?????? delay_ms(n);
}
通常發送的數據越多delay_ms延遲的時間要越久一點,這個要自己試驗。通常只能用在一些少量數據比如一兩百字節。
?
更規范的做法應該利用協議棧中的 發送完成事件?BLE_EVT_TX_COMPLETE,這個事件是在底層發送數據完成后由協議棧發上拋給應用層的。
那么就可以利用這個事件,首先發送20字節,當底層發送完成后上層收到這個 發送完成事件后再發送后續數據。
?
?
這里做一個簡單的實現
點擊(此處)折疊或打開
?
修改一下Main函數,定義一個全局的buff用來放數據并初始化,將for循環中的power_manager去掉改成通過一個按鍵來啟動發送 buff中的數據。
燒寫程序,手機連接上后使能 特性值的notify功能,然后按鍵便會受到設備發給手機的100字節數據
?
?
?
啟動發送后只會發送前20字節,當這20字節發送完成后會收到BLE_EVT_TX_COMPLETE事件,在該事件處理中添加剩余數據的發送
直接在on_ble_evt事件處理函數中添加一下這個事件的處理
上面的實現只是針對 對發送速率沒要求的情況,這里抓包看一下實際的交互過程。
部分截圖如下
?
因為每個連接事件到來時都會切換到另一個通道(頻率)上進行數據傳輸,而在這個連接事件持續時間中的數據交互都是在同一個通道上。
即每個連接事件到來時都會切換通道,但是一個連接事件內部的通信都始終在那個通道上
所以由通道號可以區分出來這里基本上是兩個連接事件才會發送一次數據,這樣效率就很低,因為實際的底層基帶發送是很快的1Mbit/s, 也就是1us發送1bit。理論上簡單算一下,這里就直接以鏈路層最長包來算,1+4+39+3 也就只有47字節,
47*8也就是發送一包的實際時間不足1ms,算上基帶啟動發送以及協議棧的一些處理也應該是幾ms的事,那么一個連接間隔除了最前面的幾毫秒發送了一下數據,之后這次連接間隔就關了。等之后的連接間隔到來才會繼續發送后續數據。那么發送效率就很低。
如果提高每個連接間隔中發送的數據包的數量,那么就可以提高發送速率。
前面的方法是調用每次發送函數后等待 完成事件,實際上,這個協議棧的底層應該有一個自己的發送buff,能存放一定數據,我們調用發送數據后協議棧會將數據放到這個buff中,最終再發送這個buff中的數據。
如果能在下個連接事件到來前竟可能的將多的數據放入這個協議棧中的buff里,那么他下次連接間隔發送的數據就變多了。
?
Sdk其實提供了這種方法,只不過比較隱晦。
我們利用的發送函數ble_nus_string_send,實際是調用了sd_ble_gatts_hvx 這個協議棧api函數,這個函數有一個返回值NRF_ERROR_BUSY?表示忙,正在處理。
這應該是表示開始發送了。
那么就可以直接重復調用這個ble_nus_string_send 函數直到其返回NRF_ERROR_BUSY 錯誤,表示已經開始發送了,不能再處理你提交的數據。
?
另外,協議棧中的buff肯定是有限的,如果我們調用這個發送函數的時候,即將到來下一個連接事件,那么buff肯定填不滿,最終出現的錯誤是NRF_ERROR_BUSY,表示已經開始發送了,你不能再填了。
?
但是如果調用的時候恰好離下一次連接事件到來還比較久,那么就會出現將協議棧中的buff填滿了,從而出現BLE_ERROR_NO_TX_BUFFERS?這個錯誤。
?
這里只是介紹這兩種錯誤,實際實現中可以不需要去判斷是不是這些錯誤,因為發送是分包一點一點發送的,我們可以直接就判斷?ble_nus_string_send函數調用是不是返回NRF_SUCCESS,如果是才 更新 發送偏移,并且繼續循環調用該函數以填更多數據到協議棧buff中,如果返回值不正確,那么直接跳出,不更新發送偏移就可以了,而并不用去區分是BUSY錯誤還是NO BUFF錯誤。
?
點擊(此處)折疊或打開
最后再修改一下main函數,發送500個字節
?
?
?
燒寫程序后運行代碼,我們再次抓一下空中包看看是否每個連接間隔中發送了多個數據包
由通道號可以看到現在一個連接事件中發送了多個包(最多6個)
總結
以上是生活随笔為你收集整理的nrf51822-提高nordic ble数据发送速率的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BLE 包结构及传输速率
- 下一篇: BLE】CC2541之通过RSSI测距