摘 要
虚拟仪器是现代计算机技术同仪器技术深层次结合的全新概念仪器,实质是利用计算机显示器的显示功能模拟传统仪器的控制面板, 以多种形式表达输出测量结果, 利用计算机强大的软件功能实现信号数据的运算、 分析和处理,完成各种测试功能的一种计算机仪器系统。
本文介绍了利用 LabView 语言来实现上、下位机之间通信的方法,并从软、硬件两个方面阐述了设计思想。在简要介绍图形化虚拟仪器平台 LabView的基础上 , 分析 STC12C5A60S2单片机与 LabView 之间的串口通信模式 , 并结合该设计中设计的数字陀螺仪(MPU6050)分析系统给出串口通信的软、硬件设计。
应用先进的虚拟仪器软件 LabView,大大降低了串口通讯复杂程度,减小了软件设计的工作量, 能够大大降低投资成本。 在实际应用中有巨大的使用价值。
关键词:STC12单片机,LabView,MPU6050
1.1 设计任务........................................................... 3
2.1 系统总设计概述..................................................... 4
2.2 最小系统原理图..................................................... 4
2.2.1 STC12C5260S2主要引脚功能说明.................................. 5
2.3 陀螺仪............................................................. 5
2.3.1 陀螺仪的概述................................................... 5
3.1 总体设计........................................................... 9
3.2 主要代码.......................................................... 10
3.3 软件工具.......................................................... 10
3.3.1 KEIL程序编写工具............................................. 10
3.3.2 STC_ISP下载工具.............................................. 11
3.3.3 labview工具.................................................. 11
4.1 软件测试.......................................................... 12
4.2 硬件测试.......................................................... 13
4.3 测试数据分析...................................................... 13
目前以计算机为上位机和以单片机为下位机的集散式控制系统被广泛的应用于工业检测和控制系统中。由于 PC机的分析处理能力强 , 处理速度快 , 而单片机价格低廉、体积小、使用灵活方便 , 所以主机一般采用 PC机, 而从机则采用单片机。串行通信是一种常用的数据传输方法, 虽然它的传输速度慢, 但它占用的通信线路少,成本低,在工程的通信方式上仍有重要地位。通过 PC机的 RS-232串行接口与单片机之间串行通信是主要的通信手段。
虚拟仪器与传统仪器技术不同,虚拟仪器在通用计算机平台上通过数据采集设备,然后根据用户的实际需求就可以构建起不同的系统。 所以虚拟仪器实际上是一个按照用户的实际需求组成的数据采集系统。 具体来说,虚拟仪器有以下特点:
(1) 虚拟仪器利用了计算机丰富的软件资源。另外,计算机还能实时、直接地对测试数据进行各种分析与处理。
(2) 因为虚拟仪器融合了计算机的硬件资源,计算机来直接处理这些应用,这样就大大的增强了传统仪器的功能, 突破了传统仪器在数据处理、 显示、传输、存储等方面的限制。
(3) 虚拟仪器基于计算机总线和模块化仪器的总线,这样就使仪器的硬件实现了模块化,就可以方便地构建模块化的虚拟仪器。
(4) 当今世界的计算机技术和相关的技术发展十分迅速,虚拟仪器也是建立在此基础上的,因此虚拟仪器随着计算机更新的速度快,功能越来与强大。
(5) 由于计算机的体系是开放式的, 所以虚拟仪器的硬件和软件都具有开放性、可重复使用的特点,而且硬件还可以互换,这样就使虚拟仪器系统更为灵活
1.1 设计任务
1、 设计单片机及其相关电路,编写MPU6050采集传输数据程序;
2、 设计基于串行通信的协议,实现和 LabView 的通信功能并将数据及数据波形显示到LABView。
2.1 系统总设计概述
当陀螺仪提供了方位、水平、位置、速度和加速度的信号时,与单片机进行串口通信,通过串口中断进行数据校验,对所检测到的数据经处理送入单片机中,经过单片机的串口传送到labview上,当labview获取值时,数据图形列表通过获得的数据以十进制形式实时显示各种信号。系统框图如下:
图2-1 系统框架图
2.2 最小系统原理图
单片微型计算机简称单片机,它是一种集成电路芯片,利用超大规模集成电路技术,将具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能集成到一块硅片上构成的一个体积小却功能完善的微型计算机,在工业控制领域的广泛应用,从上世纪80年代,由当时的4位、8位单片机,发展到现在的32位300M的高速单片机[4]。本设计中涉及到的相关硬件电路有单片机最小系统电路、传感器串口通信电路、报警电路等。通过控制电路进而实现智能化的控制,比如进行数据的接收,报警实现,发送信息,打电话。主要由单片机芯片和传感器发送数据信息完成。
图2-2 单片机最小原理图
STC12C5260S2总共40个引脚,分别为P0、P1、P2、P3口。其中P1、P2、P3口均带有内部上拉电阻,而P0没有,因此需外接一个上拉电阻。四个口都作为I/O口使用,其中,P1口是完全提供给用户使用的准双向I/O口,P0口作为低8位地址总线使用,输出低8位地址,P2口可作为高8位地址总线用,输出高8位地址,P3口作为通用I/O口使用,P3口可提供第二功能,其中P3.0、P3.1作为串行输入、输出口,P3.2、P3.3为外部中断0和1输入,P3.4、P3.5作为定时器0和1的外部计数输入,P3.6、P3.7作为外部数据存储器的写和读选通控制信号[5]。
陀螺仪,是一个圆形的中轴的结合体。而事实上,静止与运动的陀螺仪本身并无区别,如果静止的陀螺仪本身绝对平衡的话,抛除外在因素陀螺仪是可以不依靠旋转便能立定的。而如果陀螺仪本身尺寸不平衡的话,在静止下就会造成陀螺仪模型倾斜跌倒,因此不均衡的陀螺仪必然依靠旋转来维持平衡。
陀螺仪本身与引力有关,因为引力的影响,不均衡的陀螺仪,重的一端将向下运行,而轻的一端向上。在引力场中,重物下降的速度是需要时间的,物体坠落的速度远远慢于陀螺仪本身旋转的速度时,将导致陀螺仪偏重点,在旋转中不断的改变陀螺仪自身的平衡,并形成一个向上旋转的速度方向。
图2-3 陀螺仪原理图
3 系统软件
3.1 总体设计
系统上电后各个模块进行初始化,初始化完成后,陀螺仪进行工作,陀螺仪工作后产生各种信号,把信号处理后,然传给单片机,单片机与labview进行通信后,将得到的各种信号值实时显示在labview数据图形上。主要流程图如下:
图3-1 系统总框架图
3.2 主要代码
void main() {
InitMPU6050();
delay(50);
while(1){
keyscan();
formString();
sendString();
delay(50); }
}
3.3 软件工具
3.3.1 KEIL程序编写工具
Keil 是一款用于8051单片机C51语言编程的集成开发环境,由德国Keil software公司开发,为C51语言编程与调试提供全新的开发环境,是8051单片机开发程序锁必须掌握的软件开发工具。
图3-3 KEIL工具图
STC_ISP.EXE其实就是给STC单片机下载程序的。当程序编译好以后需要把程序烧录到单片机中,这样才能让硬件功能得以实现,因此就需要一个传递信息的工具,而STC_ISP的功能就是这个,STC不仅能下载程序,还能进行串口检测,最小系统就是通过它进行调试的。当程序和串口调试好以后就可以烧录程序,单片机也就活了起来。
图3-4 STC下载工具图
3.3.3 labview工具
LabVIEW是一种用图标代替文本行创建应用程序的图形化编程语言。传统文本编程语言根据语句和指令的先后顺序决定程序执行顺序,而 LabVIEW 则采用数据流编程方式,程序框图中节点之间的数据流向决定了VI及函数的执行顺序。LabVIEW 提供很多外观与传统仪器(如示波器、万用表)类似的控件,可用来方便地创建用户界面。用户界面在 LabVIEW 中被称为前面板。使用图标和连线,可以通过编程对前面板上的对象进行控制。这就是图形化源代码,又称G代码。LabVIEW 的图形化源代码在某种程度上类似于流程图,因此又被称作程序框图代码。
4 系统测试
4.1 软件测试
软件测试主要是检测本设计功能是否都能实现以及设计的可执行性。测试软件主要是KEIL程序编辑器和STC程序下载器,通过代码的编写,将程序通过下载工具将程序烧录到单片机中,进而实现设计的功能。将生成的.Hex文件通过下载器烧写进单片机,如下图所示:
图4-1 Hex程序
在进行程序下载时,应注意波特率、串口号以及单片机型号的正确选择,否侧会因选择错误而无法将程序烧录进单片机中,最终导致设计功能不能实现,下载程序操作如下图所示:
图4-2 程序下载工具
硬件测试主要是对模块的功能进行检测,确保各模块都能正常的工作。陀螺仪主要由单片机控制,当其完成检测到有串口数据时,单片机通过串口发送的数据,labview接收到数据后,并进行相关处理,labview的数据图形实时对数据的显示。测试图如图4-3所示:
图4-3测试图
4.3 测试数据分析
在单片机通过接口传数据labview后,在labview中进行数据的分析,把各种数据分散开来,得到x加速度、x角速度、y加速度、y角速度、z加速度、z角速度的值,最后把得到的各种值实时显示在其的数据图形上面。以下是得到的数据结果。如图4-5所示:
图4-5 数据图形的值
5 总结
LabView作为一个专为测试测量设计的编程语言,使用了工程师们最熟悉的图形化的编程方式,能够帮助用户高效和快速的开发测试应用。串口是常用的计算机与外部串行设备之间的数据传输通道,通过USB串口总线与PC计算机组成 虚拟仪器系统, 是目前虚拟仪器的构成方式之一,它具有接口简单,使用方便的特点。本次设计为 PC与单片机串口通信,课题完成的工作总结如下:
(1)进行了系统的硬件和软件方面的设计,设计的主要内容包括USB串口的连接,STC12C5A60S2单片机连接和数据传输,Labview串口程序设计,C语言程序的设计等等。
(2)采用USB串口数据线连接PC机与单片机,用LabVIEW8.0作为开发环境,实现了上位机PC机与下位机单片机之间的串口通信。连接PC和单片机构成单片机应用系统, PC发送数据给单片机,并将数据返还回来,PC接收并验证返还的数据。
实践证明应用先进的虚拟仪器软件LabView,大大降低了串口通讯复杂程度,减小了软件设计的工作量 ,大大降低了投资成本。本次设计实现了LabView下的串口通信,达到了预期的目标。
然而,本次设计还有一定的不足之处。用LabView 软件强大的编辑功能,配合下位机智能仪器,能够实现数据的传输功能。在这次设计中,只是初步的研究了PC机和单片机之间的通信方法,实现简单的数据发送和接收。对于复杂数据的传输和更强大功能的开发还有待发掘。
[1] Tao FL,Zhang Z.Impacts of climate change as a func-tion of global mean temperature, maize productivity andwater use in China. Climatic Change,2011,105:
[2] Tokatlidis IS.Adapting maize crop to climate change.Agronomy for Sustainable Development,2013,33:5
[3] 张毅刚.单片机原理与应用设计(第二版)2015:6
[4] 安英奇.单片机的应用于发展趋势[C]. 时代教育,2014(2):6
[5] Jeffrey Travis.《LabVIEW for Everyone》
[6] 刘君华,贾惠芹,丁晖等.虚拟仪器图形化编程语言Labview教程[M] .西安:西安电子科技大学出版社,2001.10-43
[7] 王维维.单片机最小系统实现智能抢答器.《邢台职业技术学院学报》.2014:12
[8] 吴陈城.国内无线通讯模块的客户关系管理实践研究.《中国优秀硕士学位论文全文数据库》.2011:15
[9] 微控制器.Keil为嵌入式软件开发提供简化的开发环境[J]. 单片机与嵌入式系统应用,
2009(4):87-88.
附录A 程序代码
#include <stc12c5a60s2.h>
#include <math.h> //Keil library
#include <stdio.h> //Keil library
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
// 定义12单片机端口
#define DataPort P0 //LCD1602数据端口
sbit SCL=P1^0; //IIC时钟引脚定义
sbit SDA=P1^1; //IIC数据引脚定义
sbit LCM_RS=P2^0; //LCD1602命令端口
sbit LCM_RW=P2^1; //LCD1602命令端口
sbit LCM_EN=P2^2; //LCD1602命令端口
// 定义MPU6050内部地址
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
//定义类型及变量
uchar dis[6]; //显示数字(-511至512)的字符数组
int dis_data; //变量
//函数声明
void delay(unsigned int k); //延时 void lcd_printf(uchar *s,int temp_data);
//MPU6050操作函数
void InitMPU6050(); //初始化MPU6050
void Delay5us();
void I2C_Start();
void I2C_Stop();
void I2C_SendACK(bit ack);
bit I2C_RecvACK();
void I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void I2C_ReadPage();
void I2C_WritePage();
void display_ACCEL_x();
void display_ACCEL_y();
void display_ACCEL_z();
uchar Single_ReadI2C(uchar REG_Address); //读取I2C数据
void Single_WriteI2C(uchar REG_Address,uchar REG_data); //向I2C写入数据
//整数转字符串
void lcd_printf(uchar *s,int temp_data)
{
if(temp_data<0)
{
temp_data=-temp_data;
*s='-';
}
else *s=' ';
*++s =temp_data/10000+0x30;
temp_data=temp_data%10000; //取余运算
*++s =temp_data/1000+0x30;
temp_data=temp_data%1000; //取余运算
*++s =temp_data/100+0x30;
temp_data=temp_data%100; //取余运算
*++s =temp_data/10+0x30;
temp_data=temp_data%10; //取余运算
*++s =temp_data+0x30;
}
void SeriPushSend(uchar send_data)
{
SBUF=send_data;
while(!TI);TI=0;
}
//延时
void delay(unsigned int k)
{
unsigned int i,j;
for(i=0;i<k;i++)
{
for(j=0;j<121;j++);
}
}
void Delay5us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
//I2C起始信号
void I2C_Start()
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
//I2C停止信号
void I2C_Stop()
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
void I2C_SendACK(bit ack)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
//I2C接收应答信号
bit I2C_RecvACK()
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
//向I2C总线发送一个字节数据
void I2C_SendByte(uchar dat)
{
uchar i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
I2C_RecvACK();
}
//从I2C总线接收一个字节数据
uchar I2C_RecvByte()
{
uchar i;
uchar dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
//向I2C设备写入一个字节数据
void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //内部寄存器地址,
I2C_SendByte(REG_data); //内部寄存器数据,
I2C_Stop(); //发送停止信号
}
//从I2C设备读取一个字节数据
uchar Single_ReadI2C(uchar REG_Address)
{
uchar REG_data;
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=I2C_RecvByte(); //读出寄存器数据
I2C_SendACK(1); //接收应答信号
I2C_Stop(); //停止信号
return REG_data;
}
//初始化MPU6050
void InitMPU6050()
{
Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
Single_WriteI2C(SMPLRT_DIV, 0x07);
Single_WriteI2C(CONFIG, 0x06);
Single_WriteI2C(GYRO_CONFIG, 0x18);
Single_WriteI2C(ACCEL_CONFIG, 0x01);
}
//合成数据
int GetData(uchar REG_Address)
{
uchar H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H<<8)+L; //合成数据
}
//在1602上显示10位数据
void Display10BitData(int value,uchar x,uchar y)
{ uchar i;
// value/=64; //转换为10位数据
lcd_printf(dis, value); //转换数据显示
for(i=0;i<6;i++)
{
SeriPushSend(dis[i]);
}
}
void init_uart()
{
TMOD=0x21;
TH1=0xfd;
TL1=0xfd;
SCON=0x50;
PS=1; //串口中断设为高优先级别
TR0=1; //启动定时器
TR1=1;
ET0=1; //打开定时器0中断
ES=1;
EA=1;
}
void main()
{
delay(500); //上电延时
// InitLcd(); //液晶初始化
init_uart();
InitMPU6050(); //初始化MPU6050
delay(150);
while(1)
{
Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度
Display10BitData(GetData(ACCEL_YOUT_H),7,0); //显示Y轴加速度
Display10BitData(GetData(ACCEL_ZOUT_H),12,0); //显示Z轴加速度
Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度
Display10BitData(GetData(GYRO_YOUT_H),7,1); //显示Y轴角速度
Display10BitData(GetData(GYRO_ZOUT_H),12,1); //显示Z轴角速度
SeriPushSend(0x0d);
SeriPushSend(0x0a);//换行,回车
delay(100);
}
}