如何实现BLE 最大数据吞吐率并满足设计功耗要求?
文章目錄
- 一、如何提高BLE 數(shù)據(jù)傳輸速率?
- 1.1 Nordic BLE 最大數(shù)據(jù)吞吐率是多少?
- 1.2 如何獲知BLE 當(dāng)前數(shù)據(jù)吞吐率?
- 1.3 如何提高BLE 數(shù)據(jù)傳輸速率?
- 1.3.1 LE 1M PHY 最大數(shù)據(jù)吞吐率
- 1.3.2 LE 2M PHY 最大數(shù)據(jù)吞吐率
- 1.4 如何同步數(shù)據(jù)的生產(chǎn)與發(fā)送?
- 二、如何設(shè)置廣播連接參數(shù)以滿足低功耗需求?
- 更多文章:
我們開發(fā)的BLE 設(shè)備多數(shù)都有兩點(diǎn)要求:一是低功耗,電池供電需要持續(xù)工作數(shù)周甚至數(shù)個(gè)月;二是將BLE peripheral產(chǎn)生的數(shù)據(jù)快速傳送給central,傳輸數(shù)據(jù)功耗較高,提高傳輸速率縮短傳輸時(shí)間也有利于降低平均功耗。我們?cè)撊绾卧O(shè)置廣播參數(shù)與連接參數(shù)以達(dá)到我們要求的功耗呢?該如何設(shè)置連接參數(shù)與報(bào)文長(zhǎng)度(PDU / MTU)以盡可能達(dá)到最大傳輸速率呢?
一、如何提高BLE 數(shù)據(jù)傳輸速率?
BLE 數(shù)據(jù)傳輸相關(guān)的服務(wù)中有一個(gè)比較基礎(chǔ)的串口透?jìng)鞣?wù),本文以nRF5_SDK_17.0.2 中的ble_app_uart 工程為例,展示如何提高BLE 的數(shù)據(jù)傳輸速率。
在嘗試提高BLE 數(shù)據(jù)傳輸速率前,需要先獲得兩個(gè)信息:
1.1 Nordic BLE 最大數(shù)據(jù)吞吐率是多少?
對(duì)于第一個(gè)問題,我們可以從Nordic 協(xié)議棧規(guī)格說明書中獲知,比如使用s132 softdevice 可以參考文檔:S132 SoftDevice SoftDevice Specification v7.1,查閱Bluetooth Low Energy data throughput 章節(jié),數(shù)據(jù)傳輸速率使用下面的公式:
#define OPCODE_LENGTH 1 #define HANDLE_LENGTH 2Throughput_bps = num_packets * (ATT_MTU - OPCODE_LENGTH - HANDLE_LENGTH) * 8 / seconds這里統(tǒng)計(jì)的傳輸數(shù)據(jù)指的是應(yīng)用數(shù)據(jù),ATT_MTU 減去Attribute protocol PDU Opcode 和Attribute Handle 字段長(zhǎng)度,剩下的就是Attribute Value 字段(也即有效的應(yīng)用數(shù)據(jù))。每個(gè)字節(jié)占8 比特,下表中的傳輸速率單位是kbps(如果要換算成 KB/s 需要除以8),下表Connection interval 與Connection Event Length 相等:
從上表可知,跟傳輸速率相關(guān)的因素主要有ATT MTU size、Connection interval、Connection Event Length、Communication Mode、LE PHY speed 等。比如ATT MTU size 為23、Connection interval 與Connection Event Length 取7.5 ms、Communication Mode 為Send Notification、LE 1M PHY 的最大速率為24 KB/s;ATT MTU size 為247、Connection interval 與Connection Event Length 取50 ms、Communication Mode 為Send Notification、LE 2M PHY 的最大速率為165.94 KB/s。
1.2 如何獲知BLE 當(dāng)前數(shù)據(jù)吞吐率?
一般BLE peripheral 作為GATT Server 向BLE central 也即GATT Client 傳輸數(shù)據(jù),想獲得BLE 當(dāng)前的數(shù)據(jù)吞吐率,一般有三種方式:
Nordic 手機(jī)端的應(yīng)用并沒有提供顯示當(dāng)前數(shù)據(jù)吞吐率的功能,我們開發(fā)GATT Server 應(yīng)用再去修改BLE central 代碼比較麻煩。BLE sniffer 抓包分析倒是比較方便,wireshark + nRF sniffer 抓包方案容易丟包也沒有直接統(tǒng)計(jì)數(shù)據(jù)吞吐率指標(biāo),專業(yè)的藍(lán)牙分析儀Ellisys Bluetooth Explorer 倒是可以直接統(tǒng)計(jì)數(shù)據(jù)吞吐率,藍(lán)牙分析儀成本太高。因此,本文選擇第一種方案,在BLE peripheral 代碼中添加統(tǒng)計(jì)數(shù)據(jù)發(fā)送量的功能,并通過RTT Log 打印出來。
我們?cè)?\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart 示例工程的基礎(chǔ)上添加統(tǒng)計(jì)單位時(shí)間內(nèi)數(shù)據(jù)發(fā)送量的代碼,Nordic UART Service 我們?cè)诓┪?#xff1a;如何實(shí)現(xiàn)掃碼連接BLE 設(shè)備的功能? 中已經(jīng)介紹過了,二者主要的代碼邏輯差不多,主要有兩點(diǎn)不同:
- ble_app_uart 工程在GAP 階段作為Advertiser,在函數(shù)advertising_init 中初始化廣播包內(nèi)容、廣播間隔、廣播超時(shí)時(shí)間等,然后在函數(shù)advertising_start 中開始廣播;ble_app_uart_c 工程在GAP 階段作為Scanner 和Initiator,在函數(shù)scan_init 中設(shè)置掃描過濾條件、注冊(cè)scan_evt_handler 等,然后在函數(shù)scan_start 中開始掃描周圍的廣播設(shè)備;
- ble_app_uart 工程在GATT 階段作為GATT Server,在函數(shù)services_init --> ble_nus_init 中添加NUS service(包括RX Characteristic、TX Characteristic)、注冊(cè)nus_data_handler 等,其中NUS 為Primary Service 對(duì)外提供串口透?jìng)鞣?wù);ble_app_uart_c 工程在GATT 階段作為GATT Client,在函數(shù)db_discovery_init 和nus_c_init 中發(fā)現(xiàn)對(duì)端設(shè)備提供了哪些services(特別是NUS 服務(wù))、注冊(cè)db_disc_handler 和ble_nus_c_evt_handler 等,發(fā)現(xiàn)NUS 服務(wù)后就可以訪問該服務(wù)了。
本文就不展開介紹ble_app_uart 工程代碼邏輯了,我們重點(diǎn)關(guān)心的是GATT Server 如何使用NUS 服務(wù)向GATT Client 發(fā)送數(shù)據(jù)。從函數(shù)uart_event_handle 代碼可以看出,使用函數(shù)ble_nus_data_send 可以通過NUS 服務(wù)向?qū)Χ嗽O(shè)備發(fā)送數(shù)據(jù),該函數(shù)的聲明如下:
// .\nRF5_SDK_17.0.2_d674dde\components\ble\ble_services\ble_nus\ble_nus.h/**@brief Function for sending a data to the peer.** @details This function sends the input string as an RX characteristic notification to the* peer.** @param[in] p_nus Pointer to the Nordic UART Service structure.* @param[in] p_data String to be sent.* @param[in,out] p_length Pointer Length of the string. Amount of sent bytes.* @param[in] conn_handle Connection Handle of the destination client.** @retval NRF_SUCCESS If the string was sent successfully. Otherwise, an error code is returned.*/ uint32_t ble_nus_data_send(ble_nus_t * p_nus,uint8_t * p_data,uint16_t * p_length,uint16_t conn_handle);既然是統(tǒng)計(jì)單位時(shí)間內(nèi)GATT Server 發(fā)送出去的數(shù)據(jù)量,自然需要一個(gè)定時(shí)器資源,我們選用低功耗的app_timer。為了提高數(shù)據(jù)發(fā)送速率,我們選擇Send Notification 模式。為了少做無用功,我們?cè)贜US Notification enable 的情況下再開始發(fā)送數(shù)據(jù),當(dāng)連接斷開后便停止發(fā)送數(shù)據(jù)。新增用于統(tǒng)計(jì)BLE data throughput 的代碼如下:
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c/**@brief Resources related to throughput testing.*/ #define DATA_THROUGHPUT_INTERVAL APP_TIMER_TICKS(5) /**< data throughput interval (ticks). */ APP_TIMER_DEF(m_timer_throughput_id);uint32_t m_data_sent_length = 0; uint8_t m_data_array[BLE_NUS_MAX_DATA_LEN] = {0};/**@brief Data generation timer timeout handler function.*/ static void data_throughput_timeout_handler(void * p_context) {UNUSED_PARAMETER(p_context);static uint32_t timeout_count = 0;ret_code_t err_code;timeout_count++;do{uint16_t length = BLE_NUS_MAX_DATA_LEN;err_code = ble_nus_data_send(&m_nus, m_data_array, &length, m_conn_handle);if ((err_code != NRF_ERROR_INVALID_STATE) &&(err_code != NRF_ERROR_RESOURCES) &&(err_code != NRF_ERROR_NOT_FOUND)){APP_ERROR_CHECK(err_code);}if(err_code == NRF_SUCCESS){m_data_sent_length += length;m_data_array[0]++;m_data_array[length-1]++;}} while (err_code == NRF_SUCCESS);// Timer interval 5 ms, when the timer reaches 1 second if(timeout_count == 200){// Send m_data_sent_length bytes of data within 1 second, which is equal to m_data_sent_length * 8 / 1024 kilobits of dataNRF_LOG_INFO("****** BLE data throughput: %d kbps ******", m_data_sent_length >> 7);m_data_sent_length = 0;timeout_count = 0;} }/**@brief Function for initializing the timer module.*/ static void timers_init(void) {......// Create a data generation timer for testing throughput.err_code = app_timer_create(&m_timer_throughput_id, APP_TIMER_MODE_REPEATED, data_throughput_timeout_handler);APP_ERROR_CHECK(err_code); }/**@brief Function for handling the data from the Nordic UART Service.*/ static void nus_data_handler(ble_nus_evt_t * p_evt) {if (p_evt->type == BLE_NUS_EVT_RX_DATA) {......} else if(p_evt->type == BLE_NUS_EVT_COMM_STARTED) {// Start data throughput timers.ret_code_t err_code;err_code = app_timer_start(m_timer_throughput_id, DATA_THROUGHPUT_INTERVAL,NULL);APP_ERROR_CHECK(err_code);} }/**@brief Function for handling BLE events.*/ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) {uint32_t err_code;switch (p_ble_evt->header.evt_id){case BLE_GAP_EVT_CONNECTED:......case BLE_GAP_EVT_DISCONNECTED:......// Stop data throughput timers.err_code = app_timer_stop(m_timer_throughput_id);APP_ERROR_CHECK(err_code);break;case BLE_GAP_EVT_PHY_UPDATE_REQUEST:......} }上述代碼主要包含兩部分:
值得一提的是,每個(gè)定時(shí)周期可以發(fā)送不止一個(gè)數(shù)據(jù)包,博文鏈路層空口報(bào)文設(shè)計(jì) 中提到LE 1M PHY 發(fā)送最大PDU 約需2.3 ms,上面的代碼設(shè)置的定時(shí)周期為5 ms,因此每個(gè)定時(shí)周期可以發(fā)送多個(gè)數(shù)據(jù)包,我們將ble_nus_data_send 放到循環(huán)體內(nèi),當(dāng)返回值為NRF_SUCCESS 時(shí)繼續(xù)循環(huán)發(fā)送下一個(gè)數(shù)據(jù)包。
通過RTT Log 打印當(dāng)前BLE 數(shù)據(jù)吞吐量的代碼已經(jīng)實(shí)現(xiàn)了,編譯工程 --> 將代碼燒錄到nRF52 DK 內(nèi),PC 端打開J-Link RTT Viewer,手機(jī)端打開nRF Connect for mobile。點(diǎn)擊Enable CCCDs 或者Tx Characteristic 右邊的圖標(biāo)使能NUS Notification,nRF52 DK 開始通過BLE 向手機(jī)端發(fā)送數(shù)據(jù),nRF Connect --> Show log 可以查看接收到的數(shù)據(jù),J-Link RTT Viewer 開始打印當(dāng)前的BLE 數(shù)據(jù)吞吐率:
1.3 如何提高BLE 數(shù)據(jù)傳輸速率?
上圖展示的BLE 數(shù)據(jù)吞吐率只有41 kbps,遠(yuǎn)低于nordic softdevice 支持的最大數(shù)據(jù)吞吐率,這是怎么回事呢?
BLE 數(shù)據(jù)吞吐率的計(jì)算公式:
Throughput_kbps = num_packets * (ATT_MTU - 3) * 8 / 1000 // num_packets 為單位時(shí)間也即 1 秒內(nèi)發(fā)送的數(shù)據(jù)包個(gè)數(shù)= (num_packets_interval / CONN_INTERVAL) * (ATT_MTU - 3) * 8 / 1000 // num_packets_interval 為單個(gè)連接間隔內(nèi)發(fā)送的數(shù)據(jù)包個(gè)數(shù),CONN_INTERVAL 為連接間隔,單位是秒1.3.1 LE 1M PHY 最大數(shù)據(jù)吞吐率
上述工程默認(rèn)的ATT_MTU 值為247,CONN_INTERVAL 為20 ~ 75 ms,由Throughput_kbps 等于41 kbps 可反推出num_packets_interval 等于1(CONN_INTERVAL 取中間值47.5 ms)。一個(gè)連接間隔只發(fā)送出去一個(gè)數(shù)據(jù)包,這大概是BLE 數(shù)據(jù)吞吐率這么低的主要原因吧,該如何提高BLE Throughput_kbps 呢?
前面也談到,影響B(tài)LE Throughput_kbps 的因素主要有ATT MTU size、Connection interval、Connection Event Length、Communication Mode、LE PHY speed 等,ATT MTU size 已經(jīng)設(shè)置為BLE 支持的最大值247,Connection Event 值為7.5 ms,也即一個(gè)連接周期最多只有Connection Event 時(shí)間傳輸數(shù)據(jù),這個(gè)值遠(yuǎn)小于Connection interval,我們需要讓Connection Event 占滿Connection interval。為便于跟nordic softdevice 規(guī)格說明書中的值對(duì)比,這里設(shè)置連接參數(shù)如下:
編譯工程 --> 燒錄到nRF52 DK,J-Link RTT Viewer 打印RTT Log 信息如下:
在執(zhí)行函數(shù)sd_ble_enable 時(shí)返回NRF_ERROR_NO_MEM,也即分配給softdevice 的RAM 空間不足,需要為softdevice 預(yù)留更多的空間(也即縮減application 可用RAM 空間)。我們按照RTT Log 調(diào)整RAM_Start 和RAM_Size 如下:
重新編譯工程并燒錄代碼,nRF Connect for mobile 使能notification 或CCCDs,J-Link RTT Viewer 打印的BLE 數(shù)據(jù)吞吐率如下:
BLE 最大數(shù)據(jù)吞吐率已經(jīng)達(dá)到697 kbps了,很接近nordic softdevice 規(guī)格說明書中的702.8 kbps(也即87.85 KB/s),多打印會(huì)兒是可以看到BLE data throughput 達(dá)到七百以上的,BLE 數(shù)據(jù)傳輸速率達(dá)到了softdevice 支持的最大值。
如果已知Throughput_kbps 值為702.8 kbps,ATT_MTU 值為247,CONN_INTERVAL 值為50 ms,可以通過公式反求出單個(gè)連接間隔內(nèi)發(fā)送出去的數(shù)據(jù)包個(gè)數(shù)為18,也即每個(gè)數(shù)據(jù)包以send notification 模式發(fā)送出去所需的平均時(shí)間為2.78 ms(包括radio 啟動(dòng)和切換時(shí)間、協(xié)議棧調(diào)度時(shí)間等)。
1.3.2 LE 2M PHY 最大數(shù)據(jù)吞吐率
從nordic softdevice 規(guī)格說明書可知,還可以使用BLE 5.0 新增的LE 2M PHY 特性進(jìn)一步提高數(shù)據(jù)吞吐率。鏈路層使用LE 2M PHY,可以在更短的時(shí)間發(fā)送完等長(zhǎng)度的數(shù)據(jù)包,也即在一個(gè)連接間隔可以發(fā)送更多的數(shù)據(jù)包,實(shí)現(xiàn)更大的傳輸速率。
當(dāng)Connection interval 與Connection event 均為50 ms,ATT_MTU size 為247,采用Send Notification 通信模式和LE 2M PHY 物理鏈路,可以達(dá)到的Throughput_kbps 值為1327.5 kbps,通過公式可反求出單個(gè)連接間隔內(nèi)發(fā)送出去的數(shù)據(jù)包個(gè)數(shù)為34,也即每個(gè)數(shù)據(jù)包以send notification 模式發(fā)送出去所需的平均時(shí)間為1.47 ms(由于radio 啟動(dòng)與切換時(shí)間、協(xié)議棧調(diào)度時(shí)間基本固定,因此發(fā)送單個(gè)數(shù)據(jù)包使用LE 2M PHY 比LE 1M PHY 所需時(shí)間的一半略多)。如何啟用LE 2M PHY 呢?
前面通過修改宏變量值就可以更新Data Length 和Connection Parameters,這些更新過程在鏈路層有相應(yīng)的控制報(bào)文交互(參閱博文:Link Layer Control Protocol),對(duì)于PHY Update 也有對(duì)應(yīng)的鏈路層控制報(bào)文交互。
上述工程ble_app_uart 代碼中跟PHY Update 相關(guān)的主要代碼如下:
上面的代碼是處理BLE_GAP_EVT_PHY_UPDATE_REQUEST 事件的,從BLE_GAP_PHYS_SUPPORTED 可以看出nRF52 DK 是支持BLE_GAP_PHY_2MBPS 的,變量phys 的值如果設(shè)置為BLE_GAP_PHY_1MBPS 或BLE_GAP_PHY_2MBPS 則強(qiáng)制選擇相應(yīng)的PHY,上述代碼phys 設(shè)置為BLE_GAP_PHY_AUTO 則會(huì)自動(dòng)選擇當(dāng)前最合適的PHY。如果在BLE central 端請(qǐng)求使用LE 2M PHY,BLE peripheral 端也會(huì)更新到LE 2M PHY(前提是BLE central 端與peripheral 端均支持LE 2M PHY,且peripheral 端未強(qiáng)制指定PHY)。
手機(jī)端是否支持LE 2M PHY,可以從nRF connect for mobile --> Device information 界面查看“High speed(PHY 2M) supported” 項(xiàng)為“YES” 表示支持LE 2M PHY。nRF connect for mobile 連接到nRF52 DK 廣播名NORDIC_UART 后,點(diǎn)擊“Enable CCCDs”使能NUS Notification,點(diǎn)擊"Set preferred PHY" Tx/Rx PHY 均選擇“LE 2M(Double speed)”。PC 端J-Link RTT Viewer 打印的BLE 數(shù)據(jù)吞吐率如下:
我們看到了很奇怪的現(xiàn)象,理論上切換到LE 2M PHY,BLE 數(shù)據(jù)吞吐率應(yīng)該提高近一倍的,實(shí)際情況卻是大幅下降,這是怎么回事呢?
我們也是在每個(gè)定時(shí)周期循環(huán)發(fā)送數(shù)據(jù)包,直到函數(shù)ble_nus_data_send 的返回值不為NRF_SUCCESS 或者函數(shù)data_throughput_timeout_handler 被更高優(yōu)先級(jí)的中斷搶占(協(xié)議棧softdevice 事件的優(yōu)先級(jí)高于application 中斷的優(yōu)先級(jí))。同樣的代碼LE 1M PHY 可以接近最大數(shù)據(jù)吞吐率,切換到LE 2M PHY 數(shù)據(jù)吞吐率反而下降了,我們可以合理猜測(cè)循環(huán)發(fā)送數(shù)據(jù)包的過程出問題了,也即函數(shù)ble_nus_data_send 的返回值不是NRF_SUCCESS 而過早的退出了循環(huán)。該如何驗(yàn)證并解決該問題呢?
這里選用的Send Notification 通信模式,server 可以連續(xù)向Client 發(fā)送多個(gè)數(shù)據(jù)包而不需要等待response 或Confirmation 報(bào)文(Client 可能來不及處理數(shù)據(jù)包而直接丟棄),因此可以達(dá)到較高的數(shù)據(jù)吞吐率:
調(diào)用函數(shù)ble_nus_data_send,實(shí)際上是將應(yīng)用層待發(fā)送數(shù)據(jù)指針傳給softdevice 協(xié)議棧,放入到radio FIFO 中,當(dāng)radio 將數(shù)據(jù)包成功發(fā)送出去后,softdevice 協(xié)議棧會(huì)返回BLE_GATTS_EVT_HVN_TX_COMPLETE 事件通知應(yīng)用層數(shù)據(jù)包已成功發(fā)送出去。NUS 服務(wù)則會(huì)返回BLE_NUS_EVT_TX_RDY 事件通知應(yīng)用層數(shù)據(jù)包已通過NUS 服務(wù)成功發(fā)送出去。
前面的問題既然猜測(cè)是由函數(shù)ble_nus_data_send 返回值非NRF_SUCCESS 而過早退出循環(huán)導(dǎo)致每個(gè)定時(shí)周期發(fā)送的數(shù)據(jù)包太少引起的,我們可以在每次觸發(fā)BLE_NUS_EVT_TX_RDY 事件時(shí)再次循環(huán)調(diào)用函數(shù)ble_nus_data_send 發(fā)送下一個(gè)數(shù)據(jù)包。每成功發(fā)送一個(gè)數(shù)據(jù)包觸發(fā)一次BLE_NUS_EVT_TX_RDY 事件,調(diào)用一次函數(shù)ble_nus_data_send,理論上應(yīng)該能解決上述問題,我們添加如下代碼:
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c/**@brief Function for handling the data from the Nordic UART Service.*/ static void nus_data_handler(ble_nus_evt_t * p_evt) {if (p_evt->type == BLE_NUS_EVT_RX_DATA) {......} else if(p_evt->type == BLE_NUS_EVT_COMM_STARTED) {// Start data throughput timers.......} else if (p_evt->type == BLE_NUS_EVT_TX_RDY) {ret_code_t err_code;do {uint16_t length = BLE_NUS_MAX_DATA_LEN;err_code = ble_nus_data_send(&m_nus, m_data_array, &length, m_conn_handle);if ((err_code != NRF_ERROR_INVALID_STATE) &&(err_code != NRF_ERROR_RESOURCES) &&(err_code != NRF_ERROR_NOT_FOUND)){APP_ERROR_CHECK(err_code);}if(err_code == NRF_SUCCESS){m_data_sent_length += length;m_data_array[0]++;m_data_array[length-1]++;}} while (err_code == NRF_SUCCESS);} }重新編譯工程并燒錄代碼,nRF Connect for mobile 點(diǎn)擊“Enable CCCDs”使能NUS Notification,點(diǎn)擊"Set preferred PHY" Tx/Rx PHY 均選擇“LE 2M(Double speed)”,J-Link RTT Viewer 打印的BLE 數(shù)據(jù)吞吐率如下:
切換到LE 2M PHY 后,BLE 數(shù)據(jù)吞吐率果然大幅提升,上圖顯示吞吐率可以達(dá)到1220 kbps (也即152.5 KB/s),已經(jīng)比較接近nordic softdevice 支持的最大吞吐率1327.5 kbps 了,多打印會(huì)兒是可以看到BLE data throughput 達(dá)到一千三以上的,BLE 數(shù)據(jù)傳輸速率達(dá)到了softdevice 支持的最大值。
1.4 如何同步數(shù)據(jù)的生產(chǎn)與發(fā)送?
前面的代碼直接對(duì)數(shù)組首尾字節(jié)自增后發(fā)送,實(shí)際應(yīng)用場(chǎng)景中都是將斷開連接期間暫時(shí)保存在本設(shè)備的數(shù)據(jù)或者sensor 實(shí)時(shí)產(chǎn)生的數(shù)據(jù),在BLE 建立連接后分包發(fā)送給BLE Central 設(shè)備。如何保證數(shù)據(jù)包的有序發(fā)送呢?
我們很容易想到,可以借助FIFO 緩沖隊(duì)列實(shí)現(xiàn)數(shù)據(jù)包的有序發(fā)送,這里使用nordic 提供的queue 庫(kù),生產(chǎn)出來的待發(fā)送數(shù)據(jù)有序入隊(duì),要發(fā)送的數(shù)據(jù)從隊(duì)列中取用即可。
我們將上述持續(xù)發(fā)送BLE 數(shù)據(jù)包的代碼修改為使用queue 的形式,首先需要將目錄 .\nRF5_SDK_17.0.2_d674dde\components\libraries\queue 下的源文件和頭文件路徑添加進(jìn)工程中,再在main.c 文件中包含"nrf_queue.h" 頭文件,在main.c 中添加或修改如下代碼:
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c ...... #include "nrf_queue.h" ...... #define QUEUE_ELEMENT_NUMBERS 32 #define PKGS_PER_TIMER_PERIOD 8uint8_t m_data_array[QUEUE_ELEMENT_NUMBERS][BLE_NUS_MAX_DATA_LEN] = {0}; typedef struct {uint8_t * p_data;uint16_t length; } m_element_t;NRF_QUEUE_DEF(m_element_t, m_buf_queue, QUEUE_ELEMENT_NUMBERS, NRF_QUEUE_MODE_NO_OVERFLOW); ...... /**@brief Use queue to send ble data.*/ void ble_data_send_with_queue(void) {ret_code_t err_code;m_element_t data_item;uint16_t length = BLE_NUS_MAX_DATA_LEN;while(!nrf_queue_is_empty(&m_buf_queue)){err_code = nrf_queue_pop(&m_buf_queue, &data_item);APP_ERROR_CHECK(err_code);length = MIN(length, data_item.length);err_code = ble_nus_data_send(&m_nus, data_item.p_data, &length, m_conn_handle);if ((err_code != NRF_ERROR_INVALID_STATE) &&(err_code != NRF_ERROR_RESOURCES) &&(err_code != NRF_ERROR_NOT_FOUND)){APP_ERROR_CHECK(err_code);}if(err_code == NRF_SUCCESS)m_data_sent_length += length;elsebreak;} }/**@brief Data generation timer timeout handler function.*/ static void data_throughput_timeout_handler(void * p_context) {UNUSED_PARAMETER(p_context);static uint32_t timeout_count = 0;ret_code_t err_code;static uint8_t value = 0;m_element_t data_item;uint16_t length = BLE_NUS_MAX_DATA_LEN;uint8_t pkgs = PKGS_PER_TIMER_PERIOD;timeout_count++;while (!nrf_queue_is_full(&m_buf_queue) && pkgs--){m_data_array[value % QUEUE_ELEMENT_NUMBERS][0] = value;m_data_array[value % QUEUE_ELEMENT_NUMBERS][length-1] = value;data_item.p_data = &m_data_array[value % QUEUE_ELEMENT_NUMBERS][0];data_item.length = length;err_code = nrf_queue_push(&m_buf_queue, &data_item);APP_ERROR_CHECK(err_code);value++;}ble_data_send_with_queue();// Timer interval 5 ms, when the timer reaches 1 second if(timeout_count == 200){// Send m_data_sent_length bytes of data within 1 second, which is equal to m_data_sent_length * 8 / 1024 kilobits of dataNRF_LOG_INFO("****** BLE data throughput: %d kbps ******", m_data_sent_length >> 7);m_data_sent_length = 0;timeout_count = 0;value = 0;} } ...... /**@brief Function for handling the data from the Nordic UART Service.*/ static void nus_data_handler(ble_nus_evt_t * p_evt) {if (p_evt->type == BLE_NUS_EVT_RX_DATA) {......} else if(p_evt->type == BLE_NUS_EVT_COMM_STARTED) {......} else if(p_evt->type == BLE_NUS_EVT_TX_RDY) {ble_data_send_with_queue();} }使用queue 同步數(shù)據(jù)的產(chǎn)生與發(fā)送,隊(duì)列未滿時(shí)將生產(chǎn)的數(shù)據(jù)入隊(duì),隊(duì)列非空時(shí)從隊(duì)列中取出下一個(gè)元素通過調(diào)用函數(shù)ble_nus_data_send 將其發(fā)送出去。
編譯工程報(bào)錯(cuò),提示nrf_queue 函數(shù)未定義,我們需要在sdk_config.h 文件中啟用NRF_QUEUE 模塊相關(guān)的宏變量如下:
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\pca10040\s132\config\sdk_config.h#define NRF_QUEUE_ENABLED 1重新編譯工程并燒錄代碼,nRF Connect for mobile 點(diǎn)擊“Enable CCCDs”使能NUS Notification,點(diǎn)擊"Set preferred PHY" Tx/Rx PHY 均選擇“LE 2M(Double speed)”,J-Link RTT Viewer 打印的BLE 數(shù)據(jù)吞吐率如下:
使用queue 同步數(shù)據(jù)產(chǎn)生與發(fā)送,PHY 使用LE 1M 時(shí)最大數(shù)據(jù)吞吐率為726 kbps,PHY 切換到LE 2M 時(shí)最大數(shù)據(jù)吞吐率為1334 kbps,均略高于nordic softdevice 支持的最大值,達(dá)到了我們預(yù)期的效果。
二、如何設(shè)置廣播連接參數(shù)以滿足低功耗需求?
我們開發(fā)的BLE peripheral 多數(shù)都有低功耗要求,由電池供電,如何滿足電池續(xù)航需求呢?
Nordic 提供了nRF 芯片理論功耗計(jì)算網(wǎng)頁(yè)Online Power Profiler for BLE,我們可以在該頁(yè)面修改參數(shù),看理論功耗是否滿足我們的設(shè)計(jì)要求。如果已經(jīng)試產(chǎn)出產(chǎn)品了,也可以借助Power Profiler Kit 或Power Profiler Kit II 測(cè)量產(chǎn)品的真實(shí)功耗。
假設(shè)我們使用CR2032 紐扣電池(額定容量為220 mAh,額定電壓3.0 V)供電,使用壽命一年,每天平均連接兩個(gè)小時(shí),其余時(shí)間處于idle 空閑狀態(tài),我們?cè)撊绾卧O(shè)置廣播參數(shù)與連接參數(shù),以滿足我們的設(shè)計(jì)續(xù)航要求呢?
假設(shè)我們選用nRF52832 芯片,Idle current 為2 uA,全年待機(jī)共消耗電量 = 365 * 24 * 2 uAh = 17.52 mAh。在產(chǎn)品壽命期間,BLE 連接通信時(shí)間約730 小時(shí),可供BLE 連接消耗的電量約200 mAh,BLE 連接的平均功耗為274 uA。電池并不僅僅為BLE 通信供電,還為必要的傳感器與外設(shè)工作供電,考慮到傳感器與外設(shè)工作的時(shí)間比BLE 連接通信的時(shí)間更長(zhǎng),我們假設(shè)僅電池電量的1/3 供BLE 廣播連接通信使用,其余2/3 為傳感器和芯片外設(shè)工作供電,因此BLE 廣播連接通信的平均功耗應(yīng)控制在90 uA 左右。我們?cè)贠nline Power Profiler for BLE 頁(yè)面配置如下參數(shù):
芯片選擇nRF52832、CR2032 的額定電壓為3.0 V、穿戴設(shè)備Radio Tx Power 選擇 0 dBm 可以滿足傳輸距離需求(可根據(jù)BLE 在空氣中的路徑損耗公式,結(jié)合通訊距離要求選擇合適的Tx Power)。
DC/DC regulator 是一種效率很高的穩(wěn)壓器,原理是DC->AC->DC,既可以升壓也可以降壓。與之相比,還有一種低成本的LDO (Low Dropout regulator) 穩(wěn)壓器,效率比DC/DC 低些,只能降壓使用且對(duì)輸入輸出電壓差有限制。如果想達(dá)到更低的功耗,可以選擇DC/DC,如果想進(jìn)一步降低成本,可以選擇LDO。這里我們選擇更高效率的DC/DC regulator。
BLE 芯片通常需要兩個(gè)時(shí)鐘信號(hào),比如nRF52 DK 上高頻晶振頻率為32 MHz、低頻晶振頻率為32.768 KHz,高頻晶振驅(qū)動(dòng)MCU 和高速外設(shè)工作,低頻晶振可以大幅降低芯片的待機(jī)功耗(idle 或sleep 狀態(tài)耗電的高頻時(shí)鐘關(guān)閉,僅保留低頻時(shí)鐘方便計(jì)時(shí)和喚醒)。高頻時(shí)鐘信號(hào)都需要外部晶振提供,低頻時(shí)鐘信號(hào)既可以外部晶振提供也可以使用MCU 內(nèi)部的RC 振蕩器獲得。如果使用MCU 內(nèi)部的RC 振蕩器作為低頻時(shí)鐘則需要定期對(duì)其進(jìn)行校準(zhǔn),需要大概 1.0 uA 的校準(zhǔn)電流,且時(shí)鐘精度略低些(較低的時(shí)鐘精度也會(huì)增加BLE 通訊功耗)。
配置低頻時(shí)鐘信號(hào)的代碼如下(工程ble_app_uart 默認(rèn)選擇的外部晶振作為低頻時(shí)鐘信號(hào),若想選擇MCU 內(nèi)部振蕩器作為低頻時(shí)鐘,修改sdk_config.h 中如下的四個(gè)宏變量值即可,本文選擇默認(rèn)的外部晶振):
// .\nRF5_SDK_17.0.2_d674dde\components\softdevice\common\nrf_sdh.c/**@brief Function for requesting to enable the SoftDevice, is called in the function ble_stack_init.*/ ret_code_t nrf_sdh_enable_request(void) {......nrf_clock_lf_cfg_t const clock_lf_cfg ={.source = NRF_SDH_CLOCK_LF_SRC,.rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV,.rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,.accuracy = NRF_SDH_CLOCK_LF_ACCURACY};...... }// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\pca10040\s132\config\sdk_config.h ...... // <h> Clock - SoftDevice clock configuration //========================================================== // <o> NRF_SDH_CLOCK_LF_SRC - SoftDevice clock source.// <0=> NRF_CLOCK_LF_SRC_RC // <1=> NRF_CLOCK_LF_SRC_XTAL // <2=> NRF_CLOCK_LF_SRC_SYNTH #ifndef NRF_SDH_CLOCK_LF_SRC #define NRF_SDH_CLOCK_LF_SRC 1 #endif// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval. #ifndef NRF_SDH_CLOCK_LF_RC_CTIV #define NRF_SDH_CLOCK_LF_RC_CTIV 0 #endif// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature. // <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated // <i> if the temperature has not changed.#ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV #define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0 #endif// <o> NRF_SDH_CLOCK_LF_ACCURACY - External clock accuracy used in the LL to compute timing.// <0=> NRF_CLOCK_LF_ACCURACY_250_PPM // <1=> NRF_CLOCK_LF_ACCURACY_500_PPM // <2=> NRF_CLOCK_LF_ACCURACY_150_PPM // <3=> NRF_CLOCK_LF_ACCURACY_100_PPM // <4=> NRF_CLOCK_LF_ACCURACY_75_PPM // <5=> NRF_CLOCK_LF_ACCURACY_50_PPM // <6=> NRF_CLOCK_LF_ACCURACY_30_PPM // <7=> NRF_CLOCK_LF_ACCURACY_20_PPM // <8=> NRF_CLOCK_LF_ACCURACY_10_PPM // <9=> NRF_CLOCK_LF_ACCURACY_5_PPM // <10=> NRF_CLOCK_LF_ACCURACY_2_PPM // <11=> NRF_CLOCK_LF_ACCURACY_1_PPM #ifndef NRF_SDH_CLOCK_LF_ACCURACY #define NRF_SDH_CLOCK_LF_ACCURACY 7 #endifBLE 廣播通信階段作為Advertising(connectable) role,假設(shè)TX payload 為31 bytes,當(dāng)設(shè)置Advertising interval 為160 ms 時(shí),Total average current 為87 uA,可滿足我們的功耗需求,我們可以設(shè)置如下的宏變量(將Advertising interval 設(shè)置為160 ms):
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c ...... #define APP_ADV_INTERVAL 256 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 160 ms). */ #define APP_ADV_DURATION 18000 /**< The advertising duration (180 seconds) in units of 10 milliseconds. */ ......BLE 連接通信階段作為Connection(peripheral) role,啟用Data Packet Length Extension 和Connection Event Length Extension,假設(shè)TX payload per event 和RX payload per event 均為251 bytes,選擇LE 1M PHY,我們?cè)贠nline Power Profiler for BLE 頁(yè)面配置如下參數(shù):
我們配置較小的Connection interval 可以在BLE peripheral 有數(shù)據(jù)傳輸需求時(shí)及時(shí)通知BLE central,配置較大的Slave latency 可以讓BLE peripheral 在沒有數(shù)據(jù)傳輸需求時(shí)跳過一定的連接事件以降低功耗。我們配置Connection interval 為25 ms、Slave latency 為 14 時(shí),Total average current 為89 uA,可滿足我們的功耗需求,我們可以設(shè)置如下的宏變量:
本工程源碼下載地址:https://github.com/StreamAI/Nordic_nRF5_Project/tree/main/examples/ble_peripheral\ble_app_uart。
更多文章:
- 《如何抓包分析BLE 空口報(bào)文(GAP + GATT + LESC procedure)?》
- 《如何實(shí)現(xiàn)掃碼連接BLE 設(shè)備的功能?》
- 《Nordic_nRF5_Project》
- 《Nordic nRF5 SDK documentation》
- 《BLE 技術(shù)(三)— Link Layer Packet format 》
- 《BLE 技術(shù)(四)— Link Layer communication protocol 》
- 《BLE 技術(shù)(五)— Generic Access Profile》
- 《BLE 技術(shù)(六)— GATT Profile + ATT protocol》
- 《Bluetooth Core Specification_v5.2》
總結(jié)
以上是生活随笔為你收集整理的如何实现BLE 最大数据吞吐率并满足设计功耗要求?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑端“一键“获得一个手机端截屏
- 下一篇: 计算机组装功耗,浅谈组装电脑之电脑功耗电