<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從零開始的nrf52832藍(lán)牙開發(fā)--藍(lán)牙模板解析

          共 4164字,需瀏覽 9分鐘

           ·

          2022-05-27 16:55

          ????關(guān)注、星標(biāo)公眾號,直達(dá)精彩內(nèi)容

          來源:https://blog.csdn.net/qwe5959798/article/details/120152350



          本篇文章使用SDK版本為?15.3.0,開發(fā)板使用官方開發(fā)板PCA10040。


          我們打開nRF5_SDK_15.3.0\examples\ble_peripheral\experimental\bluetoothds_template\pca10040\s132\ses
          由SDK說明文檔可知,這個例子是用Bluetooth Developer studio生成的一個模板工程,這個開發(fā)工具是由SIG推出的,不過感覺國內(nèi)用的人很少。

          代碼功能

          LED1指示連接狀態(tài),如果藍(lán)牙未被連接,LED1閃爍,如果藍(lán)牙被連接,則LED1常亮。按鍵1連接狀態(tài)下長按2S可以斷開連接并重新開啟一個無白名單廣播,未連接狀態(tài)下按下會進(jìn)入休眠狀態(tài),再按下喚醒。按鍵2也可以喚醒設(shè)備但會刪除所有綁定信息,正常運行中按下會關(guān)閉白名單。廣播超過3分鐘未連接設(shè)備則會自動休眠。使用nrf connect這個APP連接開發(fā)板如下:

          main


          main函數(shù)里面包含所有功能的初始化。我們詳細(xì)看一下和藍(lán)牙有關(guān)的初始化。

          timers_init


          因為藍(lán)牙協(xié)議棧使用到了app定時器,所以app定時器初始化是必須的,即使你沒有使用到,使用的方法官方已經(jīng)給出,如上圖紅框。

          buttons_leds_init


          這個函數(shù)第一初始化了LED和按鍵,第二把藍(lán)牙事件與LED和按鍵綁定起來。
          LED和按鍵初始化中,按鍵初始化如下:

          按鍵的設(shè)計思路是:通過定時器去實現(xiàn)按鍵按下、按鍵釋放、按鍵長按三種情況判斷,每種動作情況可以配置相同或者不同的事件類型,根據(jù)事件類型去處理不同情況。
          bsp_init里還有LED的初始化:

          可以看到在這里創(chuàng)建了一個有關(guān)LED的定時器,這個定時器里通過判斷當(dāng)前BSP的事件狀態(tài)去改變LED狀態(tài):

          bsp_led_indication這個函數(shù)因為處理的狀態(tài)比較多所以很長,這里我們只看用到的:


          按鍵初始狀態(tài)事件綁定在函數(shù)bsp_btn_ble_init里:

          因為有兩種喚醒方式(刪除綁定和不刪除),所以需要在程序喚醒后判斷是用哪一個按鍵喚醒的,從而決定要不要刪除所有綁定信息。

          而函數(shù)?advertising_buttons_configure里則更改了按鍵動作觸發(fā)的事件:

          函數(shù)bsp_event_to_button_action_assign主要用來把事件對應(yīng)到哪個按鍵完成什么動作觸發(fā)這個綁定:

          這個函數(shù)上面還有一個差不多的連接狀態(tài)下的按鍵配置函數(shù)connection_buttons_configure

          這個函數(shù)在何處被調(diào)用?首先在bsp_btn_ble.c中注冊了一個觀察者:

          它的回調(diào)函數(shù)中處理了連接和斷開兩種事件:

          也就是連接后用connection_buttons_configure配置按鍵,斷開后用advertising_buttons_configure配置按鍵。關(guān)于觀察者詳細(xì)查看下一節(jié)。

          ble_stack_init


          函數(shù)nrf_sdh_enable_request內(nèi)容如下:

          注意這里用到的通知函數(shù)有兩種:
          sdh_request_observer_notify
          sdh_state_observer_notify
          如何通知所有觀察者?如把類型為request的信息通知給所有觀察者:

          通知?state?類型如下:

          這兩個段被注冊在nrf_sdh.c內(nèi):

          用來定義段的宏定義為?NRF_SECTION_SET_DEF

          關(guān)于宏和段的使用,可以參考我之前剖析RT-Thread那篇自動初始化部分
          剖析RT-Thread中console與finsh組件實現(xiàn)(1)
          什么是觀察者?
          詳細(xì)解析參考下面博文:
          BLE事件回調(diào)機制解析

          還有在這個函數(shù)里我們需要特別注意函數(shù)?nrf_sdh_ble_enable?函數(shù)內(nèi)容:

          由上一章我們知道了其實所有數(shù)據(jù)在服務(wù)端都以attribute呈現(xiàn),就像古代的藥店,有一面墻都是抽屜,每個抽屜的標(biāo)簽寫了藥材的名字,里面則放的相應(yīng)的藥材,類比一下,每個attribute就是一個抽屜,attribute的handle就像藥材的名字,attribute的內(nèi)容就像藥材本身,而attribute的權(quán)限就像藥性??蛻舳司拖衽渌幍拇蠓?,需要什么藥得根據(jù)名字去找,然后根據(jù)藥材的藥性去決定取的量。所有的attribute組成一個表叫attribute table如下:

          所以每當(dāng)你添加服務(wù)或者特征,都會增大attribute table的大小,而這個attribute table是直接分配在協(xié)議棧所用ram里的,這里需要提一下,協(xié)議棧其實就是一段代碼,也需要運行空間,nrf的做法就像用OS創(chuàng)建了兩個任務(wù),一個任務(wù)專門跑協(xié)議棧,一個任務(wù)跑你的應(yīng)用代碼,我們知道創(chuàng)建任務(wù)是需要分配任務(wù)空間的,attribute table就占用協(xié)議棧的任務(wù)空間大小。因為協(xié)議棧的ram是從芯片ram起始地址開始的,所以我們只需要設(shè)置應(yīng)用程序的ram從哪開始、有多大就可以了,這樣前面的就全部分配給協(xié)議棧。所以回到前面,當(dāng)attribute table變大了,就需要把應(yīng)用程序的起始ram往后推,應(yīng)用程序ram大小當(dāng)然也要減去增加的,因為芯片ram總大小是一定的。
          所以如果當(dāng)你代碼沒有問題,但是程序跑不起來或者藍(lán)牙服務(wù)異常,有可能就是你的attribute table太大了導(dǎo)致數(shù)據(jù)溢出,如果只影響到協(xié)議棧,那你的藍(lán)牙服務(wù)就會崩潰,如果嚴(yán)重到影響到應(yīng)用代碼,你的整個程序都會崩潰。但是你并不能直接去修改應(yīng)用ram起始和大小,而是應(yīng)該先去增加attribute table的大?。?br style="box-sizing: border-box;">
          然后上面函數(shù)里的打印會告訴你應(yīng)該把起始地址和大小調(diào)整為多少。調(diào)整方法:


          修改格式IDE已經(jīng)給出,仿照上圖紅框,修改參數(shù)填入到下圖即可。

          比如我的這段代碼LOG:

          我只需要修改:

          最后在此函數(shù)中還初始化了一個觀察者:

          該宏NRF_SDH_BLE_OBSERVER如下:

          sdh_ble_observers段定義在nrf_sdh_ble.c內(nèi):

          如果我們后續(xù)添加自己的服務(wù)也應(yīng)該注冊在該段內(nèi)。該觀察者事件回調(diào)為:

          可以看到連接成功后,會觸發(fā)一次LED狀態(tài)指示函數(shù),也就是常亮LED1。

          peer_manager_init


          有關(guān)配對與綁定,其實主要是為了安全,詳細(xì)解析推薦博文:
          低功耗藍(lán)牙配對綁定解讀和實踐
          除了注意配對的一些參數(shù),需要注意配對事件的回調(diào)函數(shù)。

          在協(xié)議棧收到一些配對有關(guān)信息,它會處理好分為不同事件類型上報給應(yīng)用層,方法就是觸發(fā)事件回調(diào)。上圖回調(diào)函數(shù)里并沒有處理任何事件,實際我們寫為:

          上圖只處理了一種情況?PM_EVT_PEERS_DELETE_SUCCEEDED?如果你需要處理其他情況,直接添加不同的case即可:

          gap_params_init


          這個函數(shù)里可以修改加密模式、設(shè)備名稱、還有一些GAP連接參數(shù)。中間注釋掉的那段代碼是添加設(shè)備外觀的,如果添加的是SIG所制定的一些標(biāo)準(zhǔn)profile是有設(shè)備外觀的,而自定義的則無。

          gatt_init


          可以看到gatt初始化很簡單,實際在自己的項目中基本會提供一個GATT的事件回調(diào)函數(shù),如:


          可以看到就兩種事件,一種是ATT的MTU大小更新,另一種是數(shù)據(jù)長度更新。

          advertising_init


          這個函數(shù)是設(shè)置廣播相關(guān)內(nèi)容的,也就是在廣播階段你想要展示的內(nèi)容都在這里設(shè)置。最開始手機截圖可以看到?jīng)]有連接的時候也有顯示180A這個服務(wù)。

          services_init

          這個函數(shù)里就是我們要實現(xiàn)的功能,如果我們要添加自己的服務(wù),就在下圖紅框內(nèi)的服務(wù)初始化函數(shù)內(nèi)添加,如果是多個服務(wù),你可以添加多個服務(wù)初始化函數(shù)。


          可以看到上圖就是一個服務(wù)的模板文件,實際開發(fā)時,我們最好把每個服務(wù)放在單獨的.c文件里,這里服務(wù)初始化是空的,也就是沒有我們自己的服務(wù)。

          conn_params_init


          這個函數(shù)主要設(shè)置了連接參數(shù)協(xié)商參數(shù),因為連接參數(shù)是由連接發(fā)起方設(shè)置的,我們作為被連接方,如果想要改變連接參數(shù),只能通過協(xié)商的方式,即告訴連接方我們想如何連接,但是連接方可以選擇采納也可以拒絕。

          advertising_start


          可以看到廣播一開始就以快速廣播模式開始廣播,這個函數(shù)里面比較復(fù)雜,我們不過多關(guān)注,只需要注意一個地方:

          假如你前面沒有設(shè)置快速廣播的相關(guān)參數(shù),實際開啟的廣播可能會變成其他廣播模式。

          這里case中沒有break,也就是返回值是最近能得到的廣播模式。

          變量定義


          還有一些需要使用到的全局變量都定義在main.c最開始的位置。分別是與隊列模塊相關(guān)的、GATT模塊相關(guān)的、與廣播模塊相關(guān)的、保存當(dāng)前連接句柄的全局變量。

          這里看一下廣播的結(jié)構(gòu)體變量,因為它和廣播如果3分鐘沒有設(shè)備連接就自動休眠有關(guān)。
          先看一下廣播結(jié)構(gòu)體變量初始化:

          這里很重要的宏是?NRF_SDH_BLE_OBSERVER,它用來注冊觀察者,上面在藍(lán)牙協(xié)議棧初始化已經(jīng)講過。我們看一下廣播的事件回調(diào)ble_advertising_on_ble_evt

          連接成功和連接斷開不做展開,但是廣播超時后廣播就立馬停止了嗎?

          可以看到無論哪種超時原因,最后的結(jié)果都是繼續(xù)廣播,函數(shù)adv_mode_next_get會返回下個廣播模式,但是因為我們只配置了快速廣播模式一種,所以ble_advertising_start這個啟動廣播的函數(shù)里面
          在判斷所選廣播模式是否配置過會判斷不過,最終函數(shù)adv_mode_next_avail_get只能返回BLE_ADV_MODE_IDLE

          最終這個狀態(tài)會在廣播事件回調(diào)on_adv_evt里被觸發(fā)

          on_adv_evt被調(diào)用的位置在函數(shù)ble_advertising_start結(jié)尾:

          而在休眠函數(shù)sleep_mode_enter里設(shè)置如下:

          免責(zé)聲明:本文素材來源網(wǎng)絡(luò),版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請與我聯(lián)系刪除。

          ???????????????? ?END ????????????????

          關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。


          點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久久三级片AV | 就操在线观看免费视频 | 日韩爱爱网站 | 色丁香六月 | 黄A色V在线观看 |