BLE-NRF51822教程3-sdk程式架構剖析

來源:互聯網
上載者:User

BLE-NRF51822教程3-sdk程式架構剖析


nordicBLE 技術交流群498676838

本講為架構介紹,不會牽涉到太多代碼細節。

51822的官方SDK其實是沒有架構依耐性的。什麼叫架構,比如TI的BLE SDK中就有一個作業系統抽象層(OSAL)他是一個輪訓的調度。你需要按照他的方式去建立任務等等。

而51822的SDK本質上只是提供了各種調用介面,比如開啟初始化協議棧,初始化一些硬體功能模組,開始廣播,發起連結等等。這些介面怎麼用完全取決於自己。不過一般韌體開發都是一些類似的流程各種資源的初始化,51822也不例外。所以sdk中的作為從機的例子main函數都是類似如下的步驟:

以官方的串口BLE 為例:

int main(void)

{

leds_init(); //非必須,只是該例子中用到了

timers_init(); //非必須,只是該例子中用到了

buttons_init(); //非必須,只是該例子中用到了

uart_init(); //非必須,只是該例子中用到了串口

ble_stack_init(); //必須

gap_params_init(); //必須

services_init(); //跟自己建立的服務相關,不同的服務細節不同但大體建立
//過程基本一致,通常在直接使用官方的例子修改一些參數即可

advertising_init(); //廣播資料初始化,必須

conn_params_init(); //是情況而定,如果串連後不需要串連參數的協商,該初始化也 //可不要

sec_params_init(); //安全參數初始化,如果沒用到配對綁定相關這個也可以不初始化

advertising_start(); //開啟廣播,必須

// Enter main loop

for (;;)

{

power_manage(); //進入睡眠

}

}

可以看到其實核心必要的只有這5個函數而已。你可以將其他代碼全都去掉,只要留下這5個函數裝置一樣可以運行,手機也能搜到裝置並與裝置通訊。

這種初始化的方式可以說是與我們一般的單片機開發沒有區別。

那麼初始化之後呢。以前的裸板單片機開發我們就是進入一個while迴圈執行一些周而復始的事,後面為了降低功耗開始在while(1)迴圈中加個睡眠代碼讓沒有工作時晶片處於睡眠狀態,並依靠中斷來喚醒從而處理到來的事物。

而上面的51822的main函數最後也是一個for{}迴圈,power_manage(); 內部代碼其實就是一個睡眠指令。Main函數到這裡就已經沒了,最後其實就是一個迴圈睡眠。這裡看不到任何任務(task),只有睡眠。那麼可想而知,51822的協議棧實現應該是基於”事件喚醒”的,也就是沒事的時候睡眠,有事的時候喚醒工作而後繼續睡眠。那麼那些處理事件的代碼都是在哪裡的?

那協議棧到底是怎麼運作的?

我希望建立一個服務在哪裡添加?

手機發送來的資料在哪裡?

我怎麼發送資料給手機?

下面一一解釋這些問題:

協議棧如何運作?

要明白協議棧怎麼運作,首先就要理解51822的協議棧是基於100%的事件驅動的。就是說協議棧向app發送的任何資料都是基於事件的。

比如裝置收到手機發來的連結請求,或是手機發過來的資料等等。協議棧首先收到這些資料後做一些處理,然後將這些資料(比如連結請求,或是普通資料等)打包成一個結構體,並附上事件ID,比如BLE_GAP_EVT_CONNECTED或BLE_GATTS_EVT_WRITE來分別告訴上層app這個事件結構體代表的事件。

比如BLE_GAP_EVT_CONNECTED代錶鏈接事件,那麼這個事件結構體中包含的資料就是串連參數等資料。

而BLE_GATTS_EVT_WRITE代表寫事件,那麼結構體中的資料就是對端裝置(比如手機)寫給板子的資料。



比如uart的demo中dispatch派發函數

static void ble_evt_dispatch(ble_evt_t *p_ble_evt)

{

ble_conn_params_on_ble_evt(p_ble_evt);

ble_nus_on_ble_evt(&m_nus, p_ble_evt);

on_ble_evt(p_ble_evt);

}

在任何與BLE相關的事件被協議棧上拋上來給app時,ble_evt_dispatch就會被調用。從而將事件拋給各個服務函數或處理模組,這裡是將事件拋給了

串連參數管理處理函數ble_conn_params_on_ble_evt

Uart服務的事件處理函數ble_nus_on_ble_evt (nus為Nordicuart server)

通用的事件處理函數on_ble_evt

不同的事件在事件結構體ble_evt_t中通過id來區別。不同是事件處理函數通常也只是處理自己感情去的事件,我們來看看ble_nus_on_ble_evt事件處理函數的內部

voidble_nus_on_ble_evt(ble_nus_t * p_nus,ble_evt_t * p_ble_evt)

{
if ((p_nus == NULL) || (p_ble_evt == NULL))

{

return;

}

switch (p_ble_evt->header.evt_id)

{

caseBLE_GAP_EVT_CONNECTED:

on_connect(p_nus, p_ble_evt);

break;

caseBLE_GAP_EVT_DISCONNECTED:

on_disconnect(p_nus, p_ble_evt);

break;

caseBLE_GATTS_EVT_WRITE:

on_write(p_nus, p_ble_evt);

break;

default:

// No implementation needed.

break;

}

}

可以看到,uart服務事件處理函數只關心三個事件,連結事件,取消連結事件以及寫事件(對端裝置發資料過來),不同的事件再針對做不同的,這個就由開發人員自己來實現了。比如對於串連事件通常應該記錄下事件結構體中的串連控制代碼,因為後續的BLE操作基本都要基於串連控制代碼(可以看做是兩個裝置通訊的通道ID,實際為鏈路層中的資料接入地址概念)。

PS: 事件是交給dispatch來派發給各個服務以及模組的,對於更底層的事件又是如何交給dispatch函數的過程請參考群公告中的 51822教程-協議棧概述教程。


解決了所謂的事件驅動再來解決:如果希望建立一個服務在哪裡添加?

在main函數的初始化過程中有一個services_init();這個函數的內部就是添加服務,添加特徵值等代碼。

函數內部其實就是註冊了一會回呼函數nus_data_handler(該函數會在手機發資料給板子時將資料從電腦串口列印出來) 然後再執行真正的初始化函數ble_nus_init。

該函數的內部又會調用sd_ble_gatts_service_add這個協議棧的api介面來添加服務。

後面也會調用sd_ble_gatts_characteristic_add這個協議棧的api介面來添加特徵值。

層次關係如下:


也就是說完成一個完整的服務建立函數其實只要sd_ble_gatts_service_add()和sd_ble_gatts_characteristic_add()這兩個核心函數。

通常建立服務並不需要自己去從頭寫過。而是直接賦值官方的這個services_init()函數,然後做一些小改動就可以。比如修改一下uuid, 修改一下讀/寫屬性,多添加一個特徵值等。要修改的其實很少。


下面解決最後兩個問題:手機發送來的資料在哪裡?我怎麼發送資料給手機?

要搞清楚這兩個問題,先來看一下群裡常問的幾個與上面相關的問題:



問:

手機發給51822裝置的資料在哪個函數裡出來的呀,
答:
沒有函數
協議棧會拋上來一個事件結構體
收到的資料在結構體中


問:

藍芽上傳函數,與下發函數都是一樣的嗎?都是服務API函數?
答:
只有上傳函數是伺服器用來將資料傳給用戶端的。
下發資料是藍芽晶片收到資料後,協議棧會拋上來一個有資料的事件結 構體。具體參看範例程式碼中的dispatch派發程式中各個事件處理函數對各 種事件的資料。

問:

sd_ble_gatts_hvx()這個函數是藍芽的發送函數,有知道藍芽的接收函數?
答:
藍芽沒有接收函數,藍芽的資料接收在底層,接收完後會返回事件給上層的 ble_evt_dispatch分發函數,它將事件分發給各個服務或者事件處理函數。服 務或處理函數會捕獲是否存在寫事件caseBLE_GATTS_EVT_WRITE:存在就做 相應的處理。收到的資料都在返回的事件結構體裡

其實看完這三個問題基本上上面的問題其實已經解決差不多了。作為從裝置,BLE的發送資料給手機是有API介面的,就是上面問到的sd_ble_gatts_hvx(),可以通過參數來設定是以通知方式發送還是指示方式發送(通知不需要回複確認,指示需要)。但是手機發過來資料卻是沒有接收函數,為什嗎?因為協議棧是基於事件驅動的!所以收到資料後協議棧會給上層app一個寫事件(指示對端裝置寫資料過來了),而寫過來的資料時在這個事件結構體中。我們只要提取出來就行了。所以沒有接收函數API。


從另一方面也可以解釋為什麼沒有接收資料函數。因為發送資料時”同步的”,是主動調用的,在往想發送資料的時候。但是接收資料時”非同步”,資料可能隨時到來,總不來一直調用一個函數然後原地等待資料到來吧,如果資料不來豈不是什麼事都幹不了了。所以接收是基於事件驅動的。有資料來再轉過去處理。

用個圖來解釋下:



如果還是覺得有點抽象,回到前面看看協議棧運作講解部分。應該更能體會所謂的事件驅動



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.