说明
近年来随着计算机在社会领域的渗透和大规模集成电路的发展,单片机的应用正在不断地走向深入,由于它具有功能强,体积小,功耗低,价格便宜,工作可靠,使用方便等特点,因此越来越广泛地应用各个领域. 51单片机是应用最广泛的一种。该系列单片机是应用最广泛的8位单片机之一,其代表型号是ATMEL公司的AT89系列。本项目采用STC89C52单片机。
在小车的实车行车中,需要大量的数据才能得到客观准确的结论。因此在开发调试过程中有一个使用简单,方便可靠的调试系统显得越来越重要。本项目基于nrf24L01进行无线传输,并基于VB进行上位机开发。
本项目整体架构就是以蔽障小车为载体对调试系统进行了开发。小车通过红外对管感应前方的障碍,利用各轮之间的差速实现转弯。无线采集系统会对实车行驶中的各项参数进行采集。
实物图
硬件设计
1、主控电路板
小车利用三个(左、中、右)红外对管检测障碍物,并以STC89C52单片机为控制芯片控制电动小汽车的速度及转向,从而实现自动避障的功能。智能避障是基于红外传感系统,采用红外传感器实现前方障碍物检测。
2、参数
A. 充电输入电压 10-12V。
B. 充电电压最大8.4V 。
C. 5V 输出最大电流 2A 。
D. 电机驱动电流最大 1.2
3、主要功能
A.蔽障。
B.无线数据采集。
软件设计
程序对单片机端口以及nrf24L01无线模块进行初始化设置,然后启动定时器。如果定时器发生中断则执行中断程序。采集传感值并改变PWM值通过各轮的差速实现转向蔽障的功能。如果没有发生中断请求则进行无线发送数据。
无线调试系统设计
在实车的调试和结论分析过程中,需要大量的数据才能得到全面正确的结论。这就需要一个数据采集系统可以实时、准确的采集到相应的参数。无线数据采集系统可以满足这一要求。其相比于一般的数据采集系统在硬件方面得到很大程度的简化且使用可靠性更高。一般的数据采集系统是通过传感器将捕捉的现场信号转换为电信号,经模数转换器ADC采样、量化、编码后,为成数字信号,存入数据存储器,送给微处理器,通过串口方式送到上位机进行处理。无线数据传输系统就是这样一套利用无线手段,将采集的数据由测量站发送到主控站的设备。无线数据传输,它不仅简单、方便而且还成本低廉,受到普遍欢迎,开发的潜质也是有目共睹的。
nRF24L01是由NORDIC生产的工作在2.4GHz~2.5GHz的ISM 频段的单片无线收发器芯片。无线收发器包括:频率发生器、增强型“SchockBurst”模式控制器、功率放大器、晶体振荡器、调制器和解调器。输出功率频道选择和协议的设置可以通过SPI 接口进行设置。几乎可以连接到各种单片机芯片,并完成无线数据传送工作。
极低的电流消耗:当工作在发射模式下发射功率为0dBm 时电流消耗为11.3mA ,接收模式时为12.3mA,掉电模式和待机模式下电流消耗更低。
1.无线数据采集系统整体框架
无线数据采集系统将各路传感器的信号、端口值经STC89C52单片机的转化处理通过nRF24L01的发送端将数字量发送给nRF24L01的接收端。nRF24L01的接收端接收到数据后,与上位机通信,上位机将数据经再处理后进行显示、保存等。
2.无线数据采集系统下位机设计
2.1下位机硬件设计
nRF24L01发送模块有两种:1.PCB板内置天线;2.模块外置天线。由于无线的传播距离的要求不是很远,采用了内置天线的nRF24L01发送模块,如下图所示。
2.2下位机软件设计
下位机的软件设计可分为四大部分:
(1). MISO、IRQ、CSN等端口状态设置
(2).nRF24L01通道、发射速率、中断响应等参数设置
(3). nRF24L01发送函数设计
nRF24L01发送函数:
void nRF24L01_TxPacket(uint16 *Send_dat)
{
NRF24L01_WriteReg(STATUS,0x7E); //清除中断位
CE_L(); //StandBy I模式
lot_write(0xA0-32,Send_dat,32); //装数据
CE_H(); //置高CE,激发数据发送
while(IRQ_V()==1) ;
CE_L();
NRF24L01_WriteReg(STATUS,0x7E); //清除中断位
}
此函数可以实现每次发送32字节。调用简单,只需将需要发送的数组写入函数的指针形参中即可。
(4).发送控制策略设计
发送控制策略:
方式一:在PIT中断中执行发送命令
方式二:在main主函数中执行发送命令
比较两种控制策略,方式一的发送数据的实时性,可靠性都优于方式二。但在实际的实验中发现,发送命令的执行会对其他PIT中断里的函数执行产生影响,不时发生程序跑飞的情况。鉴于在main主函数中执行发送命令,其程序运行的稳定性高,且发送数据的实时性和可靠性也可以满足实验要求,最终采用了方式二。
发送控制部分程序代码:
for( ; ; )
{
nRF24L01_TxPacket(Data);
Delay(100);
}
3.无线数据采集系统上位机设计
3.1、上位机软件设计
(1)无线数据采集处理软件的界面(未运行)如图所示
(2)无线数据采集处理软件的界面(运行)如图所示
本软件基于VB6.0编写,其最大的特点在于:可视化强、使用方便。用户在使用本软件前应只需将USBnRF24L01接收模块
插入usb端口,然后打开本软件,点击连接按钮。USBnRF24L01接收到的数据会自动显示在接收区内,并自动将数据以xls格式保存在excel文档中。点击保存按钮可以将数据以txt格式保存在文本文档中,方便后期对数据的分析处理。
(2)无线数据采集处理软件程序设计
①.实现无线的数据采集功能有两种:
方法一:基于普通nRF24L01接收模块。
硬件:普通nRF24L01接收模块,单片机,max232电平转换,串口线。
软件:基于vb自带的MSComm控件编程。
方法二.基于USBnRF24L01接收模块
硬件:USBnRF24L01接收模块。
软件:基于动态链接库Netusb.dll
比较两种方法,方法一基于vb自带的MSComm控件,编程难度相对简单,但硬件复杂,并且在发送频率高的情况下,易出现缓存溢出,数据丢失和卡死现象。方法二基于USBnRF24L01接收模块,硬件简单,使用方便。使用DLL文件可以在多个应用程序共享代码和数据。综合各方面,最终选定基于USBnRF24L01接收模块进行上位机开发。
②.程序主流程图(见图)
③.数据处理模块
I.进制转化
从缓存区读取得到的数据为二进制数
据可通过vb内置函数Hex(data)实现二进
制到十六进制数据的转换。但是无法直接
二进制到十进制的转化。于是,编写了十
六进制到十进制的转化函数HEX_to_DEC(data)
II.成列存储
每六个数据为一行所以通过求余运算,
对6求余为0时,则加换行符vbCrLf。这样就可形成6列的数据块。
III.数据再处理策略
由于对每一列数据的再处理不同,所以不能采用一个For循环对一组数据进行读取。最终采取了三个For循环来进行数据处理。
④.数据保存模块
I. xls格式保存
Excel文档中每一个单元格都有相应的坐标,所以只需将相应的数据置入相应的单元格即可。
II.txt格式保存
需要先将一个文本文档打开,然后写入软件接收区的文本,最后关闭文档。完成保存。
⑤.通信模块
建立Netusb模块的bas文件。在bas文件中申明Netusb.dll动态链接库。实现USBnRF24L01接收模块与上位机的通信
⑥.程序部分代码
数据捕捉程序部分代码
Result = NetusbGetData_Noblock(buffer(0), datalen(0))
数据处理程序部分代码
s = " " & Hex(buffer(i))
sjz = HEX_to_DEC(s)
zzf = sjz / 10#
数据excle保存程序部分代码
ExSheet.Cells(count, j) = Text8.Text
ExSheet.Cells(count, d) = Text9.Text
数据文本显示程序部分代码
On Error Resume Next
ActiveWorkbook.SaveAs ("e:\data.xls")
If (i + 1) Mod 6 = 0 Then
Text4.Text = Text4.Text + "" + sjz + " " + vbCrLf
Else
Text4.Text = Text4.Text + "" + sjz + " "
End If
通信模块部分代码
Public Declare Function NetusbGetNumDevices Lib "Netusb.dll" Alias "_NetusbGetNumDevices@8" (ByVal vid As Integer, ByVal pid As Integer) As Long
Public Declare Function NetusbOpen Lib "Netusb.dll" Alias "_NetusbOpen@12" (ByVal deviceIndex As Long, ByVal vid As Integer, ByVal pid As Integer) As Byte
4.无线采集的数据
小车程序代码
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式
#define EN_AA 0x01 // 自动应答功能设置
#define EN_RXADDR 0x02 // 可用信道设置
#define SETUP_AW 0x03 // 收发地址宽度设置
#define SETUP_RETR 0x04 // 自动重发功能设置
#define RF_CH 0x05 // 工作频率设置
#define RF_SETUP 0x06 // 发射速率、功耗功能设置
#define STATUS 0x07 // 状态寄存器
#define OBSERVE_TX 0x08 // 发送监测功能
#define CD 0x09 // 地址检测
#define RX_ADDR_P0 0x0A // 频道0接收数据地址
#define RX_ADDR_P1 0x0B // 频道1接收数据地址
#define RX_ADDR_P2 0x0C // 频道2接收数据地址
#define RX_ADDR_P3 0x0D // 频道3接收数据地址
#define RX_ADDR_P4 0x0E // 频道4接收数据地址
#define RX_ADDR_P5 0x0F // 频道5接收数据地址
#define TX_ADDR 0x10 // 发送地址寄存器
#define RX_PW_P0 0x11 // 接收频道0接收数据长度
#define RX_PW_P1 0x12 // 接收频道0接收数据长度
#define RX_PW_P2 0x13 // 接收频道0接收数据长度
#define RX_PW_P3 0x14 // 接收频道0接收数据长度
#define RX_PW_P4 0x15 // 接收频道0接收数据长度
#define RX_PW_P5 0x16 // 接收频道0接收数据长度
#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置
#define READ_REG 0;//读寄存器命令
#define WRITE_REG 0X20;//写寄存器命令
#define RD_RX_PLOAD 0X61;//读取接收数据命令
#define WT_TX_PLOAD 0XA0;//写代发数据命令
#define FLUSH_TX 0XE1;
#define FLUSH_RX 0XE2;
#define NOP 0XFF;//空命令
//标志位检测函数
uint NRF24L01_TxAddr[5];//={0x34,0x43,0x10,0x10,0XE7};
uint NRF24L01_RxAddr[5];//={0x34,0x43,0x10,0x10,0XE7};
uint Rev_dat[32]={0,0,0,0,0,0,0,0};
uint NRF24L01_TxAddr[5]={0x34,0x43,0x10,0x10,0XE7};
uint NRF24L01_RxAddr[5]={0x34,0x43,0x10,0x10,0XE7};
sbit Lsingal=P1^0;
sbit Msingal=P1^1;
sbit Rsingal=P1^2;
sbit CSN=P3^3;
sbit MOSI=P3^4;
sbit SCK=P3^1;
sbit MISO=P3^2;
sbit IRQ=P3^5;
sbit CE=P3^0;
void SCK_H() {SCK=1;}
void MOSI_H() { MOSI=1;} //PA3
void CSN_H() {CSN=1;} //PA1
void CE_H() {CE=1;} //PA0
//置低函数
void SCK_L() {SCK=0;}
void MOSI_L() {MOSI=0;}
void CSN_L() {CSN=0;}
void CE_L() {CE=0;}
void SpiWrite(uint send)
void lot_write(uint addr, const uint *str1,uint len)
void nRF24L01_TxPacket(uint *Send_dat)
void NRF24L01_Unit(void)
{
PORT_SET();//必须先进行端口状态设置,才能进行各种参数的配置
CE_L(); // chip enable
CSN_H(); // Spi disable
SCK_L(); // Spi clock line init high
lot_write(TX_ADDR,NRF24L01_TxAddr,5);/// 写发送地址
lot_write(RX_ADDR_P0,NRF24L01_RxAddr,5);/// 写接收地址
NRF24L01_WriteReg(EN_AA,0x00);//0自动应答
NRF24L01_WriteReg(EN_RXADDR,0x01);//设置通道
NRF24L01_WriteReg(RF_CH,0);
NRF24L01_WriteReg(RX_PW_P0,32); //设置接收数据长度,本次设置为32字节
NRF24L01_WriteReg(RF_SETUP, 0x0f);
NRF24L01_WriteReg(CONFIG, 0x0e);
}
/************************************************
* *
* IO口初始化 *
* *
************************************************/
void io_Init(){
P2=0x00;
Lsingal=1;
Msingal=1;
Rsingal=1;
}
/************************************************
* 晶振频率:11.0592 *
* T0:定时器 频率:100k *
* T1:计数器 *
************************************************/
void timer_Init(){
TR0 = 0;
TMOD = 0x01;
TL0 = 0xD8; //T0初值=2^16-定时时间/机器周期 机器周期=12*晶振
TH0 = 0xEF; // =55553
TR0 = 1;
IE =0x8A;
}
// 延时子程序 ms*250
void delayms(uchar ms)
{
uchar i,j;
while(ms--)
{
for(i = 0; i < 120; i++){
for(j = 0; j < 250; j++);
}
}
}
//10ms
void delayten(uchar ms)
{
uchar i,j;
while(ms--)
{
for(i = 0; i < 120; i++){
for(j = 0; j < 10; j++);
}
}
}
main (){
P2=0xAA;
io_Init();
NRF24L01_Unit();
timer_Init();
for(;;){
nRF24L01_TxPacket(Rev_dat);
delayten(1);
}
}
/*************************************************
* 1: 定时器0的中断号 *
* 电机频率:1k *
* 占空比:dutyRatio *
*************************************************/
void pwmInter(void) interrupt 1{
uint lsgn=1;
uint msgn=1;
uint rsgn=1;
uint asgn=12;
lsgn=Lsingal;
msgn=Msingal;
rsgn=Rsingal;
EA=0;
TH0 = 0xD8;
TL0 = 0xEF;
asgn=4*lsgn+2*msgn+rsgn;
/*if(msgn==0){P3=0x99;}
else if(lsgn==0&&rsgn==1) {P3=0x99;}
else if(lsgn==1&&rsgn==0) {P3=0x66;}
else if(lsgn==0&&rsgn==0) {P3=0x99;}
else {P3=0xAA;} */
switch(asgn)
{
case 7:
P2=0xAA;
break;
case 6:
P2=0x44;
while(Rsingal==0);
delayms(20);
break;// 01000100
default:P2=0x11;
while(Lsingal==0||Msingal==0);
delayms(20);
break; // 00010001
}
Rev_dat[0]=lsgn;
Rev_dat[1]=msgn;
Rev_dat[2]=rsgn;
Rev_dat[3]=P1;
Rev_dat[4]=P2;
Rev_dat[5]=P3;
// nRF24L01_TxPacket(Rev_dat);
EA=1;
}