本系列教程將結合TI推出的CC254x SoC 系列,講解從環境的搭建到藍牙4.0協議棧的開發來深入學習藍牙4.0的開發過程。教程共分為六部分,本文為第五部分:
第五部分知識點:
第二十一節 DHT11溫濕度傳感器
第二十二節 藍牙協議棧之從機通訊
第二十三節 藍牙協議棧主從一體之主機通訊
第二十四節 OAD空中升級
第二十五節 SBL串口升級
?
有關TI 的CC254x芯片介紹,可點擊下面鏈接查看:
有關本文的工具下載,大家可以到以下這個地址:
朱兆祺ForARM第二十一節 DHT11溫濕度傳感器
DHT11簡介
DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器,它應用專用的數字模塊采集技術和溫濕度傳感技術,確保產品具有極高的可靠 性和卓越的長期穩定性。傳感器包括一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機相連接。因此該產品具有品質卓越、超快響應、抗干擾 能力強、性價比極高等優點。每個DHT11傳感器都在極為精確的濕度校驗室中進行校準。校準系數以程序的形式存在OTP內存中,傳感器內部在檢測型號的處 理過程中要調用這些校準系數。單線制串行接口,使系統集成變得簡易快捷。超小的體積、極低的功耗,使其成為給類應用甚至最為苛刻的應用場合的最佳選擇。產 品為4針單排引腳封裝,連接方便。
技術參數
輸 出: 單總線數字信號
測量范圍: 濕度20-90%RH, 溫度0~50℃
測量精度: 濕度+-5%RH, 溫度+-2℃
分 辨 率: 濕度1%RH,溫度1℃
互 換 性: 可完全互換 ,
長期穩定性: < ±1%RH/年
DHT11 數字濕溫度傳感器采用單總線數據格式。即,單個數據引腳端口完成輸入輸出雙向傳輸。其數據包由 5Byte(40Bit)組成。數據分小數部分和整數部分,一次完整的數據傳輸為40bit,高位先出。DHT11 的數據格式為:8bit 濕度整數數據+8bit 濕度小數數據+8bit 溫度整數數據+8bit 溫度小數數據+8bit 校驗和。其中校驗和數據為前四個字節相加。傳感器數據輸出的是未編碼的二進制數據。數據(濕度、溫度、整數、小數)之間應該分開處理。例如,某次從 DHT11 讀到的數據如圖所示:
協議棧DHT11測試
打開DHT11Example工程,我們在啟動事件中對DHT11進行初始化。如果初始化失敗則說明沒有接傳感器。
然后在定時事件中定時的讀取溫濕度的值。并將結果通過UART顯示到PC端。
從其中可以看到當前的溫度為29攝氏度,濕度為30%,往傳感器器哈一口氣可以看到溫濕度都上升了。
第二十二節 藍牙協議棧之從機通訊
之前都是外圍模塊的驅動程序,這一節開始,我們進入藍牙4.0協議棧的核心部分,從機通訊的程序設計。接下來的章節是藍牙4.0協議棧最為核心的程序設計部分。
前面的大都是外圍器件的實驗,這節我們介紹藍牙通訊中從機的角色,從機的主要工作是對外廣播,接受主機的連接,并且接受主機發送過來的數據。這里介紹兩個函數:
bStatus_t GAPRole_SetParameter( uint16 param, uint8 len, void *pValue );
這個函數主要是用來配置從機的一些參數,第一個參數表示需要配置哪個參數,例如我們需要時能從機廣播,則需要這樣調用:
uint8 initial_advertising_enable = TRUE;
GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
第二個函數是特征值改變時的回調函數,當主機給從機發送數據時,從機就會回調這個函數來告知應用層有數據送達。
static void simpleProfileChangeCB( uint8 paramID );
在低功耗藍牙中,數據的傳輸是通過特征值的讀寫來實現的。
BLE協議棧的GATT層用于應用程序在兩個連接設備之間的數據通信的。從GATT層的角度看,當設備連接后,將充當一下兩種角色中的一個:
GATT Client —— 從GATT服務器讀/寫數據的設備。
GATT Server —— 包含客戶端需要讀/寫的數據的設備。
重要的是要注意,GATTClient和Server 的角色完全獨立于BLE的鏈路層的 slave和master的角色,或GAP層peripheral和central的角色。一個slave 可以是GATT Client或GATT Server,一個master同樣可以是GATT Client或GATT Server。 一個GATT Server可以有多個完成一個特定的功能或特性GATT Server組成。
在SimpleBLEPeripheral應用程序中有三個GATT服務:
Mandatory GAP Service:這個服務包含設備和訪問信息,比如設備名稱、供應商和產品標識。
Mandatory GATT Service :這個服務包含有關服務UUID相關信息。
SimpleGATTProfile Service——這個服務是一個示例配置文件,供測試和演示。
Profile簡介
為了更容易的保持Bluetooth 設備之間的兼容,Bluetooth規范中定義了 Profile。Profile 定義了設備如何實現一種連接或者應用,你可以把 Profile 理解為連接層或者應用層協議。Bluetooth 的一個很重要特性,就是所有的 Bluetooth 產品都無須實現全部的 Bluetooth 規范,你可根據所需要的產品實現需要的Profile,不必給開發帶來更大的開銷。這就是說當需要利用藍牙提供數據傳輸功能時就必須建立對應的 Profile,TI的BLE協議棧為我們提供了部分Profile,其中一部分是非標準的Profile。其中非標準的有 SimpleGATTProfile和SimpleKeysProfile,我們將通過對這兩個Profile的介紹及實驗來了解Profile的特性和 使用。每個 Profile 初始化其響應的服務和內部寄存器。GATT 服務器將整個服務加到屬性表中,并為每個屬性分配唯一的句柄。 GATTProfile用于存儲和處理GATT服務器中的數據。在下面的實驗中需要用到的都是我們自己新建的Profile,即非標準的Profile。 其中主要要注意Profile、UUID、handle、CharacteristicValues。
SimpleGATTProfile及Btool的使用
SimpleGATTProfile中包含5個特征值,每一個的屬性都不同:
SimpleGATTProfile 特征值屬性:
Btool是PC端工具,使用特定的HCI命令與CC2540通信,PC端需要通過串口或 USB 連接 CC2540,CC2540 使用 HostTestRelease 工程,硬件可以使用 USBDongle(對應CC2540USB)或我們提供的USBDongle。
USBDongle連接從機
使用饅頭科技有限公司的USBDongle,燒寫HostTestRelease固件,連接電腦后就可以用Btool軟件來連接從機設備。
將從機工程編譯下載到開發板,連接串口到PC端,我們通過串口來觀察設備的運行,運行后可以看到設備處于廣播。
這是我們插入USBDongle到電腦,可以看到識別到一個串口插入,如圖,這就是USBDongle用CDC的方式實現的串口。
打開Btool,按左圖配置,可以看到右圖的信息,這是說明Btool已經識別到了USBDongle。
Btool的界面可以分為4個區:
1. 設備信息展示
2. 歷史記錄
3. 設備控制
4. 連接信息
確保周圍存在設備可發現,點擊Discover/Connect標簽的scan按鈕,CC2540 就會進行10s的掃描過程,在這期間可通過Cancle按鈕停止掃描。
可以看到,我們周邊有兩個設備,其中一個就是我們的開發板,根據串口輸出的信息我們知道我們設備的地址是0X7C669D9F6297,下面我們點擊establish來連接我們的開發板。
連接后可以看到兩邊都同時顯示了連接信息。
開發板輸出連接:
Btool連接的設備信息:
特征值的讀寫
接下來我們用Btool對SimpleProfile 進行使用操作。剛剛我們已經列出了SimpleProfile中的各個特征值。
使用UUID讀取特征值,CHAR1具有讀寫屬性,這里對 SimpleProfile 的第一特征值 CHAR1進行讀取操作,UUID 為0xfff1。選擇 Read/Write 選項頁并選擇 ReadUsing Characteristic UUID 功能,在Characteristic UUID選項填入f1:ff(高字節在前),點擊Read按鈕。
讀取特征值成功:
下面對此特征值進行寫入操作,寫入操作必須使用Handle值進行,而無法使用UUID來操作,那CHAR1的Handle值的什么呢?其實剛剛在我們讀取CHAR1的值的時候就已經獲取到了它的Handle。如圖,CHAR1的Handle為0x0025。
CHAR1的Handle值:
下面我們通過這個Handle對CHAR1寫入十進制的10,如圖,我們寫入成功了。
寫入成功:
在SimpleBLEPeripheral設備的串口輸出中可以看到設備提示CHAR1的值變為了10。
下面來驗證我們是否成功的將CHAR1改為了10,按照剛剛讀取CHAR1的步驟,重新讀取CHAR1的值。
CHAR1的值改為了10:
藍牙點燈
上面我們已經能夠成功的改寫一個特征值,那我們是不是可以通過發送特定的值來控制一個燈的亮滅呢?答案是肯定的。下面我們來實現這個功能。
從機工程已經有5個特征值了,我們現在增加一個特征值來控制燈的亮滅。那我們該如何來添加特征值呢?特征值的管理是在profile中實現的。所以我們需要對profile進行修改。
(1)修改simpleGATTProfile.h
在simpleGATTProfile.h中可以看到現在定義的5個特征值的標示符和UUID,我們添加一個1Byte的特征值來控制燈的亮滅。
因為simpleGATTProfile是共用的文件,為了不影響其它工程,我們使用一個宏來控制新增加的屬性。
接下來我們需要修改simpleGATTProfile.c,這個文件需要修改的地方較多,下面我們一步一步來修改。
(2)添加UUID
(3)添加屬性
(4)屬性表
(5)屬性設置操作
(6)屬性獲取操作
(7)屬性讀操作
(8)屬性寫操作
Profile的改造完成后,我們將這個宏打開,配置工程。
接著我們在staticvoid simpleProfileChangeCB( uint8 paramID )函數的switch中加入CHAR6的判斷即可。
編譯燒錄后,按照我們前面說的在Btool中對FFF6的UUID進行讀寫操作即可實現對LED的控制。
第二十三節 藍牙協議棧之主機通訊
隨著藍牙4.0模塊的大量使用,為了很多從未接觸過藍牙的工程師也能快速便捷地開發藍牙項目或者使用藍牙,主從一體、遠控IO等等特性也成為藍牙模塊必 備的條件。其實,聯合第二十一節和本節(第二十二節),我們就能將一個本無固件的裸片藍牙,使其開發為具備主從一體功能的藍牙模塊。這兩節的內容,也是本 連載篇的重點部分之一。
上一節我們對從機的工作流程有了一個整體的把握。我們現在接著來看主機的工作流程。
主機的工作主要是掃描設備,對發現的設備發起連接,然后就是對特征值的讀寫操作了。
手動連接
從機的對外廣播是在初始化的時候完成的,那主機的掃描是在哪里開始的呢?閱讀源碼可以發現主機的操作都在按鍵處理中完成的。主機通過五向按鍵中的五個按鍵實現不同的功能。
static void simpleBLECentral_HandleKeys( uint8 shift, uint8 keys )
{
(void)shift; // Intentionally unreferenced parameter
if ( keys & HAL_KEY_UP ) // 向上
{
// Start or stop discovery
if ( simpleBLEState != BLE_STATE_CONNECTED ) // 如果沒有連接,開始掃描
{
if ( !simpleBLEScanning )
{
simpleBLEScanning = TRUE;
simpleBLEScanRes = 0;
LCD_WRITE_STRING( “Discovering.。。”, HAL_LCD_LINE_1 );
LCD_WRITE_STRING( “”, HAL_LCD_LINE_2 );
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST );
}
else
{
GAPCentralRole_CancelDiscovery();
}
}
else if ( simpleBLEState == BLE_STATE_CONNECTED && // 如果連接并且發現Handle進行讀寫操作
simpleBLECharHdl != 0 &&
simpleBLEProcedureInProgress == FALSE )
{
uint8 status;
// Do a read or write as long as no other read or write is in progress
if ( simpleBLEDoWrite )
{
// Do a write
attWriteReq_t req;
req.handle = simpleBLECharHdl;
req.len = 1;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
else
{
// Do a read
attReadReq_t req;
req.handle = simpleBLECharHdl;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
if ( status == SUCCESS )
{
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
}
}
if ( keys & HAL_KEY_LEFT ) // 左
{
// Display discovery results
if ( !simpleBLEScanning && simpleBLEScanRes > 0 ) // 顯示掃描到的設備
{
// Increment index of current result (with wraparound)
simpleBLEScanIdx++;
if ( simpleBLEScanIdx >= simpleBLEScanRes )
{
simpleBLEScanIdx = 0;
}
LCD_WRITE_STRING_VALUE( “Device”, simpleBLEScanIdx + 1,
10, HAL_LCD_LINE_1 );
LCD_WRITE_STRING( bdAddr2Str( simpleBLEDevList[simpleBLEScanIdx].addr ),
HAL_LCD_LINE_2 );
}
}
if ( keys & HAL_KEY_RIGHT ) // 右
{
// Connection update
if ( simpleBLEState == BLE_STATE_CONNECTED ) // 如果連接,則更新連接
{
GAPCentralRole_UpdateLink( simpleBLEConnHandle,
DEFAULT_UPDATE_MIN_CONN_INTERVAL,
DEFAULT_UPDATE_MAX_CONN_INTERVAL,
DEFAULT_UPDATE_SLAVE_LATENCY,
DEFAULT_UPDATE_CONN_TIMEOUT );
}
}
if ( keys & HAL_KEY_CENTER ) // 中間鍵
{
uint8 addrType;
uint8 *peerAddr;
// Connect or disconnect
if ( simpleBLEState == BLE_STATE_IDLE ) // 空閑則連接
{
// if there is a scan result
if ( simpleBLEScanRes > 0 )
{
// connect to current device in scan result
peerAddr = simpleBLEDevList[simpleBLEScanIdx].addr;
addrType = simpleBLEDevList[simpleBLEScanIdx].addrType;
simpleBLEState = BLE_STATE_CONNECTING;
GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE,
DEFAULT_LINK_WHITE_LIST,
addrType, peerAddr );
LCD_WRITE_STRING( “Connecting”, HAL_LCD_LINE_1 );
LCD_WRITE_STRING( bdAddr2Str( peerAddr ), HAL_LCD_LINE_2 );
}
}
else if ( simpleBLEState == BLE_STATE_CONNECTING || // 連接則斷開連接
simpleBLEState == BLE_STATE_CONNECTED )
{
// disconnect
simpleBLEState = BLE_STATE_DISCONNECTING;
gStatus = GAPCentralRole_TerminateLink( simpleBLEConnHandle );
LCD_WRITE_STRING( “Disconnecting”, HAL_LCD_LINE_1 );
}
}
if ( keys & HAL_KEY_DOWN ) // 下
{
// Start or cancel RSSI polling
if ( simpleBLEState == BLE_STATE_CONNECTED ) // 連接則讀取RSSi的值
{
if ( !simpleBLERssi )
{
simpleBLERssi = TRUE;
GAPCentralRole_StartRssi( simpleBLEConnHandle, DEFAULT_RSSI_PERIOD );
}
else
{
simpleBLERssi = FALSE;
GAPCentralRole_CancelRssi( simpleBLEConnHandle );
LCD_WRITE_STRING( “RSSI Cancelled”, HAL_LCD_LINE_1 );
}
}
}
}
因為從機一直處于廣播狀態,所以秩序將上一節中的從機程序燒錄進開發板即可,然后將主機程序燒錄到另外一快開發板,通過五向按鍵來實現和從機的連接和讀寫功能。
(1) 上電提示
從機上電提示:
主機上電提示:
(2)根據主機的按鍵功能,我們按“UP”鍵,開始搜索周邊設備。搜索完成后,可以看到,掃描到了一個設備。
(3)接著我們查看掃描到的設備地址,按左鍵。可以看到掃描到的設備地址為0x7C669D9F638A。這個地址正是我們的從機地址。
(4)按中間鍵連接從機,可以看到主機提示連接成功,從機也提示連接成功。
(5)接著我們開始讀取從機的RSSI值,按下鍵。
(6)再次按下鍵,取消RSSI值的讀取。
(7)對從機的CHAR1進行讀寫,再次按上鍵讀取到CHAR1的值為1。
(8)接著按上鍵,對CHAR1寫入0,同時看到從機提示CHAR1的值被修改為0。
主機寫入成功:
從機提示CHAR1被改變:
上電自動連接
上一節中我們通過五向按鍵實現了主機連接從機的功能,這一節中們來實現主機上電后自動搜索連接從機。
要實現連接,從機必須處于廣播狀態,剩下的工作全部由主機完成,掃描、發起連接。
主機的狀態也有回調函數,主機啟動后,第一個狀態是初始化,所以我們在初始化完成時開始掃描,
這樣開機后主機就會開始掃描周邊設備,接下來我們在掃描完成后對掃描到的設備發起連接。
將工程編譯下載后通過串口助手觀察主機和從機的輸出可以發現主機上電后自動的完成了一系列的操作。
第二十四節 OAD空中升級
通過仿真器更新程序或者通過USB更新固件那都是一般人都可以實現的操作,但是要想實現OAD空中升級,這還是需要一定的技術能力。這一節我就帶著大家完善這一能力。
OADn air download,指空中下載模式。當我們的產品發布以后,有時需要對固件進行升級,OAD是升級方式中的一種。
配置BIM(Boot Image Manger)
打開IAR,打開BLE-CC254x-1.4.0下的工程C:TexasInstrumentsBLE-CC254x-1.4.0ProjectsleutilBIMcc254xBIM.eww,然后編譯,下載到開發板中。
配置Image A
打開OADExample工程,配置工程,添加如下幾個宏
FEATURE_OAD_BIM
HAL_IMAGE_A
FEATURE_OAD
OAD_KEEP_NV_PAGES
(1) 打開simplePeripheral.c找到宏定義DEFAULT_ENABLE_UPDATE_REQUEST,將其改為FLASE。否則會影響BLEDevice Monitor對其進行空中升級,到時可以再改回來。
(2) IAR左側導航中找到Profile文件夾,點擊右鍵添加oad_target.c和oad_target.h兩個文件,二文件位于 C:TexasInstrumentsBLE- CC254x-1.4.0ProjectsleProfilesOAD中。
(3) 在IAR導航中找到 HAL→Target→MT254xboard→Driver,右鍵添加文件hal_crc.c,該文件位于C:TexasInstruments BLE-CC254x-1.4.0Componentshal argetMT254xboard中。
(4) 在剛才的simplePeripheral.c文件中,找到simplePeripheral_Init()函數,在里面添加OADTarget_AddService()函數。
(5) 在simplePeripheral.c的前面,添加引用OAD的頭文件,OAD.h和OAD_target.h。
(6) 點擊Project→Option…,或直接按Alt+F7,選擇BuildActions,在Post-buildcommand line中添加:
“$PROJ_DIR$。。。。commoncc2540cc254x_ubl_pp.bat”“$PROJ_DIR$” “ rodUBL”“$PROJ_DIR$CC2540-OAD-ImgAExeOADExample”
注意,各雙引號之間只有一個空格。注意圖中紅框標的部分,Image_A是和之前第1步對應的。
(7) 點擊Project→Option…,或直接按Alt+F7,選擇Linker,選擇Config,Linker Configurationfile中勾選Override default,添加C:TexasInstrumentsBLE-CC254x-1.4.0Projectslecommon cc2540cc254x_f256_imgA.xcl。
(8) 點擊Project→Option…,或直接按Alt+F7,選擇Linker,選擇Extra Output。
(9) Extra Option添加Hex文件的輸出。
(10) 點擊OK,編譯下載,如果出現如下錯誤,是因為我們使用的是IAR8051 8.30版本,如果你使用的是IAR8.20版本就沒有這問題。
(11) 這個問題是因為使用了虛擬寄存器導致的,我們找到如下文件。
(12) 對文件的115行進行如下修改,將虛擬寄存器注釋掉。
(13) 編譯后,可以看到我們生成的文件
(14) 我們將hex文件疊加到BIM后面
這樣ImageA就成功燒錄進開發板了。
配置Image B
(1) 方法如Image A,其它要注意的幾個地方:點擊Project→Option…,或直接按Alt+F7,選擇C/C++Compiler,選擇Preprocessor,將Defined symbols中的HAL_IMAGE_A改成B。
(2) 點擊Project→Option…,或直接按Alt+F7,選擇Linker,選擇Config,將Linker Configuration file中的文件改為B。
(3) 保存后編譯,同樣的可以看到生成的文件。
(4) 為了區分A和B鏡像,我們將最終結果改名。
空中升級
有了bin文件就可以進行空中升級了,打開BLE Device Monitor(沒有安裝的需要安裝),打開后軟件會自動掃描設備,如圖,我們掃描到了開發板。
(2) 連接后,打開OAD
(3) 點擊file,選擇Progame(OAD),選擇生成的bin文件,可以看到我們當前運行的是A固件
(4) 點擊start,當進行到100%,就完成了固件空中升級。
(5) 升級后再次打開OAD選項,可以看到選擇運行的固件已經是B版本的了。
注意:
要把 BLE協議棧BLE-CC254x-1.4.0 安裝在C盤,在其他盤符下沒有生成bin文件。
第二十五節 SBL串口升級
SBL升級和OAD升級的配置步驟都是一樣的,主要是配置的參數不一樣,下面我們來配置一個SBL升級的固件。
配置SBL
打開IAR,打開BLE-CC254x-1.4.0下的工程C:TexasInstrumentsBLE-CC254x-1.4.0ProjectsleutilSBLiarcc254xsbl.eww,然后編譯,下載到開發板中。
配置Bin文件
(1) 添加宏
MAKE_CRC_SHDW
FEATURE_SBL
OAD_KEEP_NV_PAGES
(2) 添加build選項
“$PROJ_DIR$。。。。commoncc2540cc254x_ubl_pp.bat”“$PROJ_DIR$” “ProdUBL” “$PROJ_DIR$MT254xboardExeSBLExample”
(3) Config選項
(4) Extra Output選項
(5) 保存編譯
(6) 打開串口升級軟件SerialBootTool.exe,選擇SBLExample.bin文件。
(7) Load Image
這樣我們的SBL固件就制作完畢了。
評論