认识CC2530
1.硬件介绍
本次Zigbee技术的学习硬件采用的是江苏学蠡信息科技有限公司的无线传感器网络实验平台中的Zigbee传感节点模块。
对于液晶底板的介绍就不多说了,可以看一下BLE学习的章节。这边主要对TI最新一代的Zigbee芯片CC2530进行介绍。
CC2530是TI公司推出的一款支持基于 IEEE802.15.4 的 ZIGBEE2007/PRO 协议的2.4GHzZigbee应用的片上解决方案。CC2530结合了领先的RF接收器的优良性能以及业界标准的增强型8051芯片,有着功耗低、成本小、性能优秀等特点。
CC2530的引脚图如下所示:
2. 软件介绍
本次Zigbee的学习所使用到的IDE是IAR Embedded Workbench For 8051 8.10版本,系统为Windows 10中文专业版。关于IAR这个IDE的安装和使用在CSDN和百度上有非常多详细的教程,这里就不多赘述了,主要目的为告知一下开发环境。
3.CC2530基础使用
CC2530作为Zigbee无线通信SOC的前提是一个8051芯片,这就意味着CC2530和我们所熟悉的51单片机是有着血缘关系的,因此CC2530也可以实现基本单片机的功能,而IO输入输出控制就是对一个单片机最基本认识的开始。
3.1 IO输入输出控制
CC2530的通用IO一共有3个端口,分别是端口0、端口1和端口2。端口0和端口1都是完整的8位端口,而端口2只有5位可用。所有的端口均可以通过 SFR 寄存器 P0、P1 和 P2 位寻址和字
节寻址,每个端口引脚都可以单独设置为通用 I/O 或外部设备 I/O。CC2530的通用IO口并不多,但作为一个Zigbee无线传感节点来说足够了。接下来就通过一个实验来讲述CC2530IO口的输入输出控制。
需要实现的现象:通过液晶底板上的按钮控制红的闪烁和关闭
实验目的:完成CC2530IO输入输出控制的理解和使用
实验相关硬件原理图:
五向按键原理图:
LED原理图:
程序流程图:
本实现的实现过程也就是程序执行的流程,先进行简单的概括,具体的实现步骤参考下方代码块。
-
初始化相关GPIO
根据原理图确定控制LED的引脚号为多少同时对IO是用作输入还是输出进行设置,在本实验中LED作为一个输出设备并且其使用的是P10和P11两个IO口,因此先将P10和P11设置为输出模式并对灯的初始状态进行设置。从原理图来看,LED被置高电平关闭。而按键作为输入设备,其所对应的P06IO口就需要被设置为输入模式。
实现代码:void Initial(void) { P1DIR |= 0x03; //P10、P11定义为输出 RLED = 1; YLED = 1; //LED } void InitKey(void) { P0SEL &= ~0X40; P0INP |= 0x40; //上拉 P0DIR &= ~(0x01<<(6)); //按键在P06 ADC采集 }
-
按键扫描
既然是要通过按键对LED状态进行控制,那程序中主要的就是对按键进行扫描,判断按键是否被按下。从原理图中可以看到如果五向按键向下按,P06口将会直接接地所以传输到IO口的就是一个低电平,如此只要按键返回的值为0就可以判断按键被按下当然也要注意消除抖动。uchar KeyScan(void) { if(K1 == 0) //低电平有效 { Delay(100); //检测到按键 if(K1 == 0) { while(!K1); //直到松开按键 return(1); } } return(0); }
-
主程序循环
接下来就是一直执行按键扫描,一旦按键向下按下就控制红色LED灯状态改变。Keyvalue = KeyScan(); //扫键 if(Keyvalue>0) { if(Keyvalue == 1) GlintFlag[0] = !GlintFlag[0]; }; if(GlintFlag[0]==1) { YLED = !YLED; //闪灯 Delay(4000); } else YLED = OFF; //关灯
3.2 定时器控制
CC2530一共有4个定时器,其中定时器1为16位独立定时器,定时器3/4位8位定时器。在这里主要介绍CC2530定时器1,定时器1一共有三种操作模式,分别是自由运行模式、模模式以及正计时/倒计时模式。
-
自由运行模式
在该模式下,计数器从0x0000开始,每个活动时钟边沿加1,一直加到0xFFFF后溢出重新载入0x0000继续循环下去。当计数器加到0xFFFF会设置IRCON.T1IF 和T1STAT.OVFIF,如果设置了相应的中断屏蔽位 TIMIF.OVFIM 以及 IEN1.T1EN,将产生一个中断请求。
-
模模式
与自由运行模式类似,计数器都是从0x0000开始,每个活动时钟边沿加1。但在模模式中溢出值可以由开发人员设置,设置寄存器 T1CC0H:T1CC0L 保存的最终计数值T1CC0。如果定时器开始于T1CC0 以上的一个值,当达到最终计数值(0xFFFF)时,设置标志 IRCON.T1IF 和 T1CTL.OVFIF。如果设置了相应的中断屏蔽位 TIMIF.OVFIM 以及 IEN1.T1EN,将产生一个中断请求。
-
正计时/倒计时模式
在正计数/倒计数模式,计数器反复从 0x0000 开始,正计数直到达到 T1CC0H:T1CC0L 保存的值。然后计
数器将倒计数直到 0x0000,。这个定时器用于周期必须是对称输出脉冲而不是 0xFFFF 的应用程序,因此允许中心对齐的 PWM 输出应用的实现。在正计数/倒计数模式,当达到最终计数值时,设置标志IRCON.T1IF 和 T1CTL.OVFIF。如果设置了相应的中断屏蔽位 TIMIF.OVFIM 以及 IEN1.T1EN,将产生一个中断请求。
实验目的:使用定时器1改变LED小灯状态,实现T1 每溢出两次,两个小灯闪烁一次, 并且在停止闪烁后成闪
烁前相反的状态。
实验原理图:LED图如上方IO操作实验中原理图一致
程序流程图:
具体实现:
初始化GPIO和T1:
GPIO的初始化就不再多说,设置为输出模式并输出高电平熄灭LED灯。
T1的初始化操作需要查看T1CTL(T1控制状态寄存器),按照说明设置对应参数,在本实验中需要设置的是T1工作模式为:自由运行模式,采用128分频,并设置定时器1溢出中断标志以及通道0的中断标志位。
T1CTL表如下:
因此初始化部分程序如下:void Initial(void) { //初始化P1 P1DIR = 0x03; //P10 P11为输出 RLED = 1; YLED = 1; //灭LED //用T1来做实验 T1CTL = 0x3d; //通道0,中断有效,128分频;自动重装模式(0x0000->0xffff); }
在主循环中要做的就是判断IRCON是否被置1,置1就控制LED闪烁,当IRCON被清0后LED停止闪烁并实现亮灯状态翻转。因此main函数的程序如下:
void main() { Initial(); //调用初始化函数 RLED = 0; //点亮红色LED while(1) //查询溢出 { if(IRCON > 0) { IRCON = 0; //清溢出标志 TempFlag = !TempFlag; } if(TempFlag) { YLED = RLED; RLED = !RLED; Delay(6000); } } }
3.3 中断输入
在上一小节的定时器中其实就已经用到了中断,中断即人为设置一个断点,当通过一系列操作达到该断点触发的条件(如:定时器达到一个固定值或采用外部设备对IO口输入一个电平)就去执行另外一段程序,执行完毕后重新回到主函数中继续运行主函数中下面的程序。
在本小节主要介绍外部输入中断,即采用液晶传感器面板上的按钮用作中断触发来控制LED灯状态的改变。
实验目的:通过液晶面板上的五向按键控制LED亮灭
实验原理图:同3.1小节原理图
实验程序流程图:
具体实现:
-
GPIO初始化
在本次实验中,对GPIO的初始化操作主要包括设置LED灯对应IO口为输出并对LED初始状态进行设置,同时对P0和P1口上拉下拉进行对应配置。最后对五向按键中间按下的对应IO口设置为中断模式,下降沿触发并开启中断设置P0中断状态标志。void Init_IO_AND_LED(void) { P1DIR = 0X03; //设置LED控制IO为输出 RLED = 1; led2 = 1; P0INP &= ~0X0c; //配置P0口上拉下拉情况 P1INP &= ~0X40; //配置P1口上拉下拉情况 P0IEN |= 0X40; //P06为中断模式 PICTL |= 0X02; //下降沿 EA = 1; IEN1 |= 0X20; // P0IE = 1;开启P0口中断 P0IFG |= 0x00; };
-
中断程序
因为中断产生后会自动执行中断函数中的语句,在这主函数就直接写一个while(1)进入死循环即可,在中断函数中要做的就是当P0IFG>0的时候就先将P0IFG置0,同时将LED状态进行反转。在中断函数的最后将P0IF中断标志重新软件置0。#pragma vector = P1INT_VECTOR __interrupt void P1_ISR(void) { if(P0IFG>0) //按键中断 { P0IFG = 0; RLED = !RLED; } P0IF = 0; //清中断标志 }
3.3 ADC采集和串口
CC2530的ADC 支持多达 14 位的模拟数字转换,具有多达 12 位的 ENOB(有效数字位)。它包括一个模拟多路转换器,具有多达 8 个各自可配置的通道;以及一个参考电压发生器。转换结果通过 DMA 写入存储器。
实验目的:本实验通过ADC将片内的温度传感器数值读取出来并通过串口发送到串口助手中显示
实验原理图:
实验程序流程图:
具体实现:
-
UART串口初始化
对于串口的初始化主要是对晶振、P0端口、串口的工作方式、波特率以及接受中断的相关配置。在本实验中,P0用作串口,串口的波特率为57600并允许接收
相关程序:void initUARTtest(void) { CLKCONCMD &= ~0x40; //晶振 while(!(SLEEPSTA & 0x40)); //等待晶振稳定 CLKCONCMD &= ~0x47; //TICHSPD128 分频,CLKSPD 不分频 SLEEPCMD |= 0x04; //关闭不用的 RC 振荡器 PERCFG = 0x00; //位置 1 P0 口 P0SEL = 0x3c; //P0 用作串口 U0CSR |= 0x80; //UART 方式 U0GCR |= 10; //baud_e = 10; U0BAUD |= 216; //波特率设为 57600 UTX0IF = 1; U0CSR |= 0X40; //允许接收 IEN0 |= 0x84; //开总中断,接收中断 }
-
配置片内温度传感器的AD采集
在本实验中将系统时钟设为晶振,设 AD 目标为单片机温度传感器。void initTempSensor(void){ DISABLE_ALL_INTERRUPTS(); SET_MAIN_CLOCK_SOURCE(0); *((BYTE __xdata*) 0xDF26) = 0x80; }
-
读取温度数值
在本实验中对于片内的温度传感器数据进行4次AD采集,对4次采集结果取平均值后再将AD值转化为十进制数值温度。
温度读取相关程序:INT8 getTemperature(void){ UINT8 i; UINT16 accValue; UINT16 value; accValue = 0; for( i = 0; i < 4; i++ ) { ADC_SINGLE_CONVERSION(ADC_REF_1_25_V | ADC_14_BIT | ADC_TEMP_SENS); ADC_SAMPLE_SINGLE(); while(!ADC_SAMPLE_READY()); value = ADCL >> 2; value |= (((UINT16)ADCH) << 6); accValue += value; } value = accValue >> 2; // devide by 4 return ADC14_TO_CELSIUS(value); }
数值转换程序:
#define ADC14_TO_CELSIUS(ADC_VALUE) ( ((ADC_VALUE) >> 4) - 315)
-
主函数
在主函数中将读取温度数值函数返回的值再做一次取均值操作,将最终的均值结果通过sprintf()转为字符串后通过UartTX_Send_String发送出去。
具体实现:void UartTX_Send_String(char *Data,int len) { int j; for(j=0;j<len;j++) { U0DBUF = *Data++; while(UTX0IF == 0); UTX0IF = 0; } }
void main(void) { char i; char temperature[10]; INT16 avgTemp; initUARTtest(); //初始化串口 initTempSensor(); //初始化ADC while(1) { avgTemp = 0; for(i = 0 ; i < 64 ; i++) { avgTemp += getTemperature(); } avgTemp /= 64; sprintf(temperature, (char *)"%dC", (INT8)avgTemp); UartTX_Send_String(temperature,4); UartTX_Send_word(0x0A); Delay(20000); } }