利用plc的高速计数模块进行超声波测距实验
―――――微型控制计算机暑期设计实验报告
082039140程稳
利用51单片机来驱动超声波模块测距,是一件很容易的事,只需要结合定时中断和外部中断,利用12M或更高的晶振频率即可精确获取从发射到接收到超声波之间的时间,平均1ms对应 3.4cm的行程,本GE比赛设计需要物位测量的最大距离是30cm,即需要30*2/3.4=17.64ms,而GE PAC RX3i的PME软件梯形图程序得扫描周期2ms以上,就算是最快的定时节点也有1ms,所以若直接用PLC的普通离散量输入模块IC694MDL654输入节点来测量接收到超声波回波的时间的误差为1ms,误差距离3.4/2=1.7cm,结果自然不理想,更严重的问题在于PLC该模块无硬件中断响应功能,是不能测电平宽度的。
总之PLC的IO口工作在低速模式下是难以胜任高速测量任务的,但可喜的是GE PLC 的高速计数模块HSC304能处理2MHZ的信号,但仍无硬件中断功能。
于是想能否干脆把单片机测出的电平时间数据通过串口发送给PLC,我也试着这样连线测试,不过PLC串口的使用不像单片机这么简单,没有相关资料,PLC内部寄存器找不到PLC从单片机接收的数据。
于是仍决定放弃此方案,回到高速计数模块。
再认真阅读此模块配置信息和实验调试后,发现其可以测量出外部信号频率,于是想既然PLC无法直接测电平宽度,那干嘛不测量频率,有了频率自然有周期,有周期自然有电平宽度!
利用plc的高速计数模块检测超声波测距仪的信号接收端的频率,正常情况下应使用频率直接求得周期接而来计算时间,但由于实际测得这样根本很难实现,所以直接测频率,并利用示波器查看该频率的波形,并修改程序使得在所测距离变化的情况下,一周期内的低电平保持不变(高电平所持续的时间表示超声波从发出到接收到所经历的时间,低电平是延时,为了使得波形正常),然后测出频率及其所对应的距离。
以下是用虚拟示波器测出的超声波模块在不同距离测量回波接收脚电压波形:
利用matlab 进行曲线拟合(拟合方法是:输入频率及所测距离利用plot (f ,d ,“*”)函数绘制图像,并利用tool 下的basic fitting 选择拟合方式进行拟合并得到曲线的公式)从而利用频率计算距离
d=[64.5 52.6 46.7 39.5 26.2 23.3 17.3 12.9 6.4 3]%用卷尺测量出探头与障碍物距离
x=[168 177 183 190 204 209 216 222 231 237 ]%PLC 高速计数模块HSC304测出的频率
plot(x,d,'*')
MA TLAB二次曲线拟出的公式如下:
y = p1*x^2 + p2*x +p3
Coefficients:
p1 = 0.0016329
p2 = -1.2635
p3 = 244.32
Norm of residuals =
1.6316
实验中所遇到的几个问题
测距的误差与反射超声波的物质有关及超声波测距仪的摆放位置有关:
(1)当反射物质为水时,超声波测距仪在水桶的外面,这时所测的距离与实际相差很大,当放在桶内时则测量准确
(2)当用海绵作为反射物时则测量不精准
测量的精度与单片机延时有关系,并与plc高速计数模块的扫描时间有关系,扫描时间越长越准确,但时间越长更新的速度越慢,所以时间要设置合适,一般在1~2s 建议取1.5s
这样的精度是 5mm
鉴于此实验的创意改造,即不需要测出电平宽度而直接测出频率,故也只需要求51单片机做出的物位传感器不输出时间串行数据信号,而只输出频率信号。
于是单片机的编程设计思想是:触发IO口输出触发信号给超声波模块触发端启动模块发出超声波信号工作后,模块接收到回波后的回波脚后的电平变化信号被单片机另一普通IO口检测出后(电平由低变高),延时5~10ms(低电平),再启动新一轮触发,如此下去,单片机连接模块的回波脚的IO口就是一高电平与距离成正比,低电平不变的矩形波,如上面的虚拟示波器波形。
据此思路,单片机程序如附录所示。
附:超声波的程序(单片机给发射信号)
#include<reg51.h>
#define LED P1
#define SCAN P2
#define uchar unsigned char
#define uint unsigned int
sbit TX=P3^4;
sbit RX=P3^2;
bit flag=0;
bit flag_out=1;
uint fre=0;
void delay_25us(uchar ms)
{
uchar i,j;
for(i=0;i<ms;i++)
for(j=0;j<3;j++);
}
void delay_1ms(uchar ms)
{
uchar i,j;
for(i=0;i<ms;i++)
for(j=0;j<120;j++);
}
void INIT_measure(void) //初始化
{
IE=0X89;//1000 1001 EA T1 INT0 计数器/外部中断0
TMOD=0X10;//0001 0000 CT=0,内部计时器T1采用MODE1 16位定时IT0=1;// 负边缘触发
flag=0;//设INT0中断标志
}
void INT0_RX(void)interrupt 0 //外部中断0
{ uint distance=0;
//EX0=0;//不能关闭INT0中断,神句!!!
flag=1;
}
void TX_measure() //发射超声波
//EX0=1;//开启INT0中断
TX=1;//发射超声波
delay_25us(1);//至少9us
TX=0;//停止发射
}
void main()
{
INIT_measure();
while(1)
{
TX_measure();//发射超声波
RX=1;
delay_25us(10);
EX0=1;//开启INT0中断
flag=0;
while(!flag);//等待接受到返回信号
delay_1ms(10);
}
}。