nRF52832 作為一個低功耗藍牙晶元,其數據發送發送速率一直都偏低(高就不叫低功耗了^_^),作為初學者在網上找了很多資料,終於找到通過修改ATT_MTU來提升發送速率的方法,最快能達到8.2KB/s,現在就分享出來 ...
nRF52832 作為一個低功耗藍牙晶元,其數據發送發送速率一直都偏低(高就不叫低功耗了^_^),作為初學者在網上找了很多資料,終於找到通過修改ATT_MTU來提升發送速率的方法,最快能達到8.2KB/s,現在就分享出來
首先我用的協議棧是 nRF5_SDK_14.2.0 ,將\examples\ble_peripheral中的 ble_app_template 作為模板,以此進行修改
廢話不說,先上代碼,首先是定義
#define TIMER_INTERVAL APP_TIMER_TICKS(29) //定時器時間間隔
1 BLE_NUS_DEF(m_nus); //加入串口服務結構(修改) 2 BLE_CMD_DEF(m_cmd); //加入命令服務結構 3 APP_TIMER_DEF(m_timer1); //定時器1 4 5 uint8_t hr_data[250]; 6 uint8_t cmd_data; //接收的命令 7 bool send_state = false; //發送狀態,預設不發送 8 9 static uint16_t length = 244;
主函數基本沒修改,主要初始化了一組數據用來測試發送,加入了調度器,因為使用的定時器定時進行發送,而藍牙發送不好放在中斷里進行,定時器中斷就做一個接發送函數放入調度器的操作。定義了一個全局數組,用來存放發送的數據。
1 int main(void) 2 { 3 bool erase_bonds; 4 5 // Initialize. 6 log_init(); 7 timers_init(); 8 buttons_leds_init(&erase_bonds); 9 ble_stack_init(); 10 gap_params_init(); 11 gatt_init(); 12 advertising_init(); 13 services_init(); 14 conn_params_init(); 15 peer_manager_init(); 16 17 // Start execution. 18 NRF_LOG_INFO("Template example started."); 19 20 advertising_start(erase_bonds); 21 22 for(uint8_t i=0;i<250;i++) hr_data[i]=i; //初始化數據包 23 SEGGER_RTT_printf(0, "\n");// 此處列印信息 24 25 APP_SCHED_INIT(20, 2); //初始化調度器 26 27 // Enter main loop. 28 for (;;) 29 { 30 app_sched_execute(); //調度 31 32 if (NRF_LOG_PROCESS() == false) 33 { 34 power_manage(); 35 } 36 } 37 }
先看定時器
1 static void timers_init(void) 2 { 3 // Initialize timer module. 4 uint32_t err_code = app_timer_init(); 5 APP_ERROR_CHECK(err_code); 6 7 err_code = app_timer_create(&m_timer1, APP_TIMER_MODE_REPEATED, timer_timeout_handler); 8 APP_ERROR_CHECK(err_code); 9 }
定時器中斷服務函數
1 void timer_timeout_handler(void * p_context) 2 { 3 if ( send_state == true ) 4 { 5 hr_data[0]++; 6 if(hr_data[0]>255) hr_data[0]=0; //改變第一個位元組 7 app_sched_event_put(NULL, 0, ble_nus_send); // 加入調度 8 } 9 }
藍牙發送函數
1 void ble_nus_send(void) 2 { 3 uint32_t err_code; 4 do 5 { 6 err_code = ble_nus_string_send(&m_nus, hr_data, &length); 7 if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_BUSY) ) 8 { 9 APP_ERROR_CHECK(err_code); 10 } 11 } while (err_code == NRF_ERROR_BUSY); 12 }
接下來是藍牙服務,這裡我使用了兩個自定義的服務,因為之前測試發送同一個串口服務進行收發的話,在連續接收數據時候,發送的命令會被堵塞,所以索性改了兩個服務,分別用來收發,不知道大家有碰到過這種情況沒,有的話歡迎一起交流交流。
1 static void services_init(void) 2 { 3 uint32_t err_code; 4 ble_nus_init_t nus_init; 5 ble_cmd_init_t cmd_init; 6 7 /* 初始化串口服務 */ 8 memset(&nus_init, 0, sizeof(nus_init)); 9 err_code = ble_nus_init(&m_nus, &nus_init); 10 APP_ERROR_CHECK(err_code); 11 12 /* 初始化自定義命令服務 */ 13 memset(&cmd_init, 0, sizeof(cmd_init)); 14 cmd_init.data_handler = cmd_data_handler; //命令處理函數 15 err_code = ble_cmd_init(&m_cmd, &cmd_init); 16 APP_ERROR_CHECK(err_code); 17 }
命令數據處理函數,定義了一個全局變數,用來指示發送狀態,根據接收的命令修改狀態,以及開關定時器。
1 static void cmd_data_handler(ble_cmd_evt_t * p_evt) 2 { 3 ret_code_t err_code; 4 SEGGER_RTT_printf(0, "Receive a command\n");// 此處列印信息 5 cmd_data = p_evt->params.rx_data.p_data[0]; //接收1個位元組作為命令 6 7 switch ( cmd_data ) 8 { 9 case BLE_STOP_CMD : 10 send_state = false; //停止發送 11 err_code = app_timer_stop(m_timer1); //停止定時器 12 APP_ERROR_CHECK(err_code); 13 break; 14 case BLE_SEND_CMD : 15 send_state = true; //允許發送 16 err_code = app_timer_start(m_timer1, TIMER_INTERVAL, NULL); //啟動定時器 17 APP_ERROR_CHECK(err_code); 18 break; 19 default: 20 SEGGER_RTT_printf(0, "Invalid command\n");// 此處列印信息 21 break; 22 } 23 }
整個程式的框架基本上就是這樣,通過藍牙接收的命令打開定時器,定時器中斷將發送函數加入調度,主迴圈輪轉到發送時進行數據發送
通過修改 length 改變發送包的大小
通過修改TIMER_INTERVAL 修改定時器時間
兩者配合改變數據發送速率。
到了這裡,就得修改ATT_MTU了,不然244個位元組根本發不了,打開sdk_config.h
1 // <h> nRF_SoftDevice 2 3 //========================================================== 4 // <e> NRF_SDH_BLE_ENABLED - nrf_sdh_ble - SoftDevice BLE event handler 5 //========================================================== 6 #ifndef NRF_SDH_BLE_ENABLED 7 #define NRF_SDH_BLE_ENABLED 1 8 #endif 9 // <h> BLE Stack configuration - Stack configuration parameters 10 11 // <i> These values are not used directly by the SoftDevice handler but the application or other libraries might depend on them. 12 // <i> Keep them up-to-date with the desired configuration. 13 //========================================================== 14 // <o> NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. 15 #ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 16 #define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 1 17 #endif 18 19 // <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 20 #ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT 21 #define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0 22 #endif 23 24 // <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Maximum number of total concurrent connections using the default configuration. 25 #ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT 26 #define NRF_SDH_BLE_TOTAL_LINK_COUNT 1 27 #endif 28 29 // <o> NRF_SDH_BLE_GAP_EVENT_LENGTH - The time set aside for this connection on every connection interval in 1.25 ms units. 30 #ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH 31 #define NRF_SDH_BLE_GAP_EVENT_LENGTH 8 //預設值3 32 #endif 33 34 // <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size. 35 #ifndef NRF_SDH_BLE_GATT_MAX_MTU_SIZE 36 #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 247 //預設值23 37 #endif 38 39 // <o> NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple of 4. 40 #ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 41 #define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1408 42 #endif 43 44 // <o> NRF_SDH_BLE_VS_UUID_COUNT - The number of vendor-specific UUIDs. 45 #ifndef NRF_SDH_BLE_VS_UUID_COUNT 46 #define NRF_SDH_BLE_VS_UUID_COUNT 1 //預設為0 47 #endif
這裡修改了3個地方,
NRF_SDH_BLE_GAP_EVENT_LENGTH 這個是每個間隔預留給連接的時間
NRF_SDH_BLE_GATT_MAX_MTU_SIZE 這個就是最大MTU了
NRF_SDH_BLE_VS_UUID_COUNT 這個因為我加了兩個自定義服務,所以也要改成1
接下來通過修改 length 和 TIMER_INTERVAL 編譯下載後來測試速率了
註意的是,因為修改過NRF_SDH_BLE_GATT_MAX_MTU_SIZE,所以RAM的地址會發生改變,打開sdk_config.h,修改
1 //========================================================== 2 // <e> NRF_LOG_ENABLED - Logging module for nRF5 SDK 3 //========================================================== 4 #ifndef NRF_LOG_ENABLED 5 #define NRF_LOG_ENABLED 1 6 #endif
1 //========================================================== 2 // <e> NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend 3 //========================================================== 4 #ifndef NRF_LOG_BACKEND_RTT_ENABLED 5 #define NRF_LOG_BACKEND_RTT_ENABLED 1 6 #endif
這樣就可以通過RTT列印的信息來調整RAM的地址和大小了
再次編譯,下載,沒問題後,在andorid手機上使用 ‘nRF Connect’ 進行調試
附上我的測試圖片
數據顯示
這裡使用的就是每29ms發送一次,每次發送244位元組,通過計算,(1000/29)*244 = 8413byte 約 8.2KB/s ,這是我目前測出的最大傳輸速率,但是不停穩定,超出一米的距離基本就會失去連接,另外連接間隔我也有修改
1 #define MIN_CONN_INTERVAL MSEC_TO_UNITS(8, UNIT_1_25_MS) 2 #define MAX_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_1_25_MS) 3 #define SLAVE_LATENCY 0 4 #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
因為初次接觸藍牙,這方面不是太懂,可能寫的不是太好,歡迎指正,或者有測出更高速率的朋友也可以一起交流下
對於改變MTU為什麼會提高發送速率,我抓包分析了一下
從機接收到命令開始發數據
第一個包,可以看出數據只發送到0x13,也就是20個位元組
第二個包,從0x14 發送到了0x2e, 也就是27個位元組
第三個包,從0x2f 到0x49, 也是27個位元組
也就是說,通過修改MTU後,每次發送從第二個包開始都可以達到每個包27個位元組,這樣一來自然就會比之前限定的每次只能發20個位元組要快一點了。
先寫到這裡吧,如果有什麼寫的不對的,歡迎大家指正,後面還需要測試穩定性,距離,功耗,還好實際上也不需要用到8K的傳輸速率,還需要深入的學習BLE,加油!