Zigbee-2-組網實驗-1-經典的無線點燈
Zigbee協議棧就是將各個層定義的協議集合在一起,以函式的形式實現,並給使用者提供API進行呼叫。
這裡使用TI提供的協議棧Z-Stack 2。5。1a。它基於一個小型的作業系統OSAL,採用任務輪詢的方式進行事件的管理
在開發中不用太關心Zigbee協議的具體實現細節,主要要關心:應用層資料使用哪些函式透過什麼方式把資料傳送出去或把資料接收過來的,要學會使用Zigbee協議棧。
要開始學習協議棧相關的內容了,需要準備的東西:
至少2塊Zigbee實驗板,如果要組網時,至少需要4塊(協調器、路由器、2終端)
USB Dongle:用於進行抓包,以便深入理解Zigbee協議。
Packet Sinffer:配置Dongle硬體進行抓包的軟體工具
Z-sensor monitor:顯示無線網路拓撲結構
IAR Embedded Workbench for 8051 10。20。1。根據官方提供的例子可以下載舊版本使用,新版本會遇到一些小問題。
2.1 無線點燈
這是入門Zigbee的一個經典例子,這裡還用不到協議棧,這裡主要是學習資料的無線傳送和無線接收。需要準備兩塊Zigbee實驗板。
一些相關的名詞:
HAL(Hardware Abstract Layer):硬體抽象層
PNA(Personal Area Network):個人區域網,簡稱個域網
RF(Radio Frequency):射頻
RSSI(Received Signal Strength Indicator):接收訊號強度指示
實現內容:模組A發射,模組B接收,按下模組A的按鍵S1,點亮模組B上的LED燈。
CC2530資料:
http://www。
ti。com/product/CC2530/t
echnicaldocuments#doctype5
TI CC2530 Demo板的資料:
http://www。
ti。com/tool/cc2530emk
,下載官方提供的例子檔案
開啟例子資料夾中ide\srf05_cc2530\iar資料夾下的
light_switch.eww
對專案進行編譯,出現如下錯誤:
對Options中的Linker進行修改後編譯正常
該例程的結構如下:
Hardware Layer:是實現資料傳輸的基礎
HAL:提供一種介面來訪問GPIO、UART、ADC等,這些介面都透過相應的函式實現
Basic RF Layer:為雙向無線傳輸提供一種簡單的協議
Application Layer:使用者應用層,透過他,我們就可以 使用封裝好的Basic RF和HAL的函式
Basic RF提供了安全通訊所使用的CCM-64身份驗證和資料加密,可以在IAR中進行設定:Options——>C/C++Compiler的Preprocessor中透過定義SECURITY_CCM來實現,這裡不需要加密,所以設定為xSECURITY_CCM,前面的x表示不定義SECURITY_CCM,即不使用加密
Basic RF的工作過程:啟動、發射、接收
2。1。1 啟動
建立一個basicRfCfg_t結構體
typedef
struct
{
uint16
myAddr
;
//16位短地址,即節點的地址
uint16
panId
;
//節點的PAN ID(個域網ID,表明在哪個網路中)
uint8
channel
;
//RF通道,必須在11-26之間
uint8
ackRequest
;
//目標確認時為TRUE
#ifdef SECURITY_CCM
//是否加密,在預定義中取消了加密(C++ Compiler的preprocessor中寫了xSECURITY_CCM)
uint8
*
securityKey
;
uint8
*
securityNonce
;
#endif
}
basicRfCfg_t
;
呼叫basicRfInit進行協議的初始化:設定結構體中的這些資訊,主要是短地址、PAN ID、RF通道 uint8 basicRfInit(basicRfCfg_t* pRfConfig);
2。1。2 傳送
建立一個buffer,把payload(淨資料)放進去,最大為103位元組
呼叫basicRfSendPacket()傳送,並檢視其返回值
uint8
basicRfSendPacket
(
uint16
destAddr
,
//目的短地址
uint8
*
pPayload
,
//指向傳送緩衝區的指標
uint8
length
);
//
傳送資料長度
作用是向目的短地址傳送指定長度的資料,成功時返回SUCCESS,失敗時返回FAILED
2。1。3 接收
上層透過basicRfPacketIsReady()檢測是否收到一個新資料包,如果準備好新資料包時返回TRUE
呼叫basicRfReceive()把收到的資料複製到緩衝區中。 uint8 basicRfReceive(uint8* pRxData, uint8 len, int16* pRssi); 函式的作用是接收來自Basic RF層的資料包,併為資料和RSSI分配緩衝區
2。1。4 測試程式碼
需要根據自己所使用的硬體,對程式進行修改,遮蔽掉與LCD相關的內容
對於發射端呼叫appSwitch();對於接收端呼叫appLight();
2。1。4。1 主程式
void
main
(
void
)
{
uint8
appMode
=
NONE
;
//不設定模式
// Config basicRF初始化basicRfCfg_t結構體
basicRfConfig
。
panId
=
PAN_ID
;
//PAN ID
basicRfConfig
。
channel
=
RF_CHANNEL
;
//RF通道
basicRfConfig
。
ackRequest
=
TRUE
;
//目標確認寫TRUE
#ifdef SECURITY_CCM
basicRfConfig
。
securityKey
=
key
;
#endif
// Initalise board peripherals //外圍硬體初始化
halBoardInit
();
halJoystickInit
();
// Initalise hal_rf //HAL層RF初始化
if
(
halRfInit
()
==
FAILED
)
{
HAL_ASSERT
(
FALSE
);
}
// Indicate that device is powered
//halLedSet(1);
//根據實際實驗板,關LED1和LED3
halLedClear
(
1
);
halLedClear
(
3
);
//如果是發射端呼叫appSwitch並下載到電路板中
appSwitch
();
//如果是接收端呼叫appLight並下載到電路板中
appLight
();
// Role is undefined。 This code should not be reached
HAL_ASSERT
(
FALSE
);
}
2。1。4。2 發射端按鍵appSwitch()
static
void
appSwitch
()
{
pTxData
[
0
]
=
LIGHT_TOGGLE_CMD
;
//定義了一個燈狀態切換的命令,並存到待發送緩衝區中
// Initialize BasicRF初始化basicRF
basicRfConfig
。
myAddr
=
SWITCH_ADDR
;
if
(
basicRfInit
(
&
basicRfConfig
)
==
FAILED
)
{
HAL_ASSERT
(
FALSE
);
}
// Keep Receiver off when not needed to save power不需要時關閉接收以省電
basicRfReceiveOff
();
//因為是發射模組,不接收,所以關閉接收省電
// Main loop
while
(
TRUE
)
{
if
(
halButtonPushed
()
==
HAL_BUTTON_1
){
//如果按下S1
//if( halJoystickPushed() ) {
basicRfSendPacket
(
LIGHT_ADDR
,
pTxData
,
APP_PAYLOAD_LENGTH
);
//傳送資料
//實際是傳送了1個位元組的資料到0xBEEF地址,這恰好是燈的地址
// basicRfSendPacket(0xBEEF,pTxData[0] , 1);
// Put MCU to sleep。 It will wake up on joystick interrupt
halIntOff
();
halMcuSetLowPowerMode
(
HAL_MCU_LPM_3
);
// Will turn on global 這個函式是空的
// interrupt enable使能中斷
halIntOn
();
}
}
}
2。1。4。3 接收端LED appLight()
static
void
appLight
()
{
// Initialize BasicRF初始化basicRF
basicRfConfig
。
myAddr
=
LIGHT_ADDR
;
if
(
basicRfInit
(
&
basicRfConfig
)
==
FAILED
)
{
HAL_ASSERT
(
FALSE
);
}
basicRfReceiveOn
();
// Main loop
while
(
TRUE
)
{
while
(
!
basicRfPacketIsReady
());
//如果上層的資料包到來
if
(
basicRfReceive
(
pRxData
,
APP_PAYLOAD_LENGTH
,
NULL
)
>
0
)
{
//如果接收到資料
if
(
pRxData
[
0
]
==
LIGHT_TOGGLE_CMD
)
{
halLedToggle
(
1
);
//改變LED的狀態
}
}
}
}
2.1.4.4 硬體連線
halboard。h中對硬體的引腳進行了定義,這要根據自己的開發板進行修改
我這裡使用的板子的連線情況是:
LED1——-P10 LED3——-P14 S1——P01這裡只使用了LED1和S1
// LEDs LED1——P10
#define HAL_BOARD_IO_LED_1_PORT 1
// Green
#define HAL_BOARD_IO_LED_1_PIN 0
// Buttons
#define HAL_BOARD_IO_BTN_1_PORT 0
// Button S1
#define HAL_BOARD_IO_BTN_1_PIN 1
關於資料的接收:
basic_rf。c中打開了RF接收中斷,所以接收端可以接收到射頻訊號後接收資料
halRfRxInterruptConfig(basicRfRxFrmDoneIsr);
basicRfRxFrmDoneIsr是接收到RF資料時的中斷服務程式
halRfInit中有使能RF接收中斷: halRfEnableRxInterrupt();
在main中呼叫了halRfInit(): if(halRfInit()==FAILED) { HAL_ASSERT(FALSE); }
發現問題:按第1次S1時LED1燈點亮,但再次按時LED1並不會熄滅,但appLight中使用的是halLedToggle(1);應該是燈的狀態切換。
使用的RF通道為25,採用usb dongle抓包,發現接收端在第2次按S1次也能收到資料,兩次資料包並沒有什麼不同
後來,找到問題,只需要註釋掉main中的//halJoystickInit();即可,因為這裡並沒有使用JoyStick,應該是Joystick中使用的中斷與按鍵有衝突所致。
只需註釋掉halJoystickInit(),並重新燒寫發射端,則一切OK。
為了除錯此問題,在appSwitch和appLight的相應位置用閃燈次數表示程式執行到了什麼地方。比如:
while
(
!
basicRfPacketIsReady
());
//如果上層的資料包到來
if
(
basicRfReceive
(
pRxData
,
APP_PAYLOAD_LENGTH
,
NULL
)
>
0
)
{
//如果接收到資料
for
(
i
=
0
;
i
<
3
;
i
++
)
{
halLedToggle
(
3
);
halMcuWaitMs
(
1000
);
}
這裡除錯還了花了挺長時間,所以,以後若使用的板子上資源未使用到,與TI的不同,則最好遮蔽掉相應的程式碼,以免不必要的麻煩。
如果實際中,需要點亮一個真正的燈,在此基礎上,只需將接LED的端子P10接一個繼電器模組的控制端,繼電器輸出與燈和電源相連即可點亮實際的電燈。由於CC2530的電平為3。3V,所以需要3。3V的繼電器模組,若是採用了5V繼電器模組,則需要進行電平轉換。
如果實際中,需要點亮一個真正的燈,在此基礎上,只需將接LED的端子P10接一個繼電器模組的控制端,繼電器輸出與燈和電源相連即可點亮實際的電燈。由於CC2530的電平為3。3V,所以需要3。3V的繼電器模組,若是採用了5V繼電器模組,則需要進行電平轉換