在上篇部落格,通過閱讀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,乙太網...