题目
找到 2 块 ZigBee 模块(蓝色),模拟员工餐厅区的远程灯光自动控制效果,考核选手点对点通讯知识。在“竞赛资料\任务 3\”中提供的工程代码中添加相应代码,实现如下功能:
- 参赛选手设置信道为 24,按组号设置 PANID 为 0x8000+0x 组号,如组号为 4,则PANID 为 0x8004 ;
- 两块 ZigBee 模块板程序运行时,LED1 亮、LED2 灭
-
光照节点模块实时采集光照值,当光照小于某一给定值时(用手遮住),能够控制另一个继电器节点模块 LED2 灯亮、;当光照足够时(手放开),控制 LED2 灯灭。
补充说明:
- 参赛选手打开该题中的工程文件进行编程,参赛选手可以直接在上面进行二次开发。
- 参考文档有竞赛函数说明文档供选手参考使用。
- 将这 2 块 ZigBee 节点盒的小辣椒上贴上题号(如:任务三第 1 题则写题 1), 并安装到对应区域,接上电源,待裁判评判。
解题思路
复制一遍官方的Zigbee通用库, 里面有 CC2530_lib、Project文件夹。
- CC2530_lib里面有很多官方已经写好的API直接拿来用就可以了,比自己用寄存器写要简单。
- 如果会Z-Stack栈那么CC2530注入灵魂,人机合一。但是Z-Stack栈学习成本高。
- 所以官方给了一个没有OSAL系统的库CC2530_lib。
- Project里面的工程是直接就配置好的。
工程如下:
简单介绍一下左边的这些文件夹
- app 应用层(用户自己写的代码在这里)
- basicrf 基本无线通信层(点对点API在这里)
- board 硬件资源(串口波特率设置,LED,中断等)
- common 通用代码(主要有一个hal_uart.c)
- sensor_drv 传感器API在这里
- utils 工具API
- Output 输出的文件
右边点对点通信的地址设置
- RF_CHANNEL 信道
- PAN_ID 个人局域网ID
- MY_ADDR 本机地址
- SEND_ADDR 目标地址
函数介绍
这里只介绍该题用到的头文件(API).
重点:basicrf.h
无线配置相关函数
basicRfCfg_t数据结构的定义。
typedef struct
{
uint16 myAddr; // 本机地址
uint16 panId; // 网络ID
uint8 channel; // 通信信道, 11~26
uint8 ackRequest; //应答信号
#ifdef SECURITY_CCM // 是否加密,预定义时取消了加密
uint8 *securityKey;
uint8 *securityNonce;
#endif
} basicRfCfg_t;
相关代码:
static basicRfCfg_t basicRfConfig;
/*****点对点通讯地址设置******/
#define RF_CHANNEL 24 // 频道 11~26
#define PAN_ID 0x8004 //网络id
#define MY_ADDR 0x2019 //本机模块地址
#define SEND_ADDR 0XBEED //发送地址
uint8 basicRfInit(basicRfCfg_t *pRfConfig);
简述:
初始化点对点数据结构。在芯片中设置通道、短地址和PAN ID,并在数据包接收时配置中断。
参数:
pRfConfig – basicRfCfg_t结构体作为参数传递。
在CC2530_lib中rf_set.c的ConfigRf_Init 函数中被调用。
返回值:
设置成功(真),设置失败(假)
// 无线RF初始化
void ConfigRf_Init(void)
{
basicRfConfig.panId = PAN_ID;
basicRfConfig.channel = RF_CHANNEL;
basicRfConfig.myAddr = MY_ADDR;
basicRfConfig.ackRequest = TRUE;
while(basicRfInit(&basicRfConfig) == FAILED); // 初始化点对点数据结构
basicRfReceiveOn(); // 开启接收中断
}
void basicRfReceiveOn(void);
简述
:开启无线接收中断。(每次都要开启)
void basicRfReceiveOff(void);
简述
:关闭无线接收中断。(关闭之后不再接收)
无线接收相关函数
uint8 basicRfPacketIsReady(void);
简述:
检查新数据包是否准备好供下一个更高层读取.
返回值:
有数据包(真),没有数据包(假)
uint8 basicRfReceive(uint8 *pRxData, uint16 len, int16 *pRssi);
简述:
将最后一个传入数据包的负载复制到缓冲区中。
参数:
- pRxData 指向要填充的数据缓冲区的指针,此缓冲区必须由更高的层分配。在APP层自己声明一个数组传递进去供无线数据接收缓存。
- len – 要读入缓冲区的字节数
- rxi – 保存最后一个传入数据包信息的文件作用域变量(暂时不用调用时赋值为NULL)
相关代码:
#define APP_PAYLOAD_LEN 1 // 应用无线负载长度
#define RELAY_SET_CMD 1 // 继电器命令簇(命令集)
#define RELAY_CLEAR_CMD 0 // 主机(Master)要和从机(Slave)一致
static uint8 pTxData[APP_PAYLOAD_LEN]; // 无线发送缓存
static uint8 pRxData[APP_PAYLOAD_LEN]; // 无线接收缓存
while(1)
{
/* slave code start */
if( basicRfPacketIsReady() )
{
basicRfReceive(pRxData, APP_PAYLOAD_LEN, NULL);
if( pRxData[0] == RELAY_SET_CMD )
{
// 这两个函数在common文件夹下的hal_cc8051.h文件下
MCU_IO_OUTPUT(2, 0, 1); // 设置P2_0输出高电平继电器动作
MCU_IO_SET_HIGH_PREP(1, 0); // 设置P1_0输出高电平点亮LED2
}
else if( pRxData[0] == RELAY_CLEAR_CMD )
{
MCU_IO_OUTPUT(2, 0, 0); // 输出低电平继电器复位
MCU_IO_SET_LOW_PREP(1, 0); // LED2熄灭
}
else
{}
}
/* slave code end */
}
无线发送相关函数
uint8 basicRfSendPacket(uint16 destAddr, uint8 *pPayload, uint8 length);
简述:
无线发包
参数:
- destAddr – 目标地址
- pPayload – 信息负载(要发送的数组)
- length – 负载长度(要发送数组的长度)
返回值:
basicRFStatus_t – SUCCESS or FAILED(一般不用)
相关代码:
uint16 lightness = 0;
while(1)
{
/* user code start */
if( appMode == MASTER ) // 主机代码
{
lightness = get_guangdian_ad(); // 获取光照值该函数在sensor.h中
if( lightness < 20 ) // 如果光照值小于20
{
pTxData[0] = RELAY_SET_CMD; // 继电器动作命令
basicRfSendPacket(SEND_ADDR, pTxData, APP_PAYLOAD_LEN); // 发包
}
else
{
pTxData[0] = RELAY_CLEAR_CMD; // 继电器复位
basicRfSendPacket(SEND_ADDR, pTxData, APP_PAYLOAD_LEN);
}
printf_str((char *)uTxData,"光照值%d",lightness); // sprintf()
halUartWrite(uTxData,strlen((char *)uTxData)); // 在hal_uart.c文件中,串口输出
}
本题所有代码
#include "hal_defs.h"
#include "hal_cc8051.h"
#include "hal_int.h"
#include "hal_mcu.h"
#include "hal_board.h"
#include "hal_led.h"
#include "hal_rf.h"
#include "basic_rf.h"
#include "hal_uart.h"
#include "sensor_drv/sensor.h"
#include "TIMER.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "UART_PRINT.h"
/*****点对点通讯地址设置******/
#define RF_CHANNEL 24 // 频道 11~26
#define PAN_ID 0x8004 //网络id
#define MY_ADDR 0x2019 //本机模块地址
#define SEND_ADDR 0XBEED //发送地址
#define NONE 0
#define MASTER 1
#define SLAVE 2
#define APP_PAYLOAD_LEN 1 // 应用无线负载长度
#define RELAY_SET_CMD 1
#define RELAY_CLEAR_CMD 0
/**************************************************/
static basicRfCfg_t basicRfConfig;
static uint8 pTxData[APP_PAYLOAD_LEN]; // 无线发送缓存
static uint8 pRxData[APP_PAYLOAD_LEN]; // 无线接收缓存
uint8 appMode = SLAVE ; // 单片机角色 用于代码分支
uint8 uTxData[128]; // 串口缓冲
uint16 lightness = 0; // 光照值
// 无线RF初始化
void ConfigRf_Init(void)
{
basicRfConfig.panId = PAN_ID;
basicRfConfig.channel = RF_CHANNEL;
basicRfConfig.myAddr = MY_ADDR;
basicRfConfig.ackRequest = TRUE;
while(basicRfInit(&basicRfConfig) == FAILED); // 初始化点对点数据结构
basicRfReceiveOn(); // 开启接收中断
}
/********************MAIN************************/
void main(void)
{
// static uint8 relaySta = 0;
halBoardInit();//选手不得在此函数内添加代码???
ConfigRf_Init();//选手不得在此函数内添加代码???
// 设置LED的IO口
MCU_IO_OUTPUT(1, 1, 1);
MCU_IO_OUTPUT(1, 0, 0);
while(1)
{
/* user code start */
if( appMode == MASTER ) // 主机代码
{
lightness = get_guangdian_ad(); // 获取光照值该函数在sensor.h中
if( lightness < 20 ) // 如果光照值小于20
{
pTxData[0] = RELAY_SET_CMD; // 继电器动作命令
basicRfSendPacket(SEND_ADDR, pTxData, APP_PAYLOAD_LEN); // 发包
}
else
{
pTxData[0] = RELAY_CLEAR_CMD; // 继电器复位
basicRfSendPacket(SEND_ADDR, pTxData, APP_PAYLOAD_LEN);
}
printf_str((char *)uTxData,"光照值%d",lightness); // sprintf()
halUartWrite(uTxData,strlen((char *)uTxData)); // 在hal_uart.c文件中,串口输出
}
else if ( appMode == SLAVE )
{
if( basicRfPacketIsReady() )
{
basicRfReceive(pRxData, APP_PAYLOAD_LEN, NULL);
if( pRxData[0] == RELAY_SET_CMD )
{
MCU_IO_OUTPUT(2, 0, 1);
MCU_IO_SET_HIGH_PREP(1, 0);
}
else if( pRxData[0] == RELAY_CLEAR_CMD )
{
MCU_IO_OUTPUT(2, 0, 0);
MCU_IO_SET_LOW_PREP(1, 0);
}
else
{}
}
}
else
{}//nothing
/* user code end */
}
}
其他函数
void uart_printf(char *fmt,...); // 输出重定向,串口输出
void printf_str(char *buf, char *fmt,...); // sprintf
uint16 halUartWrite(uint8 *pBuffer, uint16 length); // 串口输出
一些技巧
在一个工程中编译主机和从机
- appMode = MASTER; 代码分支
- project -> Edit Conifgrations…
代码深入
右击->Go to define of 函数名()
右击->Open Header/Source File
API 都在头文件里!
开源客栈日暮,入栈不知归路。debug无尽头,误入代码深处,单步单步已是代码最最深处,发现bug无数!
写个作业真不容易,