基于ZigBee的无线数据采集系统.docx
《基于ZigBee的无线数据采集系统.docx》由会员分享,可在线阅读,更多相关《基于ZigBee的无线数据采集系统.docx(21页珍藏版)》请在冰豆网上搜索。
基于ZigBee的无线数据采集系统
无线数据采集控制系统的设计
课程名称专业综合课程设计
课程设计总评成绩
学生姓名、学号
学生专业班级
指导教师姓名
课程设计起止日期2016.11.7-2016.12.1
无线数据采集控制系统的设计
第1章需求分析
1.1课程设计题目
无线电子开关
1.2课程设计任务及要求
无线电子开关的设计
PC端用java编写程序通过串口给CC2530模块A发送开关等指令,CC2530模块A通过射频模块将指令以无线方式发送给CC2530模块B,CC2530模块B根据指令开灯或关灯。
针对给定的任务,结合专业课程和专业知识完成系统的硬件或软件设计,对硬件设计:
要求完成系统和接口设计,并能动手制作和调试,对测量结果进行分析处理。
设计须提供实物成果。
对软件设计:
要求能够熟悉软件工具,设计其算法或者是系统结构,实现该算法和软件,能够对其计算过程进行推导或者是说明软件系统结构,并能调试成功,对实验结果进行分析处理。
通过专业综合的课程设计,使学生能够综合掌握无线传感网技术、java语言程序设计、数据库等课程的专业知识,要求学生经过课程设计的教学环节进一步理解无线传感器网络的结构和组成原理,掌握数据采集节点,无线传输,串口通信等模块的基本设计方法,完成系统应用程序的设计。
通过专业综合课程设计,提高学生电子信息系统综合设计能力。
掌握电子信息系统的基本开发过程及应用方法。
要求学生经过课程设计的教学环节进一步理解电子信息系统的设计方法,根据所选择的对象进行应用系统的硬件和软件设计,提高学生专业的综合素质及专业能力。
1.3软硬件运行环境及开发工具
软件开发环境为eclipse+IAREmbeddedWorkbench
硬件开发环境为CC2530
1.4主要芯片说明
CC2530是ZigBee无线数据传输其中的一个核心芯片,它能够以非常低的总材料成本建立强的网络节点。
CC2530芯片有四种不同的闪存版本:
分别具32/64/128/256KB 的闪存。
CC2530芯片工作实具有不同的运行模式,使得它尤其适应超低功耗要求的系统。
运行模式之间的转换时间短进一步确保了低能源消耗。
在业界内,CC2530结合了德州仪器的业界领先的黄金单元ZigBee 协议栈,提供了一个强大和完整的ZigBee 解决方案。
CC2530芯片共包含了40个引脚,引脚的排布如图1所示:
图1:
cc2530引脚图
CC2530芯片模块大致可以分为三类:
CPU和内存相关的模块;外设、时钟和电源管理相关的模块以及无线电相关的模块。
(1)CPU 和内存:
CC253x芯片系列中使用的8051CPU内核是一个单周期的8051兼容内核。
(2)调试接口:
执行一个专有的两线串行接口,用于内电路调试。
(3)I/O控制器:
负责所有通用I/O引脚。
(4)五通道DMA控制器:
系统可以使用一个多功能的五通道DMA控制器,使用XDATA存储空间访问存储器,因此能够访问所有物理存储器。
(5)定时器1:
是一个16位定时器,具有定时器PWM功能。
。
(6)内置MAC定时器:
是专门为支持IEEE 802.15.4,MAC或软件中其他时槽的协议设计。
(7)定时器3和定时器4:
是8位定时器,具有定时器/计数器/PWM功能。
(8)睡眠定时器:
是一个超低功耗的定时器,计算32kHz晶振或32 kHz RC振荡器的周期。
(9)看门狗:
一个内置的看门狗,允许CC2530在固件挂起的情况下复位自身。
第2章系统总体设计
2.1系统组成方案
由eclipse编写上位机程序,TAR编写下位机程序,通过无线方式发送命令。
如图2
图2:
设计方案
2.2系统工作原理
上位机程序以轮询方式通过PC串口向ZigBee节点A发送命令light:
1011,节点A通过无线方式向节点B转发命令light:
1011,节点B接收到命令后点亮蓝灯,黄灯和绿灯(1点亮,0熄灭)。
上位机程序以轮询方式通过PC串口向ZigBee节点A发送命令light:
1011,节点A通过无线方式向节点C转发命令light:
1011,节点C接收到命令后点亮蓝灯,红灯和绿灯(1点亮,0熄灭)。
2.3系统构建
PC端用java编写程序通过串口给CC2530模块A发送开关等指令,CC2530模块A通过射频模块将指令以无线方式发送给CC2530模块B和模块C,CC2530模块B和模块C根据指令开灯或关灯。
根据指令(light1或light2)第5位判断后缀为1或者为2,节点B和节点C分别依据指令开灯或者关灯
第3章系统硬件设计
3.1主模块电路设计
本系统采用TI公司生产的CC2530为核心器件。
CC2530是一个真正的片上系统(SoC)解决方案,它能够以非常低的材料成本建立强大的网络节点,它结合了领先的RF收发器的优良性能、业界标准的增强型8051CPU、系统内可编程闪存、8KBRAM、A/D转换器以及许多其他强大的功能,并且其具有不同的运行模式,使得它尤其适应超低功耗要求的系统。
围绕着CC2530芯片,系统大致可分为三大模块:
CPU和内存相关的模块,外设、时钟和电源管理相关的模块以及无线电相关的模块。
3.2显示模块电路设计
设计通过A发送的指令来控制4个LED灯的亮灭,LED模块电路及灯的引脚图,如图3、图4所示。
图3:
LED模块电路
图4:
LED灯对应引脚
若要点亮LED灯,CC2530的通用IO口需要配置三个寄存器:
P1SEL,P1DIR,P1INP,P2INP
功能选择寄存器PxSEL,其中x为端口标号0~2,用来设置端口的每个引脚为通用I/O或外部设备I/O。
默认为通用I/O。
方向寄存器PxDIR,其中x为端口标号0~2,用来设置端口的每个引脚为输入或输出。
默认为输入。
输入模式寄存器P1INP,用来设置P1端口用作输入时为上拉、下拉模式或三态模式。
默认为上、下拉模式。
具体是上拉还是下拉,由P2INP来设置。
输入模式寄存器P2INP,用来设置P0、P1、P2端口用作输入时为上拉、下拉模式。
默认为上拉模式。
完整配置:
•P1SEL&=~0x03;//P1_0、P1_1通用IO
•P1DIR|=0x03;//P1_0、P1_1输出
•P1INP&=~0x03;//P1_0、P1_1上下拉
•P2INP&=~0x40;//P1上拉
简化配置:
•P1DIR|=0x03;//P1_0、P1_1输出
3.3通信模块电路设计
CC2530是符合802.15.4标准的无线收发芯片,但是本设计并没有遵守802.15.4协议规则,在发送过程中忽略了网络ID、源地址和目标地址等参数,在接收的过程中禁止了帧过滤。
通过发送和接收过程的处理使得CC2530无线部分的使用尽可能的简单清晰,通过最少的代码说明问题。
无线芯片的调试具有一定的难度,一般存在发送设备和接收设备。
为了通过最简单的代码说明无线芯片的使用,只编写一个设备的代码同时实现发送和接收功能。
设备代码的功能也相对简单,CC2530从串口接收数据并把数据通过RF部分原分不动地发送出去,于此同时CC2530把从RF部分接收的数据原分不动的通过串口发送出去,通过这样的方式实现无线串口。
发送的数据编号以及控制指令,来控制灯的亮度和开关。
串口数据属于“流”型数据包,RF部分属于“帧”型数据包。
在串口数据处理与分析中,一般采用特定的串口头和长度的方式解析数据,但是本文采用通过串口时间间隔的方式解析数据,这种方法等同于modbus-RTU串口数据处理的方法。
通过这种检测字节数据时间间隔的方法使得CC2530的串口部分可以接收无特殊格式要求的数据,真正实现无线串口功能。
第4章系统软件设计
4.1上位机程序设计
上位机程序通过两个类来实现,第一个SerialPort类用来实现控制程序面板,第二个DSerialPort类来实现串口通信。
图5所示为Java的两个类:
图5:
Java的两个类
SerialPort类中,通过网格布局管理器来实现面板的布局,设置各个标签以及文本框,复选框,组合框,窗口的大小位置以及标题等。
4个灯的设置大致相同,通过jcheckbox来表示灯选中以及未选中时的设置。
jlabel1=newJLabel("全关1:
");
jcheckbox1=newJCheckBox("绿");
jcheckbox2=newJCheckBox("红");
jcheckbox3=newJCheckBox("黄");
jcheckbox4=newJCheckBox("蓝");
jlabel2=newJLabel("全关2:
");
jcheckbox5=newJCheckBox("绿");
jcheckbox6=newJCheckBox("红");
jcheckbox7=newJCheckBox("黄");
jcheckbox8=newJCheckBox("蓝");
//灯一的设置
jcheckpanel1=newJPanel();
gridbagconstraints.anchor=GridBagConstraints.EAST;
gridbagconstraints.gridwidth=1;
gridbaglayout.setConstraints(jlabel1,gridbagconstraints);
add(jlabel1);
gridbagconstraints.anchor=GridBagConstraints.WEST;
gridbagconstraints.gridwidth=GridBagConstraints.REMAINDER;
gridbaglayout.setConstraints(jcheckpanel1,gridbagconstraints);
add(jcheckpanel1);
jcheckpanel1.add(jcheckbox4);
jcheckpanel1.add(jcheckbox3);
jcheckpanel1.add(jcheckbox2);
jcheckpanel1.add(jcheckbox1);
//灯一开关的设置
jcheckbox1.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEvente){
if(getLightState1().equals("0000"))
jlabel1.setText("全关1:
");
elseif(getLightState1().equals("1111"))
jlabel1.setText("全开1:
");
else
jlabel1.setText("开灯1:
");
}
});
通过switch语句,串口打开后轮流发送Data的参数以及设置参数异常时的命令:
voidstartRun(){
Timertimer=newTimer();
TimerTasktask=newTimerTask(){
inti=0;
publicvoidrun(){
if(com_open){//串口打开则发送
try{
switch(i){
case0:
{sp.write(lightcommand1+getLightState1());break;}
case1:
{sp.write(lightcommand2+getLightState2());break;}
}
if(i==2){
i=0;
}
else
i++;
}
catch(Exceptione){
System.out.println("发送异常");
}
}
}
};
timer.schedule(task,1000,200);//在200毫秒后执行此任务,每次间隔2秒执行一次,如果传递一个Data参数,就可以在某个固定的时间执行这个任务.
}
串口通信DSerialport类中,通过listport方法,列出了所有可用的串口,并设置返回值类型为void:
publicvoidlistPort(JComboBoxjcombox1){
CommPortIdentifiercpid;
Enumerationen=CommPortIdentifier.getPortIdentifiers();
System.out.println("nowtolistallPortofthisPC:
"+en);
while(en.hasMoreElements()){
cpid=(CommPortIdentifier)en.nextElement();
if(cpid.getPortType()==CommPortIdentifier.PORT_SERIAL){
jcombox1.addItem(cpid.getName());
System.out.println(cpid.getName()+","+cpid.getCurrentOwner());
}
}
}
通过selectport方法,选择一个端口,设置返回值类型void:
publicvoidselectPort(StringportName,intrate){
mPort=null;
CommPortIdentifiercpid;
Enumerationen=CommPortIdentifier.getPortIdentifiers();
while(en.hasMoreElements()){
cpid=(CommPortIdentifier)en.nextElement();
if(cpid.getPortType()==CommPortIdentifier.PORT_SERIAL
&&cpid.getName().equals(portName)){
mPort=cpid;
break;
}
}
openPort(rate);
}
通过openPort方法,打开SerialPort,设置返回值类型void:
privatevoidopenPort(intrate){
if(commPort==null)
System.out.println(String.format("无法找到名字为'%1$s'的串口!
",commPort.getName()));
else{
System.out.println("端口选择成功,当前端口:
"+commPort.getName()+",现在实例化SerialPort:
");
try{
serialPort=(SerialPort)commPort.open(appName,timeout);
System.out.println("实例SerialPort成功!
");
}catch(PortInUseExceptione){
thrownewRuntimeException(String.format("端口'%1$s'正在使用中!
",
commPort.getName()));
}
try{
serialPort.setSerialPortParams(rate,8,1,0);//设置波特率等参数
}
catch(UnsupportedCommOperationExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
通过checkport方法检查端口是否正确连接:
privatevoidcheckPort(){
if(commPort==null)
thrownewRuntimeException("没有选择端口,请使用"+
"selectPort(StringportName)方法选择端口");
if(serialPort==null){
thrownewRuntimeException("SerialPort对象无效!
");
}
}
通过write方法向端口发送数据,在调用此方法前需要先选择端口并确定serialport正常打开。
publicvoidwrite(Stringmessage){
checkPort();
try{
outputStream=serialPort.getOutputStream();
}catch(IOExceptione){
thrownewRuntimeException("获取端口的OutputStream出错:
"+e.getMessage());
}
try{
outputStream.write(message.getBytes());
System.out.println("信息发送成功!
");
}catch(IOExceptione){
thrownewRuntimeException("向端口发送信息时出错:
"+e.getMessage());
}finally{
try{
outputStream.close();
}catch(Exceptione){
}
}
}
通过startread开始监听从端口中接受的数据:
publicvoidstartRead(inttime){
checkPort();
try{
inputStream=serialPort.getInputStream();
}catch(IOExceptione){
thrownewRuntimeException("获取端口的InputStream出错:
"+e.getMessage());
}
try{
serialPort.addEventListener(this);
}catch(TooManyListenersExceptione){
thrownewRuntimeException(e.getMessage());
}
serialPort.notifyOnDataAvailable(true);
System.out.println(String.format("开始监听来自'%1$s'的数据--------------",commPort.getName()));
if(time>0){
this.threadTime=time*1000;
Threadt=newThread(this);
t.start();
System.out.println(String.format("监听程序将在%1$d秒后关闭。
。
。
。
",threadTime));
}
}
使用close方法关闭serialport:
publicvoidclose(){
serialPort.close();
serialPort=null;
commPort=null;
}
/**
*数据接收的监听处理函数
*/
publicvoidserialEvent(SerialPortEventarg0){
byte[]readBuffer=newbyte[20];
intnumBytes=0;
StringreadStr="";
s2="";
try{
while(inputStream.available()>0){
try{
Thread.sleep(100);//休眠100ms
}catch(InterruptedExceptione){
e.printStackTrace();
}
numBytes=inputStream.read(readBuffer);//从串口上读取数据上的流
}
for(inti=0;ireadStr=readStr+(char)readBuffer[i];
}
s2=readStr;
System.out.println("接收的数据:
"+readStr);
}
catch(IOExceptione){
e.printStackTrace();
}
}
/**
*读取接收的数据
*/
publicStringreadData(){
returns2;
}
publicvoidrun(){
try{
Thread.sleep(threadTime);
serialPort.close();
System.out.println(String.format("端口''监听关闭了!
",commPort.getName()));
}catch(Exceptione){
e.printStackTrace();
}
}
}
4.2下位机程序设计
发送过程大致可分为侦听SFD清除信道,关闭接收中断,填充缓冲区,启动发送并等待发送完成,最后恢复接收中断。
在这几个过程中唯一需要说明的便是填充缓冲区过程,在初始化过程中提到FRMCTRL0寄存器,该寄存器中AUTO_CRC标志位默认为使能状态,CC2530的物理层负载部分第一个字节为长度域,填充实际负载之前需要先填充长度域,而物理层负载在原长度的基础上增加2。
长度域数值增加2的原因是由于自动CRC的存在,CRC部分占两个字节CC2530会把这两个字节填充至发送缓冲区。
voidrf_send(char*pbuf,intlen)
{
RFST=0xE3;//RF接收使能ISRXON
//等待发送状态不活跃并且没有接收到SFD
while(FSM