nRF52832 改變ATT_MTU提高藍牙數據發送速率(nRF5_SDK_14.2.0)

来源:https://www.cnblogs.com/jiangjiu/archive/2018/12/04/10063556.html
-Advertisement-
Play Games

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前運行的那些進程。ps命令列出的是當前那些進程的快照,就是執行ps命令的那個時刻的那些進程,如果想要動態的顯示進程信息,就可以使用top命令。 要對進程進行監測和控制,首先必須要瞭解當前進程的情況,也就是需要查看當前 ...
  • 1.kangle官方腳本 linux下easypanel版本安裝及升級(集成了kangle web 伺服器和mysql,僅支持centos 5和centos 6)執行下麵的命令即可,安裝程式將自動安裝或者升級。 yum -y install wget;wget http://download.kan ...
  • 與jenkins構建項目記錄1不同的是通過tag拉去對應版本代碼 1、先安裝創建(git parameter) 2、general設置 name可任意命名,下麵源碼管理設置時變數會引用到。 3、源碼管理 ${tag}中tag名必須與上面Git Parameter中命名的name一致 4、設置完成進行 ...
  • 一、Spring Boot 入門 1、Hello World探究 1、POM文件 1、父項目 Spring Boot 版本仲裁中心: 以後我們導入依賴預設是不需要寫版本:(沒有在dependencies裡面管理的依賴自然需要寫版本號) 2、啟動器 spring-boot-starter-web: S ...
  • 有多種方式安裝jenkins 方式一、通過Tomcat發佈 安裝tomcat和jdk、maven1、安裝jdk環境jdk下載地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html( ...
  • ``` # 開啟防火牆 systemctl start firewalld.service # 防火牆開機啟動 systemctl enable firewalld.service # 關閉防火牆 systemctl stop firewalld.service # 查看防火牆狀態 firewall... ...
  • 本文最早發佈於我的51CTO博客,目前已遷移至博客園。 參考: "port的英文維基百科" ) 簡介 MAC地址定義了數據包的下一跳地址。 IP地址定義了數據包最終應該傳輸到哪台電腦上。 而埠(port)則定義了數據包中的數據應該由電腦上的哪個進程來接收。 埠是一種邏輯上的概念,用來識別一個 ...
  • 1. 部署cadvisor容器,用來收集host上的容器信息,該容器部署在需要收集容器信息的每一個主機上部署; docker run -v /:/rootfs:ro -v /var/run:/var/run:rw -v /sys:/sys:ro -v /var/lib/docker:/var/lib ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...