前言
笔者在很久很久以前就想要写一些关于Zigbee的文章,用于帮助后来者学习相关的硬件知识,我知道,硬件的学习是需要长时间积累的,因为你会在调试或者实验过程中遇到各种问题......故而,若有人可以在你初学的时候帮助你用些生动形象的语句,理解晦涩难懂的专业名词,并给出较多的实例代码,同时将自己的经验之谈留下的话,对后来者的帮助一定是不小的!本文就将以cc2530这种单片机之间的有线数据传输为出发点,详细的阐述什么是M2M技术,以及cc2530单片机作用和使用方法,用以作为我们Zigbee系列文章起点!
文章受众:想要了解M2M技术、系统学习cc2530开发板和正在学习zigbee技术的同学
一、M2M技术的介绍
M2M技术(Machine-to-Machine技术),它是一种允许设备之间进行通信和交换数据的技术。我们对英文名字,逐字逐句的分析就会发现,M2M,其中的“2”指的的“to”,用最贴切的话语翻译过来“机器与机器之间的沟通”,这里的“沟通”指的是数据传输。需要注意的是,M2M技术通常涉及将传感器、设备和其他物联网(IoT)设备连接到互联网,以便它们能够自动进行数据交换和协作,而无需人的干预。这种通信可以是有线或无线的,旨在实现自动化、监测、控制和数据收集,以提高效率、减少成本和改善各种应用。总而言之,M2M技术在连接设备和系统之间提供了一种强大的方式,以实现自动化、监测和控制,从而改善各种领域的效率和生产力。这一领域在物联网(IoT)的快速发展中起到了关键作用,将继续在未来发挥重要作用。
M2M技术的一些关键特点和方面
-
自动化通信: M2M技术旨在通过设备之间的自动通信来实现自动化。设备可以自动发送和接收数据,触发操作,或执行任务,无需人的干预。这对许多领域都有应用,如工业自动化、智能家居、农业和交通管理。
-
数据采集和传输: M2M技术使设备能够采集各种数据,如温度、湿度、位置、状态等,然后将这些数据传输到中央服务器或其他设备进行进一步处理和分析。这有助于实时监测和数据驱动的决策。
-
应用领域: M2M技术应用广泛,包括智能家居、智能城市、工业自动化、医疗保健、物流和运输、农业等。它可以改善能源管理、远程监测、设备维护和更多方面的应用。
-
通信技术: M2M通信可以基于各种通信技术,包括无线技术如蜂窝网络(如2G、3G、4G、5G)、Wi-Fi、蓝牙、Zigbee,以及有线技术如以太网、电力线通信等。选择的通信技术取决于应用需求和环境。
-
数据安全: 由于涉及大量敏感数据,M2M技术需要强调数据安全性。这包括加密通信、身份验证、访问控制和数据隐私保护。
-
云集成: M2M通常与云计算集成,将数据上传到云端,以便远程访问和分析。这使得数据处理更加灵活,还可以进行远程监测和控制。
-
标准化: 有许多M2M标准和协议,如MQTT、CoAP、OPC UA等,以确保不同设备和系统之间的互操作性。
二、CC2530与Zigbee技术
CC2530是Texas Instruments生产的一款无线SoC(系统级芯片),主要用于物联网(IoT)和嵌入式无线通信应用。Zigbee是一种低功耗、短距离、自组织的无线通信协议,通常用于物联网应用。它具有可靠的通信、低能耗、自动组网和互操作性等特点,适合用于连接各种物联网设备。
![](/qrcode.jpg)
而CC2530通常用于Zigbee技术,是一款支持Zigbee通信协议的芯片。这也就意味着后续的相关文章,都将离不开这款芯片。开发人员可以使用CC2530来构建Zigbee终端设备、协调器(Coordinator)和路由器(Router),从而构建具有自组织和可扩展性的Zigbee网络。
CC2530芯片的介绍
-
芯片概述: CC2530是一款功能强大的射频系统级芯片,具有内置的802.15.4射频收发器和微控制器单元。它基于8051内核的微控制器,并集成了IEEE 802.15.4标准的无线通信协议堆栈,通常用于低功耗、短距离无线通信应用。
-
无线通信: CC2530支持IEEE 802.15.4标准,这是一种用于低功耗、低数据速率、短距离通信的协议。它适用于各种物联网应用,包括智能家居、工业自动化、传感器网络和监测系统。
-
微控制器: CC2530集成了一个强大的8051微控制器单元,用于处理应用程序和通信任务。它还具有丰富的外设,如UART、SPI、I2C、定时器和GPIO引脚,以满足不同应用的需求。
-
低功耗设计: CC2530的设计注重低功耗性能,这使其非常适合电池供电的设备。它可以在待机模式下极少消耗电能,并支持快速唤醒以减小能耗。
-
集成性: CC2530的集成度高,内置了射频前端、射频收发器、时钟和微控制器,从而降低了系统的成本和复杂性。此外,它提供了一些易于使用的开发工具和软件支持,帮助开发人员更轻松地设计和部署无线应用。
-
应用领域: CC2530广泛应用于各种物联网设备,包括智能家居控制器、传感器节点、医疗设备、工业传感器和无线控制系统。它可用于建立自组织的传感器网络,实现设备之间的数据交换和远程控制。
-
生态系统支持:Texas Instruments提供了广泛的文档、软件工具和开发套件,以支持开发人员在CC2530上构建应用程序。这些资源有助于简化开发流程和缩短产品上市时间。
Zigbee技术详细介绍
1、Zigbee概述
Zigbee是一种无线通信标准,旨在支持设备之间的短距离通信,通常在10到100米范围内。它使用低功耗通信,适用于需要长时间运行的电池供电设备。Zigbee标准由Zigbee联盟(Zigbee Alliance)管理,旨在促进物联网的发展。
2、特点和优势
- 低功耗: Zigbee设备通常非常省电,可以在电池供电下运行数月甚至数年。
- 短距离通信: Zigbee用于设备之间的短距离通信,适用于局部网络和家庭自动化应用。
- 自组织网络: Zigbee设备可以自动构建和管理自己的网络,无需中央控制器。这种自组织性有助于大规模的设备互连。
- 低数据速率: Zigbee通常用于传输小量的数据,如传感器读数和控制指令。
- 互操作性: Zigbee标准确保不同厂商的设备可以互相通信,促进了多厂商设备之间的互操作性。
- 安全性: Zigbee提供了数据加密和安全认证功能,以保护通信的安全性。
3、Zigbee协议栈(2007_ZStack-CC2530-2.5.1a)
- 物理层(PHY): 确定无线信号的传输特性,如频率、调制和功率。
- 介质访问控制层(MAC): 处理帧的传输、设备的休眠和唤醒,以及网络拓扑。
- 网络层: 管理设备之间的路由、地址分配和数据传输。
- 应用层: 定义应用特定的数据格式和通信协议。
注意:这里既然提到协议栈,我就先囫囵解释一下,具体的细节我们后续慢慢来。我们当前文章只是对zigbee技术进行介绍,并让我们先熟悉对其芯片cc2530的使用方法。其实我们也不难理解,zigbee的核心在于远距离的无线传输,而zigbee协议栈的作用:定义了设备之间的通信规则和协议,为设备之间的低功耗、自组织通信提供了一种结构化和规范的方式。
总而言之,这个协议栈的操作和使用,将会是我们学习zigbee技术的重中之重!我们后续的文章,将围绕着ZStack-CC2530-2.5.1a(2007版)这个协议栈来展开。(ZStack是Texas Instruments(TI)为其无线芯片(如CC2530)提供的Zigbee协议栈)
4. 应用领域
- 智能家居: 控制智能照明、智能恒温器、安全系统等。
- 工业自动化: 监控和控制工厂设备、传感器网络、无线监测等。
- 智能城市: 城市照明、停车管理、垃圾桶监测等。
- 医疗保健: 远程健康监测、医疗设备互连。
- 农业: 农业传感器网络、灌溉控制、农场监测。
三、CC2530间的有线通信
这里所提到的有线通信,就是M2M技术范围内里的有线数据传输,我们的目的是通过这个有线通信来熟悉cc2530的IO口、寄存器、USART等。那么我将从基础知识必知必会、个人经验的分享总结、相关实验的详细分析这三大模块来阐述!!
1、基础知识必知必会
(首先申明一下,咱默认读者已经具备一定的硬件知识,一些特别基础的内容我将不进行解释,例如,串口是什么?这些个问题,自行学习哈!!)
1)常见外观和芯片原理图展示
cc2530开发板常见外观
cc2530原理图
2)I/O口的寄存器说明
通用IO与外设IO的区别:通用I/O(GPIO)引脚是多用途、灵活配置的通用引脚,适用于各种任务,而外设I/O引脚是专门用于连接特定外部设备或执行特定任务的引脚,功能受硬件设计限制。
这里的通用I/O口,我们能够通过寄存器的配置,可以设置为输入接口或是输出接口,I/O引脚口的常用寄存器如下图所示
1、I/O设置寄存器
2、端口方向寄存器
3)基础概念总结(精力有限,后持续更新,大伙不理解的评论区留言。。。)
1、位寻址
通常指的是对二进制数据中的特定位进行检索和取值的操作。在计算机领域,位寻值通常用于从二进制数据中提取或检查某个位的值,以进行进一步的处理或决策。这个操作可以涉及到位操作(bitwise operations)和位掩码(bit masks)。位寻值在计算机编程和嵌入式系统中经常用于位级操作,如标志位的设置和清除,以及对寄存器或数据结构中的位进行操作。它是位操作的基本组成部分。总而言之,cc2530是支持直接对位操作的8051单片机。
简单的位寻值示例:
假设有一个8位二进制数:11011011
如果你想查找第4位(从右向左数,从0开始编号)的值,你可以使用位操作来执行位寻值,例如:
- 获取第4位的值:11011011 & 00001000
- 结果为0,表示第4位是0。
2、 DMA控制器
DMA控制器是一种硬件模块,用于在无需CPU干预的情况下,直接从内存中传输数据。DMA的主要目的是提高数据传输的效率,减轻CPU负担,允许CPU在数据传输时继续执行其他任务。通俗点的理解起来,DMA控制器就像是一个自动搬运工,它可以帮助你自动将数据从一个地方搬到另一个地方,而无需CPU亲自参与。它能够独立地将数据从内存传送到外设,或者从一个外设传送到另一个外设,而不需要CPU一直盯着。这样,CPU可以专注于执行其他重要的任务,而不必亲自处理每个数据传输请求。这提高了计算机系统的效率和性能。所以,DMA控制器的作用就是减轻CPU的负担,让数据传输更加高效。
2、个人经验的分享总结(持续更新。。。)
1、两台单片机之间的有线通信,是将机器A的RXD连接机器B的TXD,一个数据输出,一个数据输入,具体流程的如图所示:
2、在开始开发之前,仔细阅读CC2530的数据手册、用户指南和参考手册等文档(实验所需资料里有)。这些文档提供了有关芯片的详细信息,包括引脚定义、寄存器配置、通信协议等。
3、相关实验的详细分析
实验前期准备
相关硬件:无线传感器网络综合实验箱1台,如下图(理论上来说,所有的开发板都可以完成实验)
软件:IAR软件、串口助手
资料链接:实验所需资料
实验一:按键实验
实验原理:当I/O引脚用作I/0端口时,引脚可以组成3个8位口,定义为P0、P1以及P2,P0和P1为8位,P2为5位,共21个引脚,所有端口可以实现位寻址。所有端口引脚都可以通过SFR寄存器PxSEL以及PxDIR进行配置和初始化。
内容:实现按键控制灯LED2与LED3不同的闪烁效果,具体为:按键按下第几次则灯连续闪烁几次,接着再按下按键,如此往复(灯闪烁次数不超过5次)(电路图如下图所示)
实现代码:
#include<iocc2530.h>
#define key1 P1_1
#define led2 P0_1
#define led3 P0_4
void delay(unsigned int time)
{
unsigned char n;
while(time>0)
{
for(n=0;n<187;++n)
{
asm("nop");
}
time--;
}
}
void main()
{
P0SEL=~0X12;
P1SEL=~0X02;
P0DIR|=0X12;
P1DIR|=0X00;
int i=0;
led2=1;
led3=1;
int j;
while(1)
{
if(key1==0)
{
while(key1==0);
i++;
for(j=0;j<i;j++)
{
led2=0;
led3=0;
delay(500);//延时函数的调用
led2=1;
led3=1;
delay(500);
}
if(i==5)
{
i=0;
}
}
}
}
实验二:外部中断实验
实验原理:CC2530有18个中断源,每个中断源都有它自己的,位于一系列寄存器中的中断请求标志。每个中断可以分别使能或禁用。通过设置中断使能寄存器IEN0、IEN1或者IEN2的中断使能位使能或禁止。某些外部设备会因为若干中断事件产生中断请求。这些中断请求可以作用于P0端口、P1端口、P2端口、DMA、计数器或者RF上。对于每个内部中断源对应的特殊功能寄存器,这些外部设备都有中断屏蔽位。当中断发生时,无论该中断使能还是禁止,CPU都会在中断标志寄存器中设置中断标志位,在程序中可以通过中断标志位来判断是否发生了相应的中断,如果当设置中断标志时,那么在下一个指令周期,由硬件强行产生一个长调用指令LCALL到对于的向量地址,运行中断服务程序。中断的响应需要不同的时间,取决于中断发生时CPU的状态。当CPU正在运行的中断服务程序的优先级大于或等于新的中断时,新的中断暂不执行,直至新的中断的优先级高于正在运行的中断服务程序。
实验内容:启动底板使灯D2与D3全灭,并且数码管显示0,按下按键S3一次后,灯D2与D3为亮,数码管显示1;按键S3再按下一次,D2与D3全灭,数码管显示2;后面的逻辑以此类推,一直显示到9,再回到初始状态。要求LED与数码管状态的变化必须在外部中断函数里实现。(电路如图1、图2所示)
实现代码:
#include <ioCC2530.h>
#define LS164_DATA P1_3
#define LS164_CLK P1_2
#define UCHAR unsigned char
#define led1 P0_1
#define led2 P0_4
#define key P1_1
int count =1;
static UCHAR LED_Map[] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,
0x7d,0x07, 0x7f,0x6f,0x00};
void delay(){
for(int i=0; i<500; i++){
for(int j=0; j<300; j++){
}
}
}
void LS164_Cfg()//I/O引脚初始化
{
P1SEL &= ~0x0C;//xxxx 00xx
P1DIR |= 0x0C;//xxxx 11xx
}
void LS164_BYTE(UCHAR Index)//数码管显示字形
{ UCHAR i = 0;
UCHAR Data = LED_Map[Index];
for(;i<8;i++)
{
if(0x80 & Data)
{
LS164_DATA = 1; //传1位数据“1”
}
else
{
LS164_DATA = 0; //传1位数据“0”
}
Data = Data << 1;
LS164_CLK = 0;
LS164_CLK = 1; //时钟形成上升沿
}
}
#pragma vector = P1INT_VECTOR //端口P0的中断处理函数
__interrupt void P1_ISR(void)
{
if(P1IFG>0) //按键中断
{
led1=!led1;
led2=!led2;
delay();
LS164_BYTE(count++);
if(count+1==11){
count=0;}
P1IFG = 0; //清除P0_0中断标志
P1IF = 0; //清除P0中断标志
}
}
void main(){
P0SEL &= ~0X12;
P0DIR |= 0x12;
P1SEL &= ~0X0E;
P1DIR |= 0x0C;
P0INP |= 0x02; //上拉
P1IEN |= 0x02; //P01设置为中断方式
PICTL |= 0X02; //下降沿触发
EA = 1;
IEN2 |= 0X10;
P0IFG |= 0x00; //初始化中断标志位
key=1;
led1=1;
led2=1;
LS164_BYTE(0);
while(1){
}
}
实验三:串口通信实验
实验原理:CC2530有两个串口USART0和USART1,它们能够分别运行与异步模式(UART)或者同步模式(SPI)。当寄存器位UxCSR.MODE设置为1时,就选择UART模式,这里的x是USART的编号,其值为0或1。两个USART具有同样的功能,可以设置单独的I/O引脚,一旦确定下来,在进行程序设计时,需要按照硬件电路来设置USART的I/O引脚。寄存器位PERCFG.U0CFG选择是否使用位置1或者备用位置2。在UART模式中,可以使用双线连接方式(含有引脚RXD、TXD)或者四线连接方式(含有引脚RXD、TXD、RTS和CTS),其中RTS和CTS引脚用于硬件流量控制。UART模式提供全双工传送,接收器中的位同步不影响发送功能。传送一个UART字节包含一个起始位、8个数据位、一个作为可选项的第9位数据或者奇偶校验位再加上一个或两个停止位。
实验内容:实现单片机与PC机之间的串口通信。在PC机串口助手的发送窗口向CC2530发送字符(0~9),并控制两个LED闪烁的次数以及数码管显示的字形,如果输入其他字符,在串口助手中显示(error),要求串口接收要编写中断函数。(串口的电路图如下图所示)
实现代码:
#include <ioCC2530.h>
#include <string.h>
#define LS164_DATA P1_3
#define LS164_CLK P1_2
#define led2 P0_1
#define led3 P0_4
#define uchar unsigned char
#define uint unsigned int
uchar str1[6]="error\n";
uchar str2[10]="0123456789";
uchar temp;//中断标志位
static uchar LED_Map[] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x00};
//静态全局变量,分别输出的值是0、1、2、3、4、5、6、7、8、9、无显示
void delay(){
for(int m=0;m<500;m++){
for(int n=0;n<300;n++){
}}}
//数码管显示字形
void LS164_BYTE(uchar Index)//数码管显示字形
{
uchar i = 0;
uchar Data = LED_Map[Index];
for(;i<8;i++)
{
if(0x80 & Data)
{
LS164_DATA = 1; //传1位数据“1”
}
else
{
LS164_DATA = 0; //传1位数据“0”
}
Data = Data << 1;
LS164_CLK = 0;
LS164_CLK = 1; //时钟形成上升沿
}
}
//串口初始化
void InitUART0(){
CLKCONCMD &=~0x40;//设置系统时钟源为32MHZ晶振
while(CLKCONSTA & 0x40);//等待晶振
CLKCONCMD &=~0x47;//设置系统主时钟频率为32MHZ
PERCFG = 0x00;//位置1p0口
P0SEL = 0x3c;//P0做串口
P2DIR &= ~0xc0;//p0优先作为UART0
U0CSR |= 0x80;//串口设置为UNRT方式
U0GCR |= 9;
U0BAUD |= 59;//这两步用于设置波特率
UTX0IF = 1;//标记初始位置1
U0CSR |= 0x40;//允许接受
IEN0 |= 0x84;//开总中断,接受中断
}
//串口发送字符串函数 发送error
void UartTX_Send_String(uchar *Data,int len)
{
uint j;
for(j=0;j<len;j++)
{
U0DBUF=*Data++;
while(UTX0IF==0);//检测字符是否发送完毕
UTX0IF=0;
}
}
//灯光闪烁
void LED_flash(uchar temp){
int count = 0;
while(count<temp){
led2=!led2;
led3=!led3;
delay();
led2=!led2;
led3=!led3;
delay();
count++;
}
}
//接收中断 U0DBUF接受 UTX0IF发送
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void){
uint n, i;//用于调用数组中的值
URX0IF=0;
temp = U0DBUF;
UartTX_Send_String(temp,sizeof(temp));
/*
for(n=0;n<10;n++){
if(temp==str2[n]){
LS164_BYTE(n);
LED_flash(n);
break;}
else if(n>=0&&n<10) continue; //用于判断是否继续执行
else UartTX_Send_String(str1,6);}}
*/
for(n=0;n<10;n++){
if(temp==str2[n]){ i = 1; break;}
else i = 2;}
switch(i){
case 1: LS164_BYTE(n); LED_flash(n); delay();delay();delay();delay(); break;
case 2: UartTX_Send_String(str1,6); break;
default:break;
}
}
void Iint_IO()
{
//初始化P0端口
P0SEL&=0X12;//led2 led3 P0_1 p0_4为IO
P0DIR|=0X12;//方向是输出
P1SEL &= ~0x0c;//xxxx 00xx 配置为普通IO模式
P1DIR |= 0x0c;//xxxx 11xx 配置为输出模式
}
//主函数
void main(void)
{
Iint_IO();
InitUART0();//串口初始化
}
实验四:定时器1实验
实验原理:CC2530共有4个定时器T1、T2、T3、T4,定时器用于范围广泛的控制和测量应用。定时器1是一个16位的定时/计数器,支持输入采样、输出比较和PWM功能。定时器1包含主要的寄存器有T1CCxH(x的取值为0-4)、T1CCxL以及T1CTL;定时器1有三种操作模式,分别是自由运行模式(计数初始值固定为65535)、模模式(计数初始值为非65535)、正计数/倒计数(计数初始值为非65535);定时器1在每个活动边沿递增或递减。活动时钟边沿周期由寄存器CLKCONCMD.TICKSPD定义,它设置全局系统时钟的划分,提供了从0.25MHz到32MHz的不同的时钟标记频率(可以使用32MHzXOSC作为时钟源),另外定时器1可在T1CTL.DIV进行第二次分频,这个分频值可为1、8、32和128;当计数值达到溢出值的时候,计数器产生一个中断请求,进而可执行定时器1的中断函数。
实验内容
使用定时器1控制两个LED的闪烁间隔,假设亮750ms,灭250ms,并且实现数码管倒计时9秒(9~0),要求定时器1使用正计数/倒计数模式定时(需要编写中断函数,数码管的电路图如下图所示)。
实现代码:
#include<ioCC2530.h>
#include<string.h>
#define LS164_DATA P1_3
#define LS164_CLK P1_2
#define led1 P0_1
#define led2 P0_4
#define key P1_1
#define uchar unsigned char
#define uint unsigned int
uint counter=0;
int count = 9;
static uchar LED_Map[] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x00};
//静态全局变量,分别输出的值是0、1、2、3、4、5、6、7、8、9、无显示
//初始化函数声明
void Init_T1()
{
P1SEL &= ~0XC0;
P1DIR = 0XC0;
CLKCONCMD &= ~0X7f;//晶振设置为32HZ
while(CLKCONSTA & 0x40);//等待晶振稳定
EA=1;//打开中断
T1IE=1;//开T1溢出中断
T1CTL = 0x0F;//启动,设128分频,设正计数/倒计数模式定时模式
T1CC0H = 0x7A;
T1CC0L = 0x12;
//31250*128*(1/32MHZ)=0.125s (其中62500是——7A12(16进制数)化成的10进制数)
//故而由于正/倒计数,每周期0.25秒
led1 = 0;//led灯初始化
led2 = 0;//亮
//初始化P0端口
P0SEL&=0x12;//led2 led3 P0_1 p0_4为IO
P0DIR|=0x12;//方向是输出
P1SEL &= ~0x0c;//xxxx 00xx 配置为普通IO模式
P1DIR |= 0x0c;//xxxx 11xx 配置为输出模式
}
void LS164_BYTE(uchar Index)//数码管显示字形
{
uchar i = 0;
uchar Data = LED_Map[Index];
for(;i<8;i++)
{
if(0x80 & Data)
{
LS164_DATA = 1; //传1位数据“1”
}
else
{
LS164_DATA = 0; //传1位数据“0”
}
Data = Data << 1;
LS164_CLK = 0;
LS164_CLK = 1; //时钟形成上升沿
}
}
void LED_Flash()
{
if(counter%4==1 || counter%4==2 || counter%4==3)//0.75s
{
led1 = 0;
led2 = 0;
}
else//0.25s
{
led1 = 1;
led2 = 1;
}
}
#pragma vector = T1_VECTOR
__interrupt void Time1(void)
{
T1STAT &= ~0X20;
IRCON = 0x00; //清除中断标志,也可由硬件自动完成
if(counter<=40)
{
counter++;//4次中断LED闪烁一轮(约为1s)
LED_Flash();
if(counter%4 == 0)
{
LS164_BYTE(count--);
}
}
else
counter = 0;//计数清零
}
void main()
{
Init_T1();
while(1)
{
if(count !=0 )
{
count = 9;
}
}
}
实验五:睡眠唤醒实验
实验原理:
1. 睡眠定时器寄存器
CC2530 的睡眠定时器(SLEEP TIMER)是一个 24 位的计数器,可以用来作为唤醒中断(仅 PM0-2)下面有效,ST 的计数时钟是 32.768K,向上计数,可以和一个 24 位数比较以产生中断,在 PM2 仍然工作,可以产生中断和触发 DMA。ST0,ST1,ST2 可读可写,这 3 个寄存器类似于串口的 BUFFER,实际上是两个寄存器。因此就计数来说,ST(指 ST0,ST1,ST2 下同)是不可写的,这里把它称为 STa,对于设置比较数来说,ST 是不可读的,把它称为 STb。 这样的话想实现定时唤醒就要利用 STb 和 STa 的数差, 相差 32768, 就表示 1 秒唤醒一次,相差 n*32768 表示 n 秒唤醒一次,这里的 n 可以是小数。此外,还有另两个重要的寄存器PCON与SLEEPCMD。
寄存器PCON作用是不同的供电模式生效,寄存器SLEEPCMD功能在于设置不同的供电模式。
睡眠定时器所用的系统时钟是低频的,而低频振荡器有两个:32kHz XOSC晶振与32kHz RC振荡器。32kHz XOSC用于运行在32768Hz,为系统需要的时间精度提供一个稳定的时钟信号。32kHz RC振荡器运行在32753Hz,两个32kHz振荡器不同同时运行。
电源有5种不同的供电模式,分别是主动模式、空闲模式、PM1、PM2、PM3主动模式是一般模式,而PM3具有最低的功耗。不同的供电模式对系统的影响如下表表1所示,并给出了稳压器和振荡器选择。
表1
主动模式:完全功能模式。稳压器的数字内核开启,16 MHz RC振荡器或32 MHz晶体振荡器运行,或者两者都运行。32kHz RCOSC振荡器或32kHz XOSC运行。
空闲模式:除了CPU内核停止运行(即空闲),其他和主动模式一样。
PM1:稳压器的数字部分开启。16 MHz RC振荡器和32 MHz晶体振荡都不运行,32kHz RCOSC振荡器或32kHz XOSC运行。复位、外部中断或睡眠定时器过期系统将转到主动模式。
PM2: 稳压器的数字部分开启。16 MHz RC振荡器和32 MHz晶体振荡都不运行,32kHz RCOSC振荡器或32kHz XOSC运行。复位、外部中断或睡眠定时器过期系统将转到主动模式。
PM3:稳压器的数字内核关闭。所有的振荡器都不运行。复位和外部中断时系统将转到主动模式。
实验内容:D2与D3初始状态为全灭,使用睡眠定时器定时使两个灯同时亮2秒,灭1秒。(LED电路图如下图所示)。
实现代码:
#include<iocc2530.h>
#define uchar unsigned char
#define uint unsigned int
#define uint32 unsigned long
#define CRYSTAL 0
#define RC 1
#define LS164_DATA P1_3
#define LS164_CLK P1_2
#define led1 P0_1
#define led2 P0_4
#define key1 P1_1
#define key2 P2_0
uchar LEDBLINK;
uint time;
//delay()延时函数
void delay(uint n)
{
uint jj;
for(jj = 0;jj<n;jj++);
for(jj = 0;jj<n;jj++);
for(jj = 0;jj<n;jj++);
for(jj = 0;jj<n;jj++);
for(jj = 0;jj<n;jj++);
}
void InitLEDIO()
{
P0SEL&=0x12;//led2 led3 P0_1 p0_4为IO
P0DIR |= 0x12;//P0_1\4定义为led输出
led1 = 1;
led2 = 1;//led初始时为关
}
//设定系统主时钟函数
void SET_MAIN_CLOCK(uint source)
{
if(source)
{
CLKCONCMD |= 0x40;//x1xx xxxx 选择16MHz RC振荡器,CLKCONCMD是时钟频率控制寄存器
while(!(CLKCONSTA & 0x40));//16MHz待稳,CLKCONSTA是时间频率状态寄存器
}
else
{
CLKCONCMD &= ~0x47;//选择32MHz晶振
while((CLKCONSTA & 0x40));
}
}
//三目运算实现设定系统低速时钟函数
void SET_LOW_CLOCK(uint source)
{
(source == RC)?(CLKCONCMD |= 0x80):(CLKCONCMD &= ~0x80);
//if(source==RC)
//CLKCONCMD |= 0x80;
//else
//CLKCONCMD &= ~0x80; //也就是说如果时钟源是RC,那么就将CLKCONCMD寄存器的第7位置1;否则将第7位清零
}
//设置睡眠时间,sec = 1时为一秒
void Set_ST_Period(uint sec)
{
long sleepTimer = 0;//读睡眠定时器当前值到变量sleepTimer中,先读ST0的值
sleepTimer |= ST0;
sleepTimer |= (long)ST1 << 8;
sleepTimer |= (long)ST2 << 16;
sleepTimer += ((long)sec *(long)32768);//用于确定增加一次的时间
//睡眠定时器的时钟频率为32.768kHz,故而1s需要增加32 768次
//将睡眠时间加到sleepTimer上
ST2 = (char) (sleepTimer >> 16);//强制字符,转换右移后的结果
ST1 = (char) (sleepTimer >> 8);
ST0 = (char) sleepTimer;
}
//初始化睡眠定时器
void Init_SLEEP_TIMER()
{
ST2 = 0x00;
ST1 = 0x0F;
ST0 = 0x0F;
EA = 1;//开中断
STIE = 1;//睡眠定时器中断使能
STIF = 0;//睡眠定时器状态设置0
}
#pragma vector = ST_VECTOR
__interrupt void ST_ISR()
{
STIF = 0;//清除睡眠定时器标志位
LEDBLINK = 1;//设置LED1闪烁标志
}
int Time_confirm()//确定睡眠定时器的时间
{
led1 =! led1;
led2 =! led2;
if(led1 == 1 && led2 == 1 )//当灯灭时,返回1,实现1秒灭
return 1;
else //当灯亮时,返回2,实现两秒亮
return 2;
}
void LED_state(uint n)
{
if(n == 1)//当返回的值为1时,实现灯灭
{
led1 = 1;
led2 = 1;
}
else if(n == 2)//当返回的值为2时,实现灯亮
{
led1 = 0;
led2 = 0;
}
}
void main(void)
{
SET_MAIN_CLOCK(CRYSTAL);//设定系统主时钟函数
SET_LOW_CLOCK(CRYSTAL);//设定系统低速时钟函数
InitLEDIO();//led灯初始化,两个灯全灭
LEDBLINK = 0;
Init_SLEEP_TIMER();//初始化睡眠定时器
time = Time_confirm();
Set_ST_Period(time);//设置睡眠时间
while(1)
{
if(LEDBLINK)
{
LED_state(time);//led灯状态确定
time = Time_confirm();
Set_ST_Period(time);
LEDBLINK = 0;//清除led灯的标志位
}
delay(100);//短暂时延,用于缓冲
}
}
实验六:温湿度传感器实验
实验原理:温湿度传感器SHT10是一款含有已校准数字信号输出的温湿度复合传感器。传感器包括一个电容式聚合体测湿元件和一个能隙式测温元件,并与一个14位的A/D转换器以及串行接口电路在同一芯片上实现无缝连接。因此,该产品具有品质卓越、超快响应、抗干扰能力强、性价比高等优点。SHT10详细操作说明,请详见其芯片数据手册。
实验内容:使用带有温湿度传感器SHT10的实验板,将其测量出来的温度通过串口助手显示出来(温度值保留一位小数),如果温度超过某一上限值,则串口助手换行显示“too high”(LED1与LED2一起闪烁,同时测量当前湿度并保留一位小数,在串口中显示出来),否则换行显示“normal”(此时不显示温度值),灯全灭(电路如下图所示)
实现核心代码:
#include "ioCC2530.h"
#define uint8 unsigned char
#define uint16 unsigned int
#define uint32 unsigned long
#define LED1 P0_0
#define LED2 P2_0extern void Sensor_PIN_INT(void);
extern uint32 ReadSHT1(uint8 Addr);
void delay(void)
{
unsigned int i;
unsigned char j;for(i=0;i<1500;i++)
{
for(j=0;j<200;j++)
{
asm("NOP");
asm("NOP");
asm("NOP");
}
}
}void UartTX_Send_String(unsigned char *Data,int len) //串口发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
}void UartTX_Send_Data(unsigned char Data,int len) //串口发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = Data;
while(UTX0IF == 0);
UTX0IF = 0;
}
}/*
#pragma vector=URX0_VECTOR //uart0中断函数
__interrupt void uart0(void){
URX0IF = 0; //清中断标志
P0_0=~P0_0;
uart_buffer = U0DBUF;
UartTX_Send_Data(uart_buffer,1);
}*/void main( void )
{
unsigned long lTemp;
float RHTValue;
float wendu;
unsigned char buf[9];
unsigned int dbuf;
P0DIR |= 0x01; //设置P0.0为输出方式;P0.4和P0.5为输入方式
P2DIR |= 0x01; //设置P2.0为输出方式
P0_0 = 1;
P2_0 = 1; //熄灭LED
CLKCONCMD &= ~0x40; //选择32M晶振
while(CLKCONSTA & 0x40); //等待XSOC稳定
CLKCONCMD = 0xb8; //TICHSPD 128分频,CLKSPD 不分频
PERCFG = 0x00; //位置1 P0 口
P0SEL = 0x3c; //P0 用作串口
U0CSR |= 0x80; //UART 方式
U0GCR |= 10; //baud_e = 10;
U0BAUD |= 216; //波特率设为57600
UTX0IF = 1;
U0CSR |= 0X40; //允许接收
IEN0 |= 0x84; //开总中断,接收中断
Sensor_PIN_INT();UartTX_Send_String("Testing...\r\n",12);//串口输出测试信号
while(1){
lTemp = ReadSHT1(3);//14bit温度
lTemp = lTemp >> 8;
RHTValue = lTemp;
RHTValue = 0.01 * RHTValue - 39.64;
wendu = RHTValue;//将传感器采集的数据赋值给设定值
buf[0] = (uint8)RHTValue;//温湿度传感器温度
buf[0] = ( ((buf[0]/10)<<4) + (buf[0]%10) );
buf[1] = (buf[0]>>4)&0xf;
buf[1] = buf[1] + '0';
buf[2] = (buf[0])&0xf;
buf[2] = buf[2] + '0';
/*小数部分*/
dbuf = (uint16)(wendu*10);
dbuf = dbuf%10;
buf[7] = dbuf;
buf[7] = buf[7] +'0';
/*小数部分*/
lTemp = ReadSHT1(5);//12bit湿度
lTemp = lTemp >> 8;
RHTValue = lTemp;
RHTValue = 0.0405 * RHTValue -4 - 2.8*RHTValue*RHTValue/1000000;
//通过采集数据计算相应的数据值
buf[3] = (uint8)RHTValue;//温湿度传感器湿度
buf[3] = ( ((buf[3]/10)<<4) + (buf[3]%10) );
buf[4] = (buf[3]>>4)&0xf;
buf[4] = buf[4] + '0';
buf[5] = (buf[3])&0xf;
buf[5] = buf[5] + '0';
/*小数部分*/
dbuf = (uint16)(RHTValue*10);
dbuf = dbuf%10;
buf[8] = dbuf;
buf[8] = buf[8] +'0';
/*小数部分*/
if(wendu > 26)
{
UartTX_Send_String("too high",8);
UartTX_Send_String(" ",4);
LED1 = !LED1;
LED2 = !LED2;
delay();
LED1 = !LED1;
LED2 = !LED2;
delay();
UartTX_Send_String("Temperature = ",14);
UartTX_Send_String(&buf[1],1);
UartTX_Send_String(&buf[2],1);
UartTX_Send_String(".",1);
UartTX_Send_String(&buf[7],1);
UartTX_Send_String(" ",4);
UartTX_Send_String("humidity = ",11);
UartTX_Send_String(&buf[4],1);
UartTX_Send_String(&buf[5],1);
UartTX_Send_String(".",1);
UartTX_Send_String(&buf[8],1);
UartTX_Send_String("\r\n",2);
}
else
{
UartTX_Send_String("Temperature = ",14);
UartTX_Send_String(&buf[1],1);
UartTX_Send_String(&buf[2],1);
UartTX_Send_String(".",1);
UartTX_Send_String(&buf[7],1);
UartTX_Send_String(" ",4);
UartTX_Send_String("humidity = ",11);
UartTX_Send_String(&buf[4],1);
UartTX_Send_String(&buf[5],1);
UartTX_Send_String(".",1);
UartTX_Send_String(&buf[8],1);
UartTX_Send_String("\r\n",2);
delay();
}
delay();
}
}
实验七:超声波测距实验
实验原理:
超声波测距模块S906功能是利用超声波对距离进行测量,其探测有效范围是2cm-500cm,探测精度为3mm,工作电压为5V;该模块包含5个引脚,分别是VCC、trig(控制端)、echo(接收端)、out(空脚)、GND。该模块的使用方法为:
1. 采用I/O触发测距,给一个至少10微秒的高电平信号(实际上25微秒最佳)
2. 模块自动发送8个40Hz的方波,自动检测是否有信号返回
3. 有信号返回,通过I/O输出一高电平,高电平持续时间就是超声波从发射到返回的时间测试的距离=(高电平时间*声速(340m/s))/2;
该模块使用方法简单,一个控制口发一个10微秒以上的高电平,就可以在接收口等待高电平输出,一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离,如此不断地周期测量,就可以达到移动测量的值了。
实验内容:使用带有超声波传感器的实验板,将其测量出来的距离(单位为cm)显示在串口助手中,如果距离低于10cm,则串口显示“L”,并且D2与D3不断闪烁;如果距离高于30cm,串口显示“H”,D2不断闪烁,距离介于10~30cm之间,两个LED为灭,并且串口显示“Normal”(D2与D3初始状态为灭,超声波模块的电路图如下图所示)
实现代码:
#include "ioCC2530.h"
#include<string.h>
#define led1 P0_0
#define led2 P2_0//配置灯的端口
unsigned char counter;
unsigned char buf[3];
float distance;
void SendASignal()
{
unsigned char i;
P1 &= ~(1<<0);//P1_0口先给一个低电平,给出一个至少10μs的高电平
//P1 |= (1<<0);
for(i=0;i<33;i++){
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
}
P1 |= (1<<0); //高电平结束
}
void delay(void)
{
unsigned int i;
unsigned char j;
for(i=0;i<5000;i++)
{
for(j=0;j<200;j++)
{
asm("NOP");
asm("NOP");
asm("NOP");
}
}
}
void UartTX_Send_String(unsigned char *Data,int len) //串口字符串发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
void UartTX_Send_Data(unsigned char Data,int len) //串口数据发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = Data;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
#pragma vector=URX0_VECTOR //uart0中断函数
__interrupt void uart0(void){
URX0IF = 0; //清中断标志
}
#pragma vector=T1_VECTOR //定时器1中断函数 50us
__interrupt void Timer1(void){
counter++;
//P0_0=~P0_0;
}
void LED_flash()
{
if(counter<10)
{
UartTX_Send_String("L\r\n",3);
led1 =!led1;
led2 =!led2;
}
else if(counter>30)
{
UartTX_Send_String("H\r\n",3);
led1 =!led1;
}
else
{
UartTX_Send_String("Normal\r\n",8);
led1 = 1;
led2 = 1;//其他情况全灭
}
}
void DATA_Init()
{
P0DIR |= 0x01; //设置P0.0为输出方式
P2DIR |= 0x01; //设置P2.0为输出方式
P1SEL &= ~((1<<0)|(1<<1));//P1.0,P1.1为GPIO
P1DIR |= ((1<<0)); //P1_0为OUTPUT
P1DIR &= ~(1<<1); //P1_1为INPUT
P1 &= ~(1<<0);
P0_0 = 1;
P2_0 = 1; //LED初始为灭
counter = 0;
CLKCONCMD &= ~0x40; //选择32M晶振
while(CLKCONSTA & 0x40); //等待XSOC稳定
CLKCONCMD = 0xb8; //TICHSPD 128分频,CLKSPD 不分频
//SLEEPCMD |= 0x04; //关闭不用的RC 振荡器
T1CC0L = 6;
T1CC0H = 0x00; //比较值
T1CTL = 0x33; //通道0,不分频,up/down模式
PERCFG = 0x00; //位置1 P0 口
P0SEL = 0x3c; //P0 用作串口
U0CSR |= 0x80; //UART 方式
U0GCR |= 10; //baud_e = 10;
U0BAUD |= 216; //波特率设为57600
UTX0IF = 1;
U0CSR |= 0X40; //允许接收
IEN0 |= 0x84; //开总中断,接收中断
EA = 1; //开总中断
T1IE = 1; //开定时器T1中断
}
void main( void )
{
DATA_Init();
while(1){
P2_0=~P2_0;//led2闪烁
SendASignal();
while(1){
if(P1_1==1) break;
}
counter = 0;
while(1){
if(P1_1==0) break;
}
distance = counter;
distance = (distance*50/1000000*340/2*100);
counter = (unsigned int)distance;
//counter保存高电平长度,单位为50us
buf[0] = counter/100;
buf[1] = (counter%100)/10;
buf[2] = (counter%10);
if(buf[0] > 0x9)
buf[0] = buf[0] - 0xA + 'A';
else
buf[0] = buf[0] + '0';
if(buf[1] > 0x9)
buf[1] = buf[1] -0xA + 'A';
else
buf[1] = buf[1] + '0';
if(buf[2] > 0x9)
buf[2] = buf[2] -0xA + 'A';
else
buf[2] = buf[2] + '0';
LED_flash();
//D_estimateOutput();//距离测试判断输出
UartTX_Send_String("Ultrasonic = ",13);
UartTX_Send_String(&buf[0],1);
UartTX_Send_String(&buf[1],1);
UartTX_Send_String(&buf[2],1);
UartTX_Send_String("cm",2);
UartTX_Send_String("\r\n",2);
delay();
}
} // end of main()
实验八:光照传感器实验
实验原理:
CC2530无线单片机的ADC支持多达14位的模拟数字转换,它包括一个模拟多路转换器,具有多达8个各自可配置的通道以及一个参考电压发生器。转换结果通过DMA写入存储器,还具有不同的运行模式。ADC模块的方框图如下图所示:
并口P0的8位都可以做DA转换使用,CC2530内部还有一个温湿度传感器,也属于DA转换的输入,AD转换的精度从8位到14位可以设置。相关的寄存器有ADCL、ADCH、ADCCON1以及ADCCON3。
实验内容:
使用带有光照传感器的实验板测量光的强度,如果低于某一个值时,则LED亮,反之则为灭。(电路图如下图所示)。
实现代码:
#include "ioCC2530.h"
#define uint8 unsigned char
#define uint16 unsigned int
#define uint32 unsigned long
extern void Sensor_PIN_INT(void);
extern uint16 ReadAdcValue(uint8 ChannelNum,uint8 DecimationRate,uint8 RefVoltage);
char uart_buffer;
void delay(void)
{
unsigned int i;
unsigned char j;
for(i=0;i<1500;i++)
{
for(j=0;j<200;j++)
{
asm("NOP");
asm("NOP");
asm("NOP");
}
}
}
void UartTX_Send_String(unsigned char *Data,int len) //串口发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
void UartTX_Send_Data(unsigned char Data,int len) //串口发送函数
{
int j;
for(j=0;j<len;j++)
{
U0DBUF = Data;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
#pragma vector=URX0_VECTOR //uart0中断函数
__interrupt void uart0(void){
URX0IF = 0; //清中断标志
P0_0=~P0_0;
uart_buffer = U0DBUF;
UartTX_Send_Data(uart_buffer,1);
}
void main( void )
{
unsigned char buf[8];
uint16 temp;
Sensor_PIN_INT();
UartTX_Send_String("Testing...\r\n",12);
while(1){
//P2_0=~P2_0;
temp = ReadAdcValue(0x1,3,2);//P0.1采集光照度,12bit,AVDD5作为参考
temp = (temp>>6);
buf[0] = (uint8)(temp&0xff);
buf[1] = (buf[0]>>4)&0xf;
buf[2] = buf[0]&0xf;
if(buf[1] > 0x9)
buf[1] = buf[1] - 0XA + 'A';
else
buf[1] = buf[1] + '0';
if(buf[2] > 0x9)
buf[2] = buf[2] - 0XA + 'A';
else
buf[2] = buf[2] + '0';
if(temp>0x32)//光强是否大于50
{
P0_0=0;
P2_0=0;
UartTX_Send_String("LIGHT = ",8);
UartTX_Send_String(&buf[1],1);
UartTX_Send_String(&buf[2],1);
UartTX_Send_String("\r\n",2);
}
else{
P0_0=1;
P2_0=1;
}
delay();
}
} // end of main()
最后
最后的最后,笔者有话要锁:文章的总结性的内容会持续性的更新,这也是我们学习zigbee系例文章的第一篇,除去代码,也洋洋洒洒写了近万字,希望能够帮助到大家!如果在学习过程中,遇到不理解的专业名词,或者不理解其作用的,可以尽管在评论区留言,我不会嘲笑你的哈哈哈哈哈哈哈哈哈哈,我看到以后,会以最快的速度在“持续更新”中为你解答,当然啦,可不许捣乱,搁哪问串口是什么.....哈哈哈哈哈哈哈。以上!