第08節 開源藍芽協議棧BTStack資料處理

2022-06-21 06:30:10 字數 4369 閱讀 9685

在上篇部落格,通過閱讀btstack的原始碼,大體了解了其框架,對於任何乙個btstack的應用程式都有乙個main函式,這個main函式是統一的。這個main函式做了某些初始化之後,最終會呼叫到應用程式提供的btstack_main,在btstack_main裡面首先做一些初始化,然後呼叫hci_power_on函式去開啟藍芽模組。

一. 資料型別

執行btstack程式時,會生成hci_dump.pklg檔案,可以使用wireshark開啟此檔案,截圖如下:

怎麼理解上圖中的資料呢?

btstack中涉及的資料有2類:

1.從硬體上獲得的資料、發給硬體的資料

2.為更新系統狀態而虛構的資料

① 傳送給藍芽控制器的command

② 從藍芽控制器獲得的event,藍芽控制器收到command後會回覆event

③ acl資料,這涉及收、發兩個方向

④ sco資料,這涉及收、發兩個方向

注意:acl、sco資料的含義以後再講。

這4種資料型別,用乙個頭部資訊來表示,參考bluetooth.h:

#define hci_command_data_packet 0x01

#define hci_acl_data_packet       0x02

#define hci_sco_data_packet       0x03

#define hci_event_packet           0x04

但是在程式中,單憑這4個數值無法分辨資料的流向,比如acl資料的型別是0x03,我們單憑0x03無法知道這資料是發給硬體、還是從硬體讀到。

為了便於除錯,btstack在列印log資訊時,把這些硬體資料型別轉換為新數值:

參考函式: hci_dump_packetlogger_setup_header

1. command :  0x00

2. event:       0x01

3. acl out     0x02

4. acl in      0x03

5. sco out    0x08

6. sco in     0x09

7. log message 0xfc

我們可以使用wireshark開啟log檔案hci_dump.pklg時,觀察裡面原始資料。

2. 為更新系統狀態而虛構的資料:

有很多種虛構的資料,下面舉幾個例子:

① 提示狀態發生了變化:

在btstack中,可能有很多層對hci_stack->state感興趣,所以當hci_stack->state發生變化時,可以使用hci_emit_state傳送乙個虛擬的event資料報,這會導致這些層的處理函式被呼叫。

btstack中使用下面函式傳送state資訊:

在wireshark中看到的原始資料為:01 60 01 xx,

第1個01表示event,60表示btstack_event_state,第2個01表示資料長度為1, xx表示資料即state值。

② 當乙個資料報已經成功發給硬體之後,我們要通知上層:你可以繼續傳送資料給硬體了。這通過hci_emit_transport_packet_sent函式來實現:

在wireshark中看到的原始資料為:01 6e 00,

第1個01表示event,6e表示hci_event_transport_packet_sent,00表示後續資料長度為0。

二、狀態機:

我們常說:初始化好藍芽模組後,就可以使用它了。

仔細琢磨這句話,藍芽模組至少有這2個狀態:

1. 初始化狀態:hci_state_initializing

2. 工作狀態:hci_state_working

當然,還有其他狀態,在**中如下表示(hci_cmd.h):

在hci_state_initializing狀態下,需要跟藍芽模組多次互動,才可以完成藍芽模組的初始化。使用「子狀態」來表示這些多次互動,在**中如下表示(hci.h):

舉個例子,子狀態中有「hci_init_send_reset」和「hci_init_w4_send_reset」:

1.當子狀態為hci_init_send_reset時:

我們傳送復位命令給藍芽模組,然後子狀態變為hci_init_w4_send_reset,它的意思是「wait for」,等待收到復位命令的回覆資訊。

2.收到該回覆資訊後,子狀態變為hci_init_send_read_local_version_information:

於是繼續給藍芽模組傳送「read loacal version」命令,然後子狀態變為hci_init_w4_send_read_local_version_information,表示等待回覆資訊

如此繼續,直到子狀態變為「hci_init_done」,初始化才結束,藍芽模組的「狀態」才放為hci_state_working。

**中有乙個結構體:

static hci_stack_t * hci_stack

hci_stack->state表示「狀態」,hci_stack->substate表示「子狀態」。

btstack的**有函式hci_run,它就是根據hci_stack結構體中的這些狀態、其他值來收發資料的。

注意:hci.c中的hci_run是核心函式,它根據hci_stack的狀態進行不同的處理。

舉例說明:

1.例子1:hci_power_control(hci_power_on);

hci_stack->state初始值為0,即hci_state_off;

呼叫hci_power_transition_to_initializing後,各狀態值如下:

// set up state machine

hci_stack->num_cmd_packets = 1; // assume that one cmd can be sent

hci_stack->hci_packet_buffer_reserved = 0;

hci_stack->state = hci_state_initializing;

hci_stack->substate = hci_init_send_reset;

接著呼叫如下**:

// trigger next/first action

hci_run();

hci_run函式中,在hci_stack->state等於hci_state_initializing時,呼叫:hci_initializing_run();

hci_initializing_run()函式內部,會根據hci_stack->substate等於hci_init_send_reset而發出復位命令,並令substate等於hci_init_w4_send_reset,這表示等待收到該命令的回覆資訊。

在等待過程中,程式休眠。

當收到資料時,程式的主迴圈繼續執行,根據上節內容,將會呼叫hci.c中的event_handler函式來處理

該函式有如下**:

// handle bt initialization

if (hci_stack->state == hci_state_initializing)

……hci_run( );

模組的當前狀態仍為hci_state_initializing,進而呼叫hci_initializing_event_handler(packet, size)。

hci_initializing_event_handler將呼叫hci_initializing_next_state(),把subsate設定為hci_init_send_read_local_version_information。

後續的hci_run會根據這個substate發出read_local_version_information的命令。

藍芽協議棧分層

更詳細的藍芽協議棧可參考文章 深入淺出低功耗藍芽 ble 協議棧 藍芽協議棧的分層,所有 profile 配置檔案層 和應用都建構在gap或gatt之上。在 bluetooth le中有四種裝置型別 central主機 peripheral從機 observer觀察者 broadcaster廣播者。...

nordic 藍芽協議棧

藍芽協議的實現叫做藍芽協議棧 藍芽協議棧有以下內容組成 應用層主協議層 控制層 物理層,主要負責指定ble的無線頻段,調製解調方式和方法等。直接決定了ble晶元的功耗靈敏度。鏈路層 協議棧核心 主要負責選擇射頻通道,如何識別空中資料報,傳送資料的時間控制,資料完整性的保證,ack應答實現,重傳機制實...

幾種開源協議棧介紹

協議棧與協議 協議棧是指網路中各層協議的總和,其形象的反映了乙個網路中檔案傳輸的過程 由上層協議到底層協議,再由底層協議到上層協議。使用最廣泛的是英特網協議棧,由上到下的協議分別是 應用層 http,telnet,dns,email等 運輸層 tcp,udp 網路層 ip 鏈路層 wi fi,乙太網...