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

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

    • 题目
    • 解题思路
    • 函数介绍
      • 重点:basicrf.h
        • 无线配置相关函数
          • basicRfCfg_t数据结构的定义。
          • uint8 basicRfInit(basicRfCfg_t *pRfConfig);
          • void basicRfReceiveOn(void);
          • void basicRfReceiveOff(void);
        • 无线接收相关函数
          • uint8 basicRfPacketIsReady(void);
          • uint8 basicRfReceive(uint8 *pRxData, uint16 len, int16 *pRssi);
        • 无线发送相关函数
          • uint8 basicRfSendPacket(uint16 destAddr, uint8 *pPayload, uint8 length);
    • 本题所有代码
      • 其他函数
      • 一些技巧

题目

找到 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里面的工程是直接就配置好的。

工程如下:
【第 2 题】 远程灯光自动控制_第1张图片

简单介绍一下左边的这些文件夹

  • 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 
#include 
#include 

#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无数!

写个作业真不容易,

你可能感兴趣的:(2019物联网大赛硬件编程)