1. 程式人生 > >nRF52832 改變ATT_MTU提高藍芽資料傳送速率(nRF5_SDK_14.2.0)

nRF52832 改變ATT_MTU提高藍芽資料傳送速率(nRF5_SDK_14.2.0)

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,加油!