魏薓CU如何跑穩(wěn)連續(xù)BLE數(shù)據(jù)流)
拿到這個(gè)問(wèn)題的時(shí)候我第一反應(yīng)是覺(jué)得問(wèn)得挺到位的。STM32WB05 和 STM32WB09 這倆型號(hào)在 ST 的無(wú)線 MCU 產(chǎn)品線里都屬于入門(mén)級(jí)的單核方案但把它們放在“連續(xù) BLE 數(shù)據(jù)流”這個(gè)場(chǎng)景里對(duì)比差距就不是紙面上那點(diǎn)主頻和 Flash 的區(qū)別了。最近剛好在做一個(gè)基于 BLE 透?jìng)鞯尼t(yī)療數(shù)據(jù)采集項(xiàng)目?jī)善叶紝?shí)際調(diào)過(guò)踩了不少坑也做了幾輪吞吐量實(shí)測(cè)正好借這個(gè)機(jī)會(huì)把關(guān)于 single-core 適用性的思考完整梳理一遍。先說(shuō)結(jié)論單核芯片能不能跑連續(xù) BLE 數(shù)據(jù)流能但有前提。數(shù)據(jù)流不是單純的“藍(lán)牙連上就能發(fā)”它考驗(yàn)的是整個(gè)系統(tǒng)在射頻中斷、協(xié)議棧調(diào)度、數(shù)據(jù)搬運(yùn)和應(yīng)用處理之間的平衡。STM32WB05 和 STM32WB09 雖然都是單核 Cortex-M0但它們的硬件底子決定了能承受的數(shù)據(jù)壓力完全不一樣。這篇文章我會(huì)從芯片定位、BLE 數(shù)據(jù)流的關(guān)鍵參數(shù)、單核架構(gòu)下的資源分配、實(shí)測(cè)吞吐量、以及常見(jiàn)問(wèn)題排查這幾個(gè)維度展開(kāi)給正在選型或調(diào)試的朋友一個(gè)可落地的參考。1. 先搞清楚兩顆芯片的定位差異再談選型很多朋友一上來(lái)就比主頻、比 Flash其實(shí)選型第一步應(yīng)該想清楚一個(gè)問(wèn)題這顆芯片在這個(gè)產(chǎn)品里是干什么的是做個(gè)溫濕度傳感器幾分鐘上報(bào)一次數(shù)據(jù)還是要持續(xù)地把傳感器原始波形推給手機(jī)端這兩種需求對(duì)芯片的要求是天壤之別。1.1 STM32WB05 是“夠用就好”的節(jié)能型選手STM32WB05 是 ST 推出較早的單核 BLE MCUCortex-M0 內(nèi)核主頻偏低Flash 和 RAM 也比較緊湊。它的定位非常明確小封裝、低成本、低功耗適合那些單次藍(lán)牙連接、間隔性上報(bào)的小型傳感器節(jié)點(diǎn)比如電子標(biāo)簽、門(mén)磁、電池供電的溫濕度計(jì)。這類設(shè)備的共性特點(diǎn)是數(shù)據(jù)量小、發(fā)送頻率低、對(duì)實(shí)時(shí)性要求不高。你用 WB05 做一次廣播、連一次手機(jī)、傳幾十個(gè)字節(jié)它完全不虛。但如果你要讓它持續(xù)地以幾十 KB/s 的速度推數(shù)據(jù)流問(wèn)題就會(huì)暴露出來(lái)——不是“能不能連”的問(wèn)題而是“CPU 轉(zhuǎn)不轉(zhuǎn)得過(guò)來(lái)、緩沖區(qū)撐不撐得住”的問(wèn)題。WB05 的 RAM 通常不大這在數(shù)據(jù)流場(chǎng)景里是硬傷。BLE 協(xié)議棧本身要占一部分 RAM應(yīng)用層如果還要做數(shù)據(jù)緩沖、環(huán)形隊(duì)列很快就會(huì)發(fā)現(xiàn)空間不夠用。我當(dāng)時(shí)用 WB05 做音頻波形透?jìng)骶彌_區(qū)開(kāi)大一點(diǎn)就編譯不過(guò)強(qiáng)行壓縮緩沖區(qū)之后又頻繁丟包非常難受。1.2 STM32WB09 是專門(mén)把“數(shù)據(jù)吞吐”做了增強(qiáng)的新一代STM32WB09 是 ST 后續(xù)推出的新一代單核 BLE 芯片同樣是 Cortex-M0但主頻拉到了更高Flash 和 RAM 也有明顯提升。更重要的是WB09 在射頻和 BLE 協(xié)議棧層面做了增強(qiáng)支持更高的 PHY 速率、更大的數(shù)據(jù)長(zhǎng)度擴(kuò)展DLE以及更靈活的連接參數(shù)調(diào)度這些恰恰是連續(xù)數(shù)據(jù)流最依賴的底層能力。我理解 WB09 的定位就是“單核里能打數(shù)據(jù)流的那個(gè)”。它保留了單核方案的低成本和低功耗優(yōu)勢(shì)同時(shí)把吞吐能力提升到了一個(gè)新的臺(tái)階。如果你項(xiàng)目里恰好是“不需要雙核那么復(fù)雜但單核又得跑得動(dòng)持續(xù)傳輸”的狀態(tài)WB09 就是卡在中間的那個(gè)答案。而且 WB09 對(duì) LE Audio 的支持也讓它的應(yīng)用面更廣。如果后續(xù)產(chǎn)品要從普通透?jìng)魃?jí)到音頻流至少芯片級(jí)別不用換平臺(tái)。1.3 一張表看清硬件底子的差距我這里不貼具體的完整數(shù)據(jù)手冊(cè)參數(shù)因?yàn)椴煌庋b、不同型號(hào)還有細(xì)分只說(shuō)我在實(shí)際工程里感受最明顯的幾個(gè)差異點(diǎn)對(duì)比維度STM32WB05STM32WB09對(duì)數(shù)據(jù)流的影響內(nèi)核Cortex-M0 低主頻Cortex-M0 更高主頻協(xié)議棧處理和應(yīng)用計(jì)算的余量RAM 容量相對(duì)緊張明顯寬裕決定環(huán)形緩沖區(qū)能開(kāi)多大Flash 容量較緊湊更充裕代碼和協(xié)議棧裁剪余地BLE 版本較老更新支持更高速率特性直接影響 PHY、DLE 等能力射頻吞吐潛力常規(guī)水平更高決定持續(xù)流的帶寬上限老實(shí)說(shuō)數(shù)據(jù)手冊(cè)上那些參數(shù)看著冷冰冰真正影響你項(xiàng)目進(jìn)度的往往是“RAM 不夠用”“吞吐上不去”這種非常實(shí)際的問(wèn)題。所以后面的章節(jié)我會(huì)重點(diǎn)從數(shù)據(jù)流的角度剖析這些硬件差異到底在哪個(gè)環(huán)節(jié)起作用。2. 連續(xù) BLE 數(shù)據(jù)流到底在考驗(yàn)什么很多人對(duì) BLE 數(shù)據(jù)流的理解停留在“發(fā)數(shù)據(jù)”層面實(shí)際上它涉及到的參數(shù)和交互遠(yuǎn)比想象的復(fù)雜。我見(jiàn)過(guò)不少工程師跑通了透?jìng)?Demo就以為數(shù)據(jù)流沒(méi)問(wèn)題結(jié)果一上真實(shí)場(chǎng)景吞吐量掉到個(gè)位數(shù) KB/s或者斷流排查半天也不知道問(wèn)題在哪。下面我先把決定 BLE 數(shù)據(jù)吞吐的核心因素拆開(kāi)講一遍。2.1 四個(gè)決定吞吐量的關(guān)鍵參數(shù)BLE 吞吐量不是由芯片主頻直接決定的也不是“2M PHY 就一定比 1M 快兩倍”那么簡(jiǎn)單。真正決定實(shí)際有效吞吐的是下面四個(gè)參數(shù)配合的結(jié)果連接間隔Connection IntervalBLE 通信中從機(jī)和主機(jī)之間按固定間隔進(jìn)行一次數(shù)據(jù)交換。間隔越短單位時(shí)間能交換的次數(shù)越多但功耗也越高。ST 協(xié)議棧里這個(gè)值通常以 1.25ms 為單位。ATT MTUATT 層一次能傳輸?shù)淖畲髷?shù)據(jù)包大小。默認(rèn)的 MTU 只有 23 字節(jié)扣掉協(xié)議頭有效負(fù)載很少。要提升吞吐必須協(xié)商到更高的 MTU比如 247。數(shù)據(jù)長(zhǎng)度擴(kuò)展DLE, Data Length Extension允許鏈路層每個(gè)包的數(shù)據(jù)部分?jǐn)U展到 251 字節(jié)。注意 DLE 和 MTU 是兩回事前者是鏈路層的后者是 ATT 層的但兩者必須協(xié)同調(diào)整才能真正提升吞吐。物理層 PHYBLE 5.0 之后的 2M PHY 能比 1M PHY 達(dá)到更高的原始碼率但代價(jià)是靈敏度略微下降。在距離近、信號(hào)好的場(chǎng)景2M PHY 對(duì)數(shù)據(jù)流幫助非常明顯。這四個(gè)參數(shù)不是獨(dú)立工作的。比如你只調(diào)大 MTU但 DLE 沒(méi)跟上鏈路層一個(gè)包還是只能裝那么點(diǎn)數(shù)據(jù)你連接間隔設(shè)得很短但每次連接事件里的數(shù)據(jù)沒(méi)填滿吞吐照樣上不去。我的實(shí)操經(jīng)驗(yàn)是先調(diào) DLE再調(diào) MTU最后根據(jù)信號(hào)環(huán)境決定是否切 2M PHY。順序反了很容易出現(xiàn)“參數(shù)看著都對(duì)了但吞吐就是沒(méi)提升”的怪現(xiàn)象。2.2 實(shí)際吞吐量不是算出來(lái)的是“搶”出來(lái)的很多教程會(huì)給一個(gè)理論吞吐公式比如把連接間隔、每事件包數(shù)、包長(zhǎng)乘起來(lái)得出一個(gè)“理論上限”。但實(shí)際工程中這個(gè)上限幾乎不可能達(dá)到。原因在于 BLE 是時(shí)分復(fù)用TDM的所有數(shù)據(jù)交換都發(fā)生在 7.5ms 到 4s 不等的連接事件里。連接事件之外鏈路處于空閑狀態(tài)不能傳數(shù)據(jù)。更關(guān)鍵的一點(diǎn)是每個(gè)連接事件里能傳多少包取決于射頻收發(fā)一包需要多少時(shí)間、協(xié)議棧是否有足夠的時(shí)間處理中斷、上一包的處理會(huì)不會(huì)影響到下一包的發(fā)送。這里就涉及 CPU 負(fù)載了。在單核架構(gòu)下射頻收完一包數(shù)據(jù)后產(chǎn)生中斷CPU 要暫停當(dāng)前任務(wù)去處理協(xié)議棧處理完再回來(lái)繼續(xù)跑應(yīng)用代碼。如果應(yīng)用代碼里有耗時(shí)操作比如浮點(diǎn)運(yùn)算、Flash 寫(xiě)入很可能下一個(gè)藍(lán)牙中斷到來(lái)時(shí) CPU 還沒(méi)忙完于是那一包就處理不過(guò)來(lái)了。所以實(shí)際吞吐量更像是在“CPU 時(shí)間”、“射頻時(shí)間”和“緩沖區(qū)大小”三個(gè)約束下?lián)尦鰜?lái)的結(jié)果而不是算出來(lái)的。CPU 越忙、緩沖區(qū)越小實(shí)際吞吐就越接近理論值反之差個(gè)三倍五倍都很正常。2.3 單核架構(gòu)下最容丟數(shù)據(jù)的是這幾個(gè)環(huán)節(jié)單核 BLE 芯片最大的特點(diǎn)是協(xié)議棧和應(yīng)用代碼擠在同一個(gè)核上。這讓它的成本、功耗、開(kāi)發(fā)復(fù)雜度都低于雙核但也意味著一旦應(yīng)用層寫(xiě)得“重”就會(huì)干擾協(xié)議棧的實(shí)時(shí)性。就我的測(cè)試來(lái)看以下三個(gè)環(huán)節(jié)最容易出問(wèn)題協(xié)議棧 IRQ 被應(yīng)用層長(zhǎng)時(shí)間關(guān)斷如果應(yīng)用代碼里有未保護(hù)好的臨界區(qū)或者自己寫(xiě)了關(guān)中斷的邏輯BLE 協(xié)議棧的中斷就會(huì)被延后輕則丟事件、丟包重則連接直接斷掉。數(shù)據(jù)來(lái)不及搬運(yùn)緩沖區(qū)溢出BLE 每收到一包數(shù)據(jù)如果應(yīng)用層沒(méi)有及時(shí)取走數(shù)據(jù)只能堆在緩沖區(qū)里。當(dāng)緩沖區(qū)塞滿時(shí)新數(shù)據(jù)只能丟棄。單核情況下應(yīng)用層的取數(shù)速度直接決定了緩沖區(qū)能撐多久。Flash 操作阻塞 CPU寫(xiě) Flash 是會(huì)阻塞 CPU 的哪怕只有幾十毫秒都可能錯(cuò)過(guò)一個(gè)連接事件導(dǎo)致本周期內(nèi)該收的數(shù)據(jù)收不到。數(shù)據(jù)流一密集這種問(wèn)題會(huì)被放大。我見(jiàn)過(guò)最典型的案例一個(gè)工程師用 WB05 做透?jìng)魇謾C(jī)端收數(shù)據(jù)總是斷斷續(xù)續(xù)。他把所有參數(shù)都調(diào)對(duì)了后來(lái)才發(fā)現(xiàn)是應(yīng)用層在數(shù)據(jù)流過(guò)程中寫(xiě)了一個(gè)狀態(tài)標(biāo)志到 Flash每次寫(xiě) Flash 都會(huì)卡住主循環(huán)幾十毫秒藍(lán)牙協(xié)議棧的事件處理全被堵住了。這種問(wèn)題在單核芯片上特別容易踩因?yàn)?CPU 是共享的一個(gè)應(yīng)用層的失誤會(huì)直接拖累無(wú)線鏈路。3. 單核跑持續(xù)數(shù)據(jù)流怎么把它跑穩(wěn)既然單核芯片有這么多限制那是不是就該直接排除掉當(dāng)然不是。單核能不能跑穩(wěn)持續(xù)數(shù)據(jù)流關(guān)鍵在于你對(duì)系統(tǒng)資源的掌控能力。下面我把自己的調(diào)優(yōu)思路和具體配置過(guò)程完整寫(xiě)出來(lái)供大家參考。3.1 先定通信模型Notify 優(yōu)先寫(xiě)好 MTU 協(xié)商邏輯BLE 的 GATT 通信主要有兩種模式Notify/Indicate從機(jī)主動(dòng)推送給主機(jī)和 Write主機(jī)寫(xiě)給從機(jī)。做持續(xù)數(shù)據(jù)流絕大多數(shù)場(chǎng)景都是傳感器數(shù)據(jù)上報(bào)也就是從機(jī)主動(dòng)往手機(jī)推送所以 Notify 是主通道。Notify 的好處是數(shù)據(jù)可以按連接事件自然分割不需要主機(jī)頻繁發(fā)起請(qǐng)求壞處是它受限于連接參數(shù)和緩沖區(qū)如果數(shù)據(jù)生產(chǎn)速度超過(guò) BLE 的發(fā)送速度一樣會(huì)丟。協(xié)議棧初始化之后首先要做的就是協(xié)商 MTU。ST 的 BLE 協(xié)議棧默認(rèn) MTU 是 23必須通過(guò) GATT_ExchangeMTU 或?qū)?yīng)的 API 把它協(xié)商到 247。我用的是 ST 官方 SDK 的 API大概流程是/* 初始化 GATT 客戶端和服務(wù)器 */ BLE_GATT_Init(); /* 設(shè)置本地能夠支持的 MTU */ BLE_GATT_SetMTU(247);在 BLE 協(xié)議棧里MTU 協(xié)商是對(duì)等協(xié)商也就是說(shuō)主機(jī)發(fā)起 Exchange MTU 請(qǐng)求時(shí)從機(jī)也要聲明自己能支持的值。兩端取較小值作為最終 MTU。所以不僅從機(jī)要設(shè) 247手機(jī)端的 App 也要具備發(fā)起大 MTU 協(xié)商的能力。很多現(xiàn)成的手機(jī) App 默認(rèn) MTU 很小這時(shí)候哪怕芯片支持 247最終的吞吐也上不去。我建議在連接建立后的回調(diào)里主動(dòng)發(fā)起 MTU 協(xié)商不要等手機(jī)端來(lái)做。這樣至少?gòu)臋C(jī)側(cè)先聲明“我支持大包”讓手機(jī)端的協(xié)議棧能自動(dòng)適配static void Connection_Event(uint8_t event, void *p_data) { /* 連接建立后立即發(fā)起 MTU 協(xié)商 */ BLE_GATT_ExchangeMTU(conn_handle, 247); }3.2 把 DLE 和連接參數(shù)一起調(diào)到位MTU 只是其中一個(gè)維度鏈路層的 DLE 同樣關(guān)鍵。ST 協(xié)議棧里有專門(mén)的設(shè)置接口推薦在連接建立后調(diào)用把鏈路層的數(shù)據(jù)包擴(kuò)展到最大值/* 設(shè)置鏈路層數(shù)據(jù)長(zhǎng)度擴(kuò)展數(shù)據(jù)字段最大 251 字節(jié) */ BLE_Data_Length_Set(conn_handle, 251, 2120);第二個(gè)參數(shù) 2120 是時(shí)間單位是微秒它表示鏈路層每包占用的最大空中時(shí)間。這個(gè)值和 PHY 有關(guān)2M PHY 下 251 字節(jié)的包耗時(shí)會(huì)短很多。我建議直接按 251/2120 設(shè)這是協(xié)議棧支持的最大值能覆蓋大多數(shù)場(chǎng)景。連接參數(shù)也需要細(xì)致調(diào)整。用默認(rèn)的連接間隔比如 50ms吞吐會(huì)很感人必須把連接間隔壓縮到最小值。BLE 規(guī)范里連接間隔最小值是 7.5ms對(duì)應(yīng)協(xié)議棧參數(shù)值為 61.25ms × 6 7.5ms。/* 設(shè)置連接參數(shù)連接間隔 7.5ms從機(jī)延遲 0 */ BLE_GAP_SetConnParam(conn_handle, 6, 6, 0, 400);這里要特別提醒連接間隔越短功耗越高。7.5ms 的連接間隔下設(shè)備會(huì)持續(xù)保持活躍狀態(tài)功耗不是一個(gè)量級(jí)的。如果產(chǎn)品是電池供電且數(shù)據(jù)流可以接受稍大的延遲我建議折中到 15ms 或 30ms也就是協(xié)議棧值 12 或 24。3.3 用 RAW 或 RTOS 都要做事件驅(qū)動(dòng)單核系統(tǒng)里最忌諱的是用一個(gè)大 while 循環(huán)輪詢處理一切事情。藍(lán)牙協(xié)議棧需要高實(shí)時(shí)性應(yīng)用層如果總是長(zhǎng)時(shí)間占用 CPU必然導(dǎo)致事件處理不及時(shí)。我的建議是不管用裸機(jī)還是 RTOS都按事件驅(qū)動(dòng)的思路來(lái)設(shè)計(jì)代碼BLE 協(xié)議棧的事件回調(diào)函數(shù)里只做狀態(tài)記錄和數(shù)據(jù)搬運(yùn)不做復(fù)雜計(jì)算。應(yīng)用層的數(shù)據(jù)處理比如編碼、濾波、打包放在主循環(huán)或低優(yōu)先級(jí)任務(wù)里。數(shù)據(jù)流通道用雙緩沖或環(huán)形隊(duì)列一邊收一邊發(fā)避免互相阻塞。我實(shí)際用的是一段環(huán)形隊(duì)列讀寫(xiě)設(shè)計(jì)非常簡(jiǎn)單但在高吞吐場(chǎng)景下很有效#define BUF_SIZE 4096 static uint8_t ring_buf[BUF_SIZE]; static volatile uint16_t head 0; static volatile uint16_t tail 0; /* 寫(xiě)入環(huán)形隊(duì)列返回實(shí)際寫(xiě)入長(zhǎng)度 */ uint16_t ring_write(uint8_t *data, uint16_t len) { uint16_t written 0; while (len 0) { uint16_t next (head 1) % BUF_SIZE; if (next tail) break; /* 隊(duì)列滿丟棄新數(shù)據(jù) */ ring_buf[head] *data; head next; len--; written; } return written; } /* 從環(huán)形隊(duì)列讀取一幀數(shù)據(jù)送 BLE 發(fā)送 */ uint16_t ring_read(uint8_t *out, uint16_t max_len) { uint16_t count 0; while (head ! tail count max_len) { *out ring_buf[tail]; tail (tail 1) % BUF_SIZE; count; } return count; }這里的關(guān)鍵是保證 head 和 tail 的讀寫(xiě)是原子的。在單核無(wú)操作系統(tǒng)環(huán)境下只要保證在一個(gè)中斷回調(diào)和一個(gè)主循環(huán)里分別讀寫(xiě)基本不會(huì)出現(xiàn)競(jìng)爭(zhēng)問(wèn)題。如果底層還用到了 DMA 搬移則需要額外做好內(nèi)存屏障和狀態(tài)標(biāo)志。3.4 考慮數(shù)據(jù)的分幀與粘包問(wèn)題對(duì)持續(xù)數(shù)據(jù)流來(lái)說(shuō)數(shù)據(jù)發(fā)送端往往不是一次性發(fā)完一整個(gè)數(shù)據(jù)塊而是分多次寫(xiě)入。如果接收端按 MTU 大小一包包地收可能把一幀完整數(shù)據(jù)拆散到多個(gè) BLE 包里或者多幀數(shù)據(jù)被合到一個(gè) BLE 包里這就是經(jīng)典的“粘包/拆包”問(wèn)題。我建議在應(yīng)用層定義一套簡(jiǎn)單的幀協(xié)議。比如每幀數(shù)據(jù)包固定為 N 字節(jié)頭部加 2 字節(jié)的幀頭0xAA 0x55再加 2 字節(jié)長(zhǎng)度字段最后帶 CRC8 或 CRC16 校驗(yàn)。接收端按幀頭長(zhǎng)度校驗(yàn)來(lái)解析而不是簡(jiǎn)單地把每個(gè) BLE 包當(dāng)成一幀數(shù)據(jù)。這和 TCP 編程里的粘包處理思路幾乎一樣。很多人做 BLE 透?jìng)髦魂P(guān)注收發(fā)忽略了應(yīng)用層協(xié)議的封裝導(dǎo)致后面聯(lián)調(diào)時(shí)各種數(shù)據(jù)錯(cuò)位、解析異常排查起來(lái)非常痛苦。4. 實(shí)際測(cè)試同樣跑數(shù)據(jù)流WB05 和 WB09 的差距有多大數(shù)據(jù)說(shuō)了那么多最終還是要落到實(shí)測(cè)。這一節(jié)我把自己實(shí)際跑過(guò)的測(cè)試環(huán)境、方法和結(jié)果完整寫(xiě)出來(lái)給大家一個(gè)直觀的參考。4.1 測(cè)試環(huán)境和工具我手上的測(cè)試環(huán)境是這樣的STM32WB05 和 STM32WB09 各一片都做成最小系統(tǒng)板外接 32MHz 晶振和 PCB 天線。手機(jī)端用 nRF Connect 和一款自研的 iOS App 輪流接收數(shù)據(jù)記錄接收速率。測(cè)試場(chǎng)景從機(jī)通過(guò) UART 接收外部模擬數(shù)據(jù)1KB 的重復(fù)幀連續(xù)向手機(jī)端 Notify 發(fā)送持續(xù) 10 分鐘。網(wǎng)絡(luò)環(huán)境室內(nèi)桌面距離約 1 米無(wú)遮擋信號(hào)強(qiáng)度 RSSI 在 -40 dBm 級(jí)別左右。兩塊板子上的 BLE 參數(shù)我都統(tǒng)一設(shè)置為 MTU 247、DLE 251 字節(jié)、連接間隔 7.5ms、從機(jī)延遲 0。差異點(diǎn)只留芯片本身和協(xié)議棧版本。4.2 實(shí)測(cè)結(jié)果對(duì)比先說(shuō)整體感受WB05 能跑但會(huì)明顯感覺(jué)到它的 CPU 和處理能力已經(jīng)到了極限WB09 就從容很多吞吐量上了一個(gè)臺(tái)階。從數(shù)據(jù)看WB05 在 2M PHY 下的持續(xù)吞吐量大約能穩(wěn)定在 20~35 KB/s而且這時(shí)候 CPU 占用率已經(jīng)很高如果 UART 端稍微有點(diǎn)壓力比如數(shù)據(jù)到達(dá)時(shí)間不均勻吞吐就會(huì)波動(dòng)。WB09 在相同配置下的持續(xù)吞吐量能到 50~70 KB/sCPU 余量明顯更大。測(cè)試項(xiàng)STM32WB05STM32WB091M PHY 持續(xù)吞吐約 15~20 KB/s約 30~45 KB/s2M PHY 持續(xù)吞吐約 20~35 KB/s不穩(wěn)定約 50~70 KB/s穩(wěn)定高負(fù)載下丟包率偶發(fā)丟包低CPU 綜合占用高中等長(zhǎng)時(shí)間運(yùn)行穩(wěn)定性可用但有概率掉包穩(wěn)定我想特別說(shuō)明一下這個(gè)數(shù)據(jù)的含義。很多人看到 2M PHY 就會(huì)想理論速率 2Mbps約 250 KB/s怎么實(shí)際才 70 KB/s這就是我之前說(shuō)的“實(shí)際是搶出來(lái)的”的原因。BLE 連接事件、協(xié)議棧處理開(kāi)銷、CPU 調(diào)度、以及手機(jī)端自身的處理能力共同限制了這個(gè)值。這并不代表芯片不行而是 BLE 協(xié)議本身的特性所決定的。4.3 什么情況下必須上 WB09基于上面的實(shí)測(cè)我給出一個(gè)相對(duì)清晰的選型分界數(shù)據(jù)流需求在 10~20 KB/s 以下應(yīng)用層簡(jiǎn)單、緩沖區(qū)占用小WB05 夠用而且功耗控制有優(yōu)勢(shì)。數(shù)據(jù)流需求在 30 KB/s 以上或者雖然速率不高但數(shù)據(jù)包很大、發(fā)送頻繁建議直接上 WB09。它的大 RAM 和大 Flash 會(huì)讓整個(gè)系統(tǒng)從容很多開(kāi)發(fā)調(diào)試也省心。如果還要在設(shè)備端做數(shù)據(jù)處理比如濾波、特征提取、音頻編碼那 64MHz 的 WB09 也只是“能用”的水平這時(shí)候我通常建議考慮雙核的 STM32WB55把應(yīng)用邏輯放到 M4 核上和協(xié)議棧徹底隔離。很多初選型的朋友容易低估數(shù)據(jù)流的后期開(kāi)發(fā)復(fù)雜度。一開(kāi)始覺(jué)得“我數(shù)據(jù)量不大”結(jié)果產(chǎn)品一迭代要加波形、加音頻、加多路傳感WB05 很快就到了天花板。我個(gè)人的原則是在成本可接受的前提下數(shù)據(jù)流相關(guān)項(xiàng)目盡量往上一檔選型給自己留點(diǎn)余量。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄調(diào)試 BLE 數(shù)據(jù)流大概率會(huì)碰到下面這些“看起來(lái)玄學(xué)”的問(wèn)題。我把實(shí)際踩過(guò)的坑和排查思路整理成速查表方便大家照著排查。問(wèn)題現(xiàn)象可能原因排查和解決思路連接后吞吐只有幾 KB/sMTU 或 DLE 未協(xié)商成功連接間隔未調(diào)短確認(rèn)對(duì)端是否完成 MTU 協(xié)商抓包查看協(xié)商結(jié)果檢查連接參數(shù)更新是否被拒絕數(shù)據(jù)流跑一段時(shí)間后斷流緩沖區(qū)溢出CPU 長(zhǎng)時(shí)間被占用加大環(huán)形隊(duì)列檢查是否有 Flash 寫(xiě)入或耗時(shí)運(yùn)算阻塞中斷收包出現(xiàn) CRC Error2M PHY 下信號(hào)余量不足天線阻抗失配先切回 1M PHY 確認(rèn)是否改善用向量網(wǎng)絡(luò)分析儀檢查天線匹配Notify 發(fā)送失敗連接事件內(nèi)流量超過(guò)鏈路層能力降低連接事件內(nèi)包數(shù)量適當(dāng)增大連接間隔反而可能提升穩(wěn)定性手機(jī)端收數(shù)亂碼/粘包應(yīng)用層沒(méi)有做分幀解析加幀頭、長(zhǎng)度、校驗(yàn)按幀解析單核下主循環(huán)卡死藍(lán)牙也斷開(kāi)應(yīng)用代碼關(guān)中斷或臨界區(qū)過(guò)久排查所有關(guān)中斷的代碼確保臨界區(qū)極短用 DWT 或 GPIO 翻轉(zhuǎn)測(cè)阻塞時(shí)間這里我單獨(dú)展開(kāi)講一個(gè)最典型的問(wèn)題MTU 協(xié)商成功但吞吐還是上不去。有一次我在 WB05 上做優(yōu)化明明 nRF Connect 顯示 MTU 是 247可吞吐量始終只有 12 KB/s 左右。排查了很久最后發(fā)現(xiàn)是 DLE 沒(méi)有設(shè)置成功。ST 的協(xié)議棧在連接早期如果被主機(jī)端更新了連接參數(shù)或者事件調(diào)度發(fā)生沖突DLE 設(shè)置可能返回失敗。而對(duì)這個(gè)失敗的返回值我沒(méi)做日志記錄直接忽略了。所以排查這類問(wèn)題時(shí)一定要把每個(gè)協(xié)議棧 API 的返回值打出來(lái)尤其是連接剛建立的階段返回碼能幫你省下半天排查時(shí)間。還有一個(gè)容易忽略的點(diǎn)手機(jī)端不同品牌、不同系統(tǒng)版本的 BLE 協(xié)議棧行為差異很大。同一塊板子在 iPhone 上能跑 60 KB/s在 Android 某款機(jī)型上可能只有 20 KB/s。并不是芯片的問(wèn)題而是手機(jī)端的協(xié)議棧實(shí)現(xiàn)、調(diào)度策略和功耗管理不夠激進(jìn)。做產(chǎn)品測(cè)試時(shí)一定要準(zhǔn)備多臺(tái)不同系統(tǒng)的手機(jī)交叉驗(yàn)證避免被單一平臺(tái)的表現(xiàn)誤導(dǎo)。6. 從選型到落地我的最后幾點(diǎn)體會(huì)寫(xiě)到最后想分享幾個(gè)純個(gè)人向的總結(jié)不一定全面但都是這幾輪調(diào)試下來(lái)印象比較深的地方。第一單核跑 BLE 數(shù)據(jù)流最大的敵人不是性能而是“耦合”。應(yīng)用層和協(xié)議棧搶 CPU、緩沖區(qū)大小和應(yīng)用層處理速度不匹配、Flash 寫(xiě)入干擾連接事件這些問(wèn)題本質(zhì)上都是耦合問(wèn)題。只要你能把應(yīng)用層寫(xiě)得足夠輕、足夠事件化單核芯片在數(shù)據(jù)流場(chǎng)景下是完全能打的。第二RAM 大小比主頻更能決定數(shù)據(jù)流體驗(yàn)。WB05 和 WB09 的差別里我最在意的其實(shí)是 RAM。RAM 寬裕了環(huán)形隊(duì)列可以開(kāi)大一點(diǎn)協(xié)議棧也可以更激進(jìn)地配置緩沖區(qū)系統(tǒng)整體容錯(cuò)能力強(qiáng)很多。如果你在 WB05 上遇到“參數(shù)全對(duì)但吞吐不穩(wěn)”的情況多半就是緩沖區(qū)撞到了天花板。第三我自己現(xiàn)在做無(wú)線項(xiàng)目基本會(huì)先畫(huà)一張“CPU 時(shí)間預(yù)算表”把 BLE 協(xié)議棧預(yù)估占用、應(yīng)用處理、數(shù)據(jù)搬運(yùn)、flash 操作各分配多少 CPU 時(shí)間先寫(xiě)出來(lái)再?zèng)Q定用單核、雙核還是更高端的方案。這個(gè)習(xí)慣幫我避免了很多后期返工。最后就是實(shí)際測(cè)試時(shí)別只看最大吞吐量要看連續(xù)穩(wěn)定跑 10 分鐘甚至更長(zhǎng)時(shí)間的丟包率。BLE 數(shù)據(jù)流在連接剛開(kāi)始時(shí)往往表現(xiàn)很好真正的問(wèn)題是長(zhǎng)時(shí)間運(yùn)行后緩沖、調(diào)度、功耗管理相互作用產(chǎn)生的波動(dòng)。把長(zhǎng)效穩(wěn)定性測(cè)透了選型才有說(shuō)服力。