【第 2 题】 远程灯光自动控制

  • Post author:
  • Post category:其他




题目

找到 2 块 ZigBee 模块(蓝色),模拟员工餐厅区的远程灯光自动控制效果,考核选手点对点通讯知识。在“竞赛资料\任务 3\”中提供的工程代码中添加相应代码,实现如下功能:

  • 参赛选手设置信道为 24,按组号设置 PANID 为 0x8000+0x 组号,如组号为 4,则PANID 为 0x8004 ;
  • 两块 ZigBee 模块板程序运行时,LED1 亮、LED2 灭
  • 光照节点模块实时采集光照值,当光照小于某一给定值时(用手遮住),能够控制另一个继电器节点模块 LED2 灯亮、;当光照足够时(手放开),控制 LED2 灯灭。


    补充说明:
  • 参赛选手打开该题中的工程文件进行编程,参赛选手可以直接在上面进行二次开发。
  • 参考文档有竞赛函数说明文档供选手参考使用。
  • 将这 2 块 ZigBee 节点盒的小辣椒上贴上题号(如:任务三第 1 题则写题 1), 并安装到对应区域,接上电源,待裁判评判。



解题思路

复制一遍官方的Zigbee通用库, 里面有 CC2530_lib、Project文件夹。

  1. CC2530_lib里面有很多官方已经写好的API直接拿来用就可以了,比自己用寄存器写要简单。
  2. 如果会Z-Stack栈那么CC2530注入灵魂,人机合一。但是Z-Stack栈学习成本高。
  3. 所以官方给了一个没有OSAL系统的库CC2530_lib。
  4. 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 */
}
Created with Raphaël 2.2.0 while(1){ basicRfPacketIsReady()? uint8 basicRfReceive(uint8 *pRxData, uint16 len, int16 *pRssi); 根据命令簇执行相关动作 }//伪代码 yes no



无线发送相关函数



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);	// 串口输出



一些技巧

在一个工程中编译主机和从机

  1. appMode = MASTER; 代码分支
  2. project -> Edit Conifgrations…

代码深入

右击->Go to define of 函数名()

右击->Open Header/Source File

API 都在头文件里!

开源客栈日暮,入栈不知归路。debug无尽头,误入代码深处,单步单步已是代码最最深处,发现bug无数!

写个作业真不容易,



版权声明:本文为weixin_42613840原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。