日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

如何实现BLE 最大数据吞吐率并满足设计功耗要求?

發(fā)布時(shí)間:2024/3/24 编程问答 94 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何实现BLE 最大数据吞吐率并满足设计功耗要求? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 一、如何提高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è)信息:

  • 當(dāng)前使用的BLE 協(xié)議棧支持的理論最大數(shù)據(jù)吞吐率是多少?
  • 如何獲知當(dāng)前的BLE 數(shù)據(jù)傳輸速率是多少?
  • 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ù)吞吐率,一般有三種方式:

  • BLE peripheral 端統(tǒng)計(jì)單位時(shí)間內(nèi)發(fā)送出去的數(shù)據(jù)量;
  • BLE central 端統(tǒng)計(jì)單位時(shí)間內(nèi)接收到的數(shù)據(jù)量;
  • BLE sniffer 抓取單位時(shí)間內(nèi)傳輸?shù)膱?bào)文中有效數(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:......} }

    上述代碼主要包含兩部分:

  • app_timer 資源的創(chuàng)建、開始與結(jié)束,包括超時(shí)回調(diào)函數(shù)的注冊(cè)。當(dāng)NUS Notification enable 事件發(fā)生時(shí)開始定時(shí)器,當(dāng)連接斷開時(shí)停止定時(shí)器;
  • 超時(shí)回調(diào)函數(shù)data_throughput_timeout_handler 的實(shí)現(xiàn),主要有兩個(gè)任務(wù):一是調(diào)用函數(shù)ble_nus_data_send 發(fā)送數(shù)據(jù)(參照函數(shù)uart_event_handle 內(nèi)調(diào)用函數(shù)ble_nus_data_send 并檢查返回值的代碼,僅當(dāng)返回NRF_SUCCESS 時(shí)才計(jì)入已發(fā)送數(shù)據(jù));二是通過RTT Log 打印1 秒內(nèi)發(fā)送出去的數(shù)據(jù)量。
  • 值得一提的是,每個(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ù)如下:

    // .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c #define MIN_CONN_INTERVAL MSEC_TO_UNITS(50, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(50, UNIT_1_25_MS) /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\pca10040\s132\config\sdk_config.h #define NRF_SDH_BLE_GAP_EVENT_LENGTH 40 // The time set aside for this connection on every connection interval in 1.25 ms units.#define NRF_SDH_BLE_GAP_DATA_LENGTH 251 #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 247

    編譯工程 --> 燒錄到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)的主要代碼如下:

    // .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c /**@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_PHY_UPDATE_REQUEST:{NRF_LOG_DEBUG("PHY update request.");ble_gap_phys_t const phys ={.rx_phys = BLE_GAP_PHY_AUTO,.tx_phys = BLE_GAP_PHY_AUTO,};err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);APP_ERROR_CHECK(err_code);} break;......} }// .\nRF5_SDK_17.0.2_d674dde\components\softdevice\s132\headers\ble_gap.h /**@defgroup BLE_GAP_PHYS GAP PHYs* @{ */ #define BLE_GAP_PHY_AUTO 0x00 /**< Automatic PHY selection. Refer @ref sd_ble_gap_phy_update for more information.*/ #define BLE_GAP_PHY_1MBPS 0x01 /**< 1 Mbps PHY. */ #define BLE_GAP_PHY_2MBPS 0x02 /**< 2 Mbps PHY. */ #define BLE_GAP_PHY_CODED 0x04 /**< Coded PHY. */ #define BLE_GAP_PHY_NOT_SET 0xFF /**< PHY is not configured. *//**@brief Supported PHYs in connections, for scanning, and for advertising. */ #define BLE_GAP_PHYS_SUPPORTED (BLE_GAP_PHY_1MBPS | BLE_GAP_PHY_2MBPS) /**< All PHYs except @ref BLE_GAP_PHY_CODED are supported. */

    上面的代碼是處理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 #endif

    BLE 廣播通信階段作為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è)置如下的宏變量:

    // .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c ...... #define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /**< Maximum acceptable connection interval (30 ms), Connection interval uses 1.25 ms units. */ #define SLAVE_LATENCY 14 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */ ......

    本工程源碼下載地址: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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。