概述
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/technicaldocuments#doctype5
TI CC2530 Demo板的资料:http://www.ti.com/tool/cc2530emk,下载官方提供的例子文件
打开例子文件夹中idesrf05_cc2530iar文件夹下的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继电器模块,则需要进行电平转换最后
以上就是落后棒球为你收集整理的zigbee组网_Zigbee-2-组网实验-1-经典的无线点灯的全部内容,希望文章能够帮你解决zigbee组网_Zigbee-2-组网实验-1-经典的无线点灯所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复