1. 程式人生 > >BLE藍芽協議 — BLE連線建立過程梳理(二)

BLE藍芽協議 — BLE連線建立過程梳理(二)

主裝置和從裝置建立連線之後,所有的資料通訊都是在連線事件(Connection Events)中進行的。


尖刺的波就是連線事件(Connection events),剩下的Sleeping是睡眠時間,裝置在建立連線之後的大多數時間都是處於Sleeping,這種情況下耗電量比較低,而在連線事件(Connection events)中,耗電量就相對高很多,這也是BLE為什麼省電的原因之一。

每個連線事件(Connection events)中,都需要由Master發起包,再由Slave回覆。

Master即主機,簡稱MSlave即從機,簡稱S。抓包過程中看到的M->S或者S->M即主機到從機或者從機到主機。

連線引數 (Connection Parameters)

通過修改下面三個引數,就可以設定BLE連線過程中的傳輸速度和功耗。

1.Connection Interval(連線間隔)



Connection Interval(GAPROLE_MIN_CONN_INTERVAL && GAPROLE_MAX_CONN_INTERVAL)連線間隔,在BLE的兩個裝置的連線中使用跳頻機制。兩個裝置使用特定的通道傳送和接收資料,然後過一段時間後再使用新的通道(BLE協議棧的鏈路層處理通道的切換)。兩個裝置在切換通道後傳送和接收資料稱為一個連線事件。儘管沒有應用資料被髮送和接收,兩個裝置仍舊會交換鏈路層資料(空包 Empty PDU)來維持連線。

這個連線間隔就是指在一個連線事件(Connection events)的開始到下一個連線事件(Connection events)的開始的時間間隔。連線間隔以1.25ms為單元,連線間隔的範圍是63200既7.5ms4s之間。

2.Slave Latency(從裝置延遲或者從裝置時延)


允許Slave(從裝置)在沒有資料要發的情況下,跳過一定數目的連線事件(Connection events),在這些連線事件(Connection events)中不必回覆Master(主裝置)的包,這樣就能更加省電。

範圍可以是0 ~ 499

更詳細的使用解析如下:


Slave Latency = OFF也就是Slave Latency為0時,Master發包,Slave必須回覆,如果不回覆,Master就會認為Slave那邊接收不正常。

Slave Latency = ON也就是Slave Latency不為0的時候,圖中Slave Latency為 3Master發包,Slave沒有資料要回復的時候,就會忽略 個連線事件,在第 個連線事件接收到Master傳送的資料之後,回覆Master。如果Slave有資料要傳送就會喚醒,也就是說即使Slave Latency為 3,但是在Master發第二包的時候Slave有資料要回復,這個時候就會立即回覆Master而不是等到 個連線事件之後的第 個連線事件去回覆。

3.Supervision Timeout(超時時間或者監控超時)


這個引數設定了一個超時時間,如果BLE在這個時間內沒有發生通訊的話,就會自動斷開。

單位是 10ms,該變數的範圍是10 ~ 3200,折算成時間範圍是100ms ~ 32s 。

連線間隔、從機時延以及超時時間這三者必須滿足如下公式:

Supervision Timeout  > (1 +slaveLatency)* (connectionInterval)

上述公式必須滿足,否則連線就會不正常斷開。

這三個連線引數不同情況下對通訊速率和功耗的影響:

1.Connection Interval縮短,Master和Slave通訊更加頻繁,提高資料吞吐速度,縮短了資料傳送的時間,當然也增加了功耗。

2.Connection Interval增長,通訊頻率降低,資料吞吐速度降低,增加了資料傳送的時間,當然,這種設定降低了功耗。

3.Slave Latency減少或者設定為 0,每次Connection Events中都需要回復Master的包,功耗會上升,資料傳送速度會提高。

4.Slave Latency加長,功耗下降,資料傳送速度降低。

連線引數更新規程

連線建立時,主裝置通過連結請求資料包傳送連線引數。當連線活躍了一段時間,連線引數也許不再適用於當前使用的服務。出於提高效率的目的,連線引數需要進行更新。較之首先斷開連線、接著更換新引數重新連線,還有一種在鏈路中更新引數更為簡單的途徑,如下圖所示:


為此,主裝置向從裝置傳送連線更新請求,即LL_CONNECTION_UPDATE_REQ,當中攜帶了新的引數。這些引數不必進行協商,從裝置或者接受和使用它們,或者斷開鏈路。連線更新請求中包含了早先建立連線時用過的一部分引數,還有一個稱為瞬時(instant)的新引數:

1.傳輸視窗大小

2.傳輸視窗偏移量

3.連線間隔

4.從裝置延遲

5.監控超時

6.瞬時

瞬時引數決定了連線更新的開始時刻。傳送訊息時,主裝置為連線更新選定一個未來的時間點,並且放在訊息中。接到訊息後,從裝置會記住這個未來的時刻,屆時再切換至新的連線引數。這有助於解決無線系統裡的一個最大問題----報文重傳。只要資料包的重傳次數足夠,並最終在瞬時之前傳輸成功,上述過程執行起來就不會有問題。但是,如果該資料包屆時沒能完成傳輸,鏈路就有可能丟失。

由於低功耗藍芽沒有時鐘,要決定瞬時時刻只有依靠計算連線事件的個數。因此,每一個連線事件都會被計數,鏈路上的第一個連線事件,也就是在連線請求之後的位於首個傳輸窗口裡的連線事件記為 0。因此,瞬時實際上是一個連線事件的計數器,相應的連線事件到來時就使用新的引數。為了讓從裝置收到資料包,主裝置必須為其提供足夠的機會。不過從裝置延遲是多少,都應該至少保證 6 次資料傳送機會。也就是說,如果從裝置延遲為 500ms,那麼瞬時通常被設定在 3s 之後的某個未來時刻。

瞬時到來時,從裝置開始偵聽傳送視窗,就好像連線建立的過程那樣。主裝置能夠調整從裝置的計時,總體而言不超過 1.25ms。不過,由於主裝置可能還是一個經典藍芽裝置,上述調整使其得以協調低功耗藍芽從裝置,從而更好地完成排程。一旦該過程結束,新的連線間隔、監控超時、從裝置延遲值將投入使用。

連線引數的修改

連線引數更新請求命令可以讓從裝置更新鏈路層的連線引數,如下圖所示。這些引數包括連線間隔(從裝置希望主裝置允許從裝置傳送資料包的頻率)、從裝置延遲(從裝置能夠忽略主裝置的連線事件的最大值)以及監控超時。


在連線中,如果從裝置希望修改當前的連線引數則可以使用該命令。比方說,如果連線事件的間隔有可能太快了,導致過多的電量浪費。這在從裝置時延很大時沒有問題,但如果不是這樣,從裝置將會頻繁的偵聽鏈路。這在一些情況下是必要的,例如裝置間首次繫結、互發多個數據包、探索服務和裝置特性等。但在很多其他情況下,儘可能地減少從裝置必須偵聽連線事件的數量對提高電池壽命至關重要。

連線引數更新請求命令僅用於從裝置向主裝置傳送,這是由於主裝置隨時都能啟動鏈路層連線引數更新控制(Connection Parameter Update Control)規程。如果該命令由主裝置傳送,從裝置會將其視為一個錯誤,並返回帶有命令不理解原因程式碼的命令拒絕命令。

從裝置可以在任何時候傳送該命令;收到該資訊的主裝置如果可以修改連線引數,則將返回連線引數更新響應(Connection Parameter Update Response),其中的結果程式碼設為接受(accepted)。隨後,主裝置將會啟動鏈路層連線引數更新控制規程。

當然,如果主裝置不同意從裝置的請求引數,它可以傳送結果程式碼為拒絕(rejected)的連線引數更新響應命令以拒絕請求。此時從裝置有兩個選擇:要麼接受主裝置希望的正在使用的連線引數,要麼終止連線。終止連線的做法咋看起來可能讓人覺得很激進,但是,假如使用當前的引數從裝置將會在一週內耗盡電量,而使用請求的引數則可以持續數年,很明顯,合理的選擇只有一個。

修改連線引數時,如果要減少主裝置拒絕從裝置請求的可能性,可以在請求裡設定一個可接受的引數範圍。經過精心設計的從裝置會樂意接受很寬的引數範圍。由於主裝置可能正忙於實時會話音訊連線或者高質量語音連線等任務,它可以接受一定範圍內的連線間隔引數。裝置可接受的間隔引數會根據當前任務的不同而不同,可能有別於上一次裝置連線時的引數。

要提高主裝置接受連線引數的機率,還有個方法是從裝置提供一個合理的從裝置延遲。主裝置可以選擇最合適的連線事件間隔,從裝置則使用最佳功耗的從裝置延遲引數。

舉個例子,如果從裝置想每 600ms 同步一次,它可以請求範圍 100ms ~ 750ms 的連線間隔引數,並帶上從裝置延遲5。如果主裝置選擇 100ms,則從裝置每6個連線事件同步一次;如果主裝置選擇 200ms,則從裝置每 個連線事件同步一次,實現其所期望的 600ms 間隔;如果主裝置選擇 300ms,則從裝置忽略每隔一個連線事件同步一次;如果主裝置選擇 400ms,則從裝置每 400ms 同步一次。

下面介紹一下在TICC2540CC2541中,連線引數修改的方法

(一)連線成功建立之後從裝置自動申請修改連線引數。

我們以TI 1.4.0協議棧中的simpleBLEPeripheral工程為例來進行講解,在這個工程的Projects\ble\SimpleBLEPeripheral\Source\simpleBLEPeripheral.c應用檔案中定義瞭如下的巨集:

  1. // Whether to enable automatic parameter update request when a connection is formed
  2. #define DEFAULT_ENABLE_UPDATE_REQUEST         TRUE
從上面的註釋中,我們可以看出這個巨集的作用是當一個連線建立的時候,是否需要自動申請連線引數更新。當設定為TRUE的時候就是需要,當設定為FALSE的時候就是不需要。那這個巨集到底是如何起作用的呢?下面我們來看一下。

1.Projects\ble\SimpleBLEPeripheral\Source\simpleBLEPeripheral.c應用檔案中的SimpleBLEPeripheral_Init初始化方法裡對該巨集進行了處理,原始碼如下:

  1. uint8 enable_update_request = DEFAULT_ENABLE_UPDATE_REQUEST;  
  2. GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, sizeof( uint8 ), &enable_update_request );  

2.我們看下GAPRole_SetParameter方法裡面的相關的操作,相關操作在Projects\ble\Profiles\Roles\peripheral.c檔案裡,原始碼如下:

  1. case GAPROLE_PARAM_UPDATE_ENABLE:  
  2.     if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )  
  3.     {  
  4.         gapRole_ParamUpdateEnable = *((uint8*)pValue);  
  5.     }  
  6.     else
  7.     {  
  8.         ret = bleInvalidRange;  
  9.     }  
  10.     break;  

3.從上面的程式碼不難看出,將我們設定的巨集賦給了gapRole_ParamUpdateEnable全域性變數,下面我們在本檔案中搜索一下該全域性變數使用的地方,發現在如下原始碼中用到了:

  1. // 連線成功建立之後底層返回的事件
  2. case GAP_LINK_ESTABLISHED_EVENT:  
  3. {  
  4.     gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;  
  5.     if ( pPkt->hdr.status == SUCCESS )  
  6.     {  
  7.         VOID osal_memcpy( gapRole_ConnectedDevAddr, pPkt->devAddr, B_ADDR_LEN );  
  8.         gapRole_ConnectionHandle = pPkt->connectionHandle;  
  9.         gapRole_state = GAPROLE_CONNECTED;  
  10.         if ( gapRole_RSSIReadRate )  
  11.         {  
  12.             // Start the RSSI Reads
  13.             VOID osal_start_timerEx( gapRole_TaskID, RSSI_READ_EVT, gapRole_RSSIReadRate );  
  14.         }  
  15.         // Store connection information
  16.         // 儲存連線剛建立時的連線引數
  17.         gapRole_ConnInterval = pPkt->connInterval;  
  18.         gapRole_ConnSlaveLatency = pPkt->connLatency;  
  19.         gapRole_ConnTimeout = pPkt->connTimeout;  
  20.         // Check whether update parameter request is enabled
  21.         // 檢測更新連線引數請求是否被使能
  22.         if ( gapRole_ParamUpdateEnable == TRUE )  
  23.         {  
  24.             // Get the minimum time upon connection establishment before the 
  25.             // peripheral can start a connection update procedure.
  26.             // 獲取設定的時間間隔,從機將在連線建立之後
  27.             // 延時至少該時間間隔之後觸發連線引數更新事
  28.             // 件。
  29.             uint16 timeout = GAP_GetParamValue( TGAP_CONN_PAUSE_PERIPHERAL );  
  30.             // 在延時timeout*1000 ms之後觸發連線引數更新事件
  31.             osal_start_timerEx( gapRole_TaskID, START_CONN_UPDATE_EVT, timeout*1000 );  
  32.         }  

4.上面的註釋非常清楚了,在連線成功建立返回的事件中判斷我們設定的巨集,如果設定為TRUE,那就獲取我們設定的時間間隔,在延時我們設定的時間間隔(上面註釋中提到至少,因為用的是系統定時器,有可能在執行別的事件,所以實際的延時時間會大於我們設定的時間,當然,一般情況下偏移的那點時間是可以忽略的)之後,觸發連線引數更新事件,進行連線引數的更新。那上面原始碼中獲取的時間間隔以及後面要更新的連線引數是在什麼地方設定的呢?下面我們繼續回到應用層檔案中檢視相關設定。

5.在Projects\ble\SimpleBLEPeripheral\Source\simpleBLEPeripheral.c檔案中定義瞭如下巨集用來設定自動更新連線引數時,相關連線引數的值,原始碼如下:

  1. // Minimum connection interval (units of 1.25ms, 80=100ms) if automatic parameter update request is enabled
  2. // 如果自動更新連線引數請求被使能的話,用到的最小連線間隔,單位1.25 ms
  3. #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     80
  4. // Maximum connection interval (units of 1.25ms, 800=1000ms) if automatic parameter update request is enabled
  5. // 如果自動更新連線引數請求被使能的話,用到的最大連線間隔,單位1.25 ms
  6. #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     800
  7. // Slave latency to use if automatic parameter update request is enabled
  8. // 如果自動更新連線引數請求被使能的話,用到的從機時延
  9. #define DEFAULT_DESIRED_SLAVE_LATENCY         0
  10. // Supervision timeout value (units of 10ms, 1000=10s) if automatic parameter update request is enabled
  11. // 如果自動更新連線引數請求被使能的話,用到的超時時間,單位10 ms
  12. #define DEFAULT_DESIRED_CONN_TIMEOUT          1000
  13. // Connection Pause Peripheral time value (in seconds)
  14. // 如果自動更新連線引數請求被使能的話,用到的時間間隔,單位s
  15. #define DEFAULT_CONN_PAUSE_PERIPHERAL         6

6.通過上述巨集進行相應設定的地方在Projects\ble\SimpleBLEPeripheral\Source\simpleBLEPeripheral.c檔案的SimpleBLEPeripheral_Init初始化方法中,原始碼如下:

  1. VOID GAP_SetParamValue( TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL );  
  2. uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;  
  3. uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;  
  4. uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;  
  5. uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;  
  6. GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );  
  7. GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );  
  8. GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), &desired_slave_latency );  
  9. GAPRole_SetParameter( GAPROLE_TIMEOUT_MULTIPLIER, sizeof( uint16 ), &desired_conn_timeout );  

上述操作在Projects\ble\Profiles\Roles\peripheral.c檔案裡的具體實現我們就不一起看了,因為裡面其實就是一個賦值的過程,所以大家自行檢視即可。

(二)連線成功建立之後從裝置在需要的時候去修改某個連線引數或者全部的連線引數。

1.修改單個連線引數的方法

修改最小連線間隔

  1. uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;  
  2. GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );  

修改最大連線間隔

  1. uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;  
  2. GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );  

修改從裝置延遲

  1. uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;  
  2. GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), &desired_slave_latency );  

修改超時時間

  1. uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;  
  2. 相關推薦

    BLE協議BLE連線建立過程梳理

    主裝置和從裝置建立連線之後,所有的資料通訊都是在連線事件(Connection Events)中進行的。尖刺的波就是連線事件(Connection events),剩下的Sleeping是睡眠時間,裝置在建立連線之後的大多數時間都是處於Sleeping,這種情況下耗電量比較低,而在連線事件(Connectio

    CC2640R2F BLE5.0 協議棧通用屬性配置檔案GATT

    通用屬性配置檔案(GATT) 正如GAP層負責連線相關的功能,GATT主要是負責在兩個已經連線的裝置互動資料,GAP層把BLE裝置區分為主機Master(Central)和從機Slave(Perpherial),在GATT層則區分為Server和Client。客

    1、核心技術瞭解協議、架構、硬體和軟體筆記

    原文地址:http://www.cnblogs.com/zjutlitao/p/4742428.html 宣告:這篇文章是樓主beautifulzzzz學習網上關於藍芽的相關知識的筆記,其中比較多的受益於xubin341719的藍芽系列文章,同時還有其他網上作者的資料。由於有些文章只做參

    核心技術瞭解協議、架構、硬體和軟體筆記

    下面是摘抄筆記內容:     藍芽,是一種支援裝置短距離通訊(一般10m內)的無線電技術。能在包括行動電話、PDA、無線耳機、膝上型電腦、相關外設等眾多裝置之間進行無線資訊交換。利用“藍芽”技術,能夠有效地簡化行動通訊終端裝置之間的通訊,也能夠成功地

    Mesh網路效能及網路特點總結(

    原文連結:(歡迎關注公眾號 智聯網事,一週一篇原創文章,一起探討智聯網) https://mp.weixin.qq.com/s?__biz=MzI3NDE2NDMwNQ==&mid=2649905766&idx=1&sn=27820d890dc9ed91f305b86d0

    速攻NRF52832系列教程之方法篇

    三、如何快速攻克一個SoC晶片     在這個講求速度、效率的年代,慢一步就會愈發被動,錯失諸多良機。     對產品研發公司來說,工程師如能迅速攻克一款更新更強的晶片,搶先發布產品,就能早一步搶佔市

    在ArcGIS中建立Python工具

    上一篇中我們瞭解到有兩種方式在 ArcGIS 中建立 Python工具,這一篇就來看看如何在標準工具箱中建立指令碼工具。 ArcGIS Help 中指令碼工具的幫助過於枯燥,在這裡,我以一個具體的例項來總結構建指令碼工具的過程,我要實現的需求是做個快速實現羽化邊界效果的小工具,

    AliOS Things的啟動過程分析

    AliOS Things的啟動過程分析(二) 在AliOS Things的啟動過程分析(一)中分析了developerkit從系統上電到呼叫main函式所經歷的一些步驟,接下來詳細分析一下main函式的一些工作,主要是核心的相關初始化工作。main函式所處的位置位於    

    JavaFX學習筆記——重要理念的建立與辨析

    並非萬物皆為Node 對Stage和Scene的大小位置等屬性設定與Node不統一             可能是之前用過QT以及cocos2dX的先入為主的觀念導致的問題 &n

    dubbo-php-framework的服務註冊zookeeper過程解析

    我們接著dubbo-php-framework的服務註冊zookeeper過程解析(一)繼續分析剩下的介面。 //建立zk service,這裡的zk service也是封裝過的,不是原生zk的ser

    資料庫連線池的學習——DBCP連線

           DBCP(DataBase connection pool),資料庫連線池。是 apache 上的一個 java 連線池專案,也是 tomcat 使用的連線池元件。單獨使用dbcp需要2個包:commons-dbcp.jar,commons-pool.jar。

    建立型模式

    關於建立型模式中工廠方法模式,抽象工廠模式在上一篇文章中我們瞭解了一下,今天我們看一下單例模式,建造者模式,原型模式。 單例模式 單例模式我們用的很多,我想大家也都理解這種模式,就是保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 單例模式(Singleton

    建立型工廠:抽象工廠模式

    抽象工廠模式 抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠建立其他工廠。該超級工廠又稱為其他工廠的工廠。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最

    Arduino ESP8266 建立OneNet裝置

    使用者註冊 使用者註冊和產品建立我們在OneNET平臺上開展產品開發的前提。為了使用OneNET裝置雲的強大功能,首要做的是在OneNET上註冊您的開發者賬號,來建立您專屬的“開發者中心”; 點選首頁右上角的“註冊”按鈕,註冊使用者賬號; 填寫使用者名稱、使用者密碼、有效手機號

    軟體設計模式-建立型模式

     生成器模式:          生成器模式將一個複雜物件的構建與他的表示分離,使得同樣的構建過程可以建立不同的表示。          設計類圖:  組成部分:   (1)Builder:建立Product物件的抽象介面    (2)  Concrete Builde

    使用腳手架建立 React 專案

    在使用腳手架建立 React 專案(一)的基礎上,繼續進行專案的修改。目標:建立簡單的todo元件,只為了簡單的新增功能1  npm run eject //將專案的配置資訊從預設的依賴包中提取出來,會生成config資料夾2 使用編輯器,此處使用HbuilderX。(wes

    通過Jedis客戶端連線不到redis

    之前我的一篇文章,也是解決Jedis連線不到redis的,但是情況不一樣,之前的問題主要是防火牆的問題,但是現在看來並不是防火牆的問題,因為redis自身也有配置來限制外網的訪問,所以當時也不知道為什麼就可以了,今天主要以redis配置的角度來看一下,如何解決外網訪問red

    從零開始建立uCosIII專案:配置uCosIII

    新增BSP檔案 新建BSP.h檔案,內容如下 #ifndef _BSP_H_ #define _BSP_H_ #include <stdarg.h> #include <stdio.h> #include <cpu.h> #i

    Spring IoC容器構建過程分析草稿,持續整理中

    接上一篇的內容:http://blog.csdn.net/caihaijiang/article/details/35795781 5、invokeBeanFactoryPostProcessors 該方法的主要功能就是從spring配置檔案中,獲取實現 BeanFacto

    docker建立本地映象及Dockerfile的語法

    docker export 容器名 > my_container.tar,我們將容器本地化為映象,用cat my_container.tar | sudo docker import - imp