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

對專案進行編譯,出現如下錯誤:

Zigbee-2-組網實驗-1-經典的無線點燈

對Options中的Linker進行修改後編譯正常

Zigbee-2-組網實驗-1-經典的無線點燈

該例程的結構如下:

Zigbee-2-組網實驗-1-經典的無線點燈

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

);

}

Zigbee-2-組網實驗-1-經典的無線點燈

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

();

}

}

}

Zigbee-2-組網實驗-1-經典的無線點燈

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的狀態

}

}

}

}

Zigbee-2-組網實驗-1-經典的無線點燈

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次也能收到資料,兩次資料包並沒有什麼不同

Zigbee-2-組網實驗-1-經典的無線點燈

後來,找到問題,只需要註釋掉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繼電器模組,則需要進行電平轉換

Zigbee-2-組網實驗-1-經典的無線點燈