单片机原理与接口技术课程设计单片机与超声波测距.docx
《单片机原理与接口技术课程设计单片机与超声波测距.docx》由会员分享,可在线阅读,更多相关《单片机原理与接口技术课程设计单片机与超声波测距.docx(21页珍藏版)》请在冰点文库上搜索。
![单片机原理与接口技术课程设计单片机与超声波测距.docx](https://file1.bingdoc.com/fileroot1/2023-7/9/b39ee744-028d-4128-aae1-931373e87e13/b39ee744-028d-4128-aae1-931373e87e131.gif)
单片机原理与接口技术课程设计单片机与超声波测距
“单片机原理与接口技术”课程设计
项目结题报告
项目名称:
单片机与超声波测距
组别:
难度系数:
中等选题
项目成员:
学院名称:
填写日期:
2018年1月2日
指导老师:
2018.1.2.
一、项目团队成员及分工简介
团队成员:
姓名
性别
学号
联系方式
本人签名
具体分工
本小组组长,项目牵头人,主程序设计与编写。
项目的主要架构,各个功能的设计与实现都是由他完成,同时也安排分配组员人力,组织会议。
后期对于超声波设备的选型与采购,以及对于设备的调试也是由他完成,是小组的重要技术人员。
辅助组长编程,文案资料整理。
文案资料整理,查找阅读超声波设备的资料,帮助组长生成字模。
二、项目方案设计
(一)项目预期要求
项目设计书上有如下的要求:
利用超声传感器回波,实现距离检测,液晶屏上显示被测距离。
因此,我的设想是,能够选到合适的元器件,我们在单片机上使能一个输入管脚和一个输出管脚。
在输出管脚触发一个命令之后,可以把超声波信息以某种形式传递回单片机,再进行数据处理,最后显示出来。
然后除了基本的测距功能之外,我还想要做出一些额外功能,最大化我们项目的实用性。
因此,我想出了测距模式,调试模式和计时模式三大基本功能。
测距模式就是最简单的超声波测距并显示的功能,在操作简单的前提下尽可能直观的看到被测距离的各项信息。
计时模式是我想要的一个附加功能,用于除了测距之外,满足计时需求,在某些场合下也可以发挥作用。
液晶屏的显示也是一项难点,怎样才能把复杂的数字直观的显示出来呢,怎样把数字字母乃至汉字以点阵的模式显示出来,而且可以实时变化,有各个不同的界面。
我想要的,预期的项目成型是这样的,可以简单的按键点按操作,在按键按下后立刻进行数据处理,然后最后把测量信息完整美观的显示在液晶屏幕上面。
除了可以看见的人机交互部分,内部的算法我也希望可以做到直观而且准确,精准。
同时,额外功能也要经可能的准确易用,最好可以一个按键就对应一个功能,然后在不同的功能间切换的同时,屏幕上也可以有所指示,可以让用户用的舒服,也要尽可能的降低用户的学习成本,让使用者可以快速上手。
至此,我们项目的几个重难点已经基本成型,现在我开始逐个讲述。
(二)项目方案设计
(1)硬件选型
我在互联网进行了搜索,找到了满足我要求的超声波模块:
HC-SR04P。
HC-SR04P是一款宽电压工作的超声波测距模块。
模块外形尺寸如下图
它的各项参数非常契合我们的需求,也被用在很多单片机的距离传感上。
从图1可以看到它有四个管脚,分别为“Trig”,”Echo”,”Vcc”,”Gnd”。
Vcc接电源,可以是5V-3.3V,Gnd接地,这些都不再赘述。
Trig为输入管脚,需要输入一个高电平作为触发信号。
Echo为输出管脚,在输入Trig高电平后,超声波模块开始工作,发出超声波信号并接收,然后将
超声波的回响时间以高电平的形式在Echo中输出。
高电平持续的时间就是超声波从发射到返回的时间。
图3
(2)功能设计:
计时器
至此,现在我们需要在单片机编程。
为了得到我们所需的测距值,首先要能够测算出回响信号的时长。
因此我们需要一个单片机内部的计时器功能。
在这里我使用了一个溢出中断的计时器,使它每秒触发100000次中断,而每次触发中断的动作是使一个全局的变量i自增一次。
这样的话,计时值就是这个变量的值乘以十微秒。
之后我又另设了一个全局变量x来控制计时器的开关,既x被置1的时候,i才会开始它的自增,而x被重新置0时,i便停止自增,然后再读取i值。
被读取到的i值根据声速再换算到距离,有距离y=1.73*i毫米。
基本的四则运算也不再赘述了。
(3)LED屏幕显示
LED屏幕显示我们采用了例程uc1701的显示方案,将例程中的屏幕显示资源加入我们的文件之后,可以在屏幕上进行中文与数字的显示。
因为我希望可以显示到小数点后两位的值,用了一些取整作差的方法,可以在程序段中体现出来。
在屏幕的显示上,我做了三行的显示。
第一行显示被测算的距离,第二行显示超声波的回响时间,以便用来进行更精确的计量。
第三行显示的则是目前单片机的运行模式,除了简单的单次测距模式之外,我还做了一个用于内部调试的测试模式,用一个按键的按下时间来模拟超声波的回响时间,以便我在没有安装超声波模块的时候也能进行程序的编写和查错。
还有一个计时器功能,算是一个附加功能,可以像一个普通秒表一样进行计时,只不过计时是精确到微秒级别的,呵呵。
(4)交互设计
人机交互是这个项目很重要的一环。
我希望在交互尽可能简单的前提下,能做到完整的功能表现。
我使能了三个按键,三个LED灯,分别用于不同的功能。
蓝色LED灯与按键k2对应,按下后单片机向模块发出一个短暂的高电平信号,同时蓝色LED灯亮起表示触发信号成功发出。
绿色LED灯与回响信号相对应,绿灯亮起则说明机器成功接收到了模块发来的回响信号,并且在屏幕上显示回响时间与测算的距离。
最后红色LED灯与计时模块相对应,红灯亮起说明模块正在计时,而K3键按下一次表示计时开启,再按下一次表示计时结束,输出计时值。
(5)方案筛选
对于使用外设硬件的项目,特别是我们是先进行硬件筛选,确定硬件之后再进行的项目开发,方案是直接拿出的,也会很难拿出备选方案。
但是实际上我们在很多个模块上都尝试过很多方案,最后拿出我们目前能做到效果最好的产品。
现在我想谈谈一个被放弃的功能,实时测距模式。
或者叫雷达模式。
当时的设想是,开启这个模式后,单片机开始自动循环测距,你可以通过按键设置一个检测值,当距离检测到低于此值时,开始量报警灯报警,并且在屏幕上实时显示测量距离。
这个功能我花了很多心思,包括程序设计,交互设计,甚至字模也都做好了,但是实际上到了超声模块的时候,还是发现了不少问题。
首先是屏幕的刷新响应时间很长,而且反复刷新屏幕的话效果很不理想,显示效果很差,而且超声波模块有一个最短工作周期,如果连续测距的话精度很差。
实际上,对于距离实时监测,红外线或者是其它传感器要更适合一点,超声传感器比较适合单次的测距,所以这个功能被忍痛放弃了,最后拿出的成品保留了单次测距与计时功能,还有一个用于开发的调试模式。
本来调试模式是准备在开发完成之后移除的,但是后面发现这个功能其实对于添加新功能或者新模块调试很有帮助,于是保留了下来。
现在我来说说这次的最终版用到了哪些模块。
单片机本身的模块有
1.单片机的计时器模块
2.单片机的LED以及按键模块
3.单片机后方的两个管脚PF2和PF3,以及下方的供电接口和Ground接口
4.单片机的液晶显示屏模块
然后拓展模块是外设的超声波传感器模块
(三)项目完成情况
对于项目完成情况来说,基本是圆圆满满,最终效果和预期一样,甚至可以说是超过了预期,可喜可贺。
首先是·计时器功能,经过多次调试之后,计时器很精准,我们和自己手机的秒表功能试了一下基本一致。
在保证了精准的计时之后,各个功能实际上都是围绕计时功能的,所以调试的都很顺利。
最早完成的是测试模式。
实际上在程序还处在编写阶段的时候,我们的超声波模块并没有到货,实际上我是完全用一个按键来模拟了回响信号的上升沿,然后以此来进行调试。
但是要注意的是,因为声速其实很快,所以即使是按键只是感觉迅速的按了一下,显示的模拟距离都是很大的,而且基本是超出量程的,所以在测试模式下,模拟的距离并不是一个显示完整的数据,但是可以借此看到按下键的时间长度。
在超声波模块拿到之后,一开始并不能正常工作。
实际上虽然有回响信号,但是与其说是一段上升沿,其实只有一次小小的脉冲,可能每次的持续时间都不到10微秒。
这个是让我很焦头烂额的一件事,甚至是怀疑设备出现了故障。
后面发现电源由3.3V接到5V之后就突然完全没问题可以工作了。
这个其实真的是让我喜出望外。
我发现外设模块其实在3.3V下无法正常工作(我用电表测试过电源电压,确实是是3.3V),这一点和产品说明上存在较大的出入,也希望厂商能够注意到这件事。
在5V的工作电压下,超声波传感器可以完全正常工作,测试距离经过我们测量也基本误差在5mm之外。
但是超声波还是会受到环境的影响,比如有噪声和被测物表面不平整都会使测量值有明显偏差。
有时会出现收不到回程声波的情况,这时单片机上的一个白色指示灯会亮起,提示接受到的上升沿有异常,显示值也会大大超过量程。
计时模式工作平稳,计时正常,可喜可贺。
三、项目具体设计与实施
1、总体设计
软件模块
1.单片机的计时器模块:
计时器模块分为计时器的使能,设置与外部中断部分的编写。
计时器的数据被其它各个模块调用修改。
2.单片机的LED以及按键模块:
LED以及按键模块,使能之后进行调用。
3.单片机后方的两个管脚PF2和PF3,以及下方的供电接口和Ground接口:
PF2和PF3管脚进行软件使能,供电与Ground接口直接连接单片机上已有管脚。
4.单片机的液晶显示屏模块:
液晶屏显示模块调用Uc1701显示,汉字显示通过字模生成软件进行生成。
硬件模块
1.超声波测距模块:
其电源管脚与单片机电源接口连接,GND接口与单片机Ground接口相连,触发管脚连接单片机PF2管脚,回响管脚连接单片机PF3管脚。
程序流程图(以测距模式为例)
2、详细模块设计
(1)单片机计时器模块
单片机的计时器模块分成两个部分,包括
主程序内的使能
ui32Period=(SysCtlClockGet())/100000;
TimerLoadSet(TIMER0_BASE,TIMER_A,ui32Period-1);
IntEnable(INT_TIMER0A);
TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
IntMasterEnable();
TimerEnable(TIMER0_BASE,TIMER_A);
外部中断
inti=0;
intx=0;
voidTimer0IntHandler(void)
{
TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
if(x==1)
{i++;}
}
在主程序中,计时器被设置为每秒触发十万次中断,而在中断程序中,每次中断都会带来一次变量i的自增。
而变量x作为自增的开关,控制了计时器的使用和暂停。
这样的话,就形成了一个可以通过置x的值来控制计时的全局计时器,而且可以很便利的被其它程序读取计时值。
(2)单片机LED与按键模块
主程序内的使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
HWREG(GPIO_PORTF_BASE+GPIO_O_LOCK)=GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE+GPIO_O_CR)|=0x01;
HWREG(GPIO_PORTF_BASE+GPIO_O_LOCK)=0;
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_0,1<<0);//LEDPF0
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE,GPIO_PIN_4);
GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_4,1<<4);//LEDPA4
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE,GPIO_PIN_6);
GPIOPinWrite(GPIO_PORTD_BASE,GPIO_PIN_6,1<<6);//LEDPD6
GPIOPinTypeGPIOInput(GPIO_PORTA_BASE,GPIO_PIN_2);GPIOPinTypeGPIOInput(GPIO_PORTA_BASE,GPIO_PIN_3);
这是各个按键与LED灯的使能,不做赘述。
按键的状态检测
unsignedcharnowkeybutt=1;
unsignedcharlastkeybutt=1;
nowkeybutt=GPIOPinRead(GPIO_PORTA_BASE,GPIO_PIN_2);if((lastkeybutt!
=0)&&(nowkeybutt==0))
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,1<<2);//PF2
Delaynms(25);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0<<2);//PF2
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_0,0<<0);//pf0
}
if((lastkeybutt==0)&&(nowkeybutt!
=0))
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_0,1<<0);//pf0
}
lastkeybutt=nowkeybutt;
上面这是测距按钮的按键状态检测的程序段,其它按键检测的方式都与它一样。
我们通过两个变量来记忆和比对按键的当前置位与上次置位,来获取上升沿和下降沿,并且在各个沿中施加动作。
这个方法来自吴老师的课程,非常的巧妙,我也很喜欢这个方法。
(3)管脚PF2,PF3对应的Trig与Echo接口
主程序内的使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_2);//pf2,tri
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0<<2);//pf2,tri
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_3);//pf3,eco
在使能中,触发管脚PF2设置为了输出管脚,回响管脚PF3设置为了输入管脚。
回响的状态检测与动作
unsignedcharnowkeyeco=0;
unsignedcharlastkeyeco=0;
nowkeyeco=GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_3);//eco
if((lastkeyeco==0)&&(nowkeyeco!
=0))
{
x=1;//eco
GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_4,0<<4);//pA4
}
if((lastkeyeco!
=0)&&(nowkeyeco==0))
{
x=0;
UC1701Init(60000);
UC1701Clear();
UC1701ChineseDispaly(0,0,2,HZ2);
UC1701ChineseDispaly(1,0,2,HZ1);
UC1701ChineseDispaly(0,12,2,MM);
UC1701ChineseDispaly(1,12,2,US);
UC1701ChineseDispaly(3,8,5,HZ5);
UC1701DisplayN(1,4,i*10);
UC1701DisplayN(0,4,y);
UC1701CharDispaly(0,9,".");
UC1701DisplayN(0,10,y*100-((int)y*100));
i=0;
GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_4,1<<4);//pA4
}
lastkeyeco=nowkeyeco;
y=1.73*i;
回响的上升下降沿检测与按键的检测大致相同,只不过初始位与置位位与按键完全相反。
在检测到回响的上升沿时,计时器开关x被置1,开始计时。
在检测到下降沿时,x置0,停止计时,在屏幕上显示数据之后,将计时器的计数值清零。
PF2的动作在上文“按键的状态检测”中出现了,在按键被按下时,PF2短暂的生成一个短脉冲。
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,1<<2);//PF2
Delaynms(25);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0<<2);//PF2
这里的“Delaynms(25);”是一个延时程序:
voidDelaynms(unsignedintdi)
{
unsignedintda,db;
for(da=0;dafor(db=0;db<10;db++);
}
值置为25可以得到满足要求的长度40us的短脉冲。
(4)单片机液晶显示屏模块
液晶显示屏模块使用了UC1701的液晶显示模块,在头文件中添加#include"uc1701.h"
UC1701Init(60000);
UC1701Clear();
UC1701ChineseDispaly(0,0,2,HZ2);
UC1701ChineseDispaly(1,0,2,HZ1);
UC1701ChineseDispaly(0,12,2,MM);
UC1701ChineseDispaly(1,12,2,US);
UC1701ChineseDispaly(3,8,4,HZ3);
UC1701DisplayN(1,4,i*10);
UC1701DisplayN(0,4,y);
UC1701CharDispaly(0,9,".");
UC1701DisplayN(0,10,y*100-((int)y*100));
其中y=1.73*i;
在这段液晶显示的代码中,除了固定的中文显示采用了外部字模,还有值得讲的就是实现了小数点后数据的显示。
其实也就是利用了C语言入门中学到过的取整函数“(int)“,然后再用小数点后几项减去小数点前几项,就单独得到了二位的小数点后数据,然后小数点单独用字符显示语句,然后在小数点后显示两位小数。
(5)其它功能:
计时器模块
longintcounttimer=0;
unsignedcharnowkeytime=1;
unsignedcharlastkeytime=1;
unsignedcharkeytimer=0;
nowkeytime=GPIOPinRead(GPIO_PORTA_BASE,GPIO_PIN_3);//pa3
if((lastkeytime!
=0)&&(nowkeytime==0))
{
counttimer++;
keytimer=counttimer%2;
if(keytimer==1)
{
x=1;
UC1701Init(60000);
UC1701Clear();
UC1701ChineseDispaly(1,0,2,HZ1);
UC1701ChineseDispaly(1,12,2,MS);
UC1701ChineseDispaly(3,6,5,HZ4);
GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_4,0<<4);
}
if(keytimer==0)
{
x=0;
GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_4,1<<4);
UC1701Init(60000);
UC1701Clear();
UC1701ChineseDispaly(1,0,2,HZ1);
UC1701ChineseDispaly(1,12,2,MS);
UC1701ChineseDispaly(3,6,5,HZ4);
UC1701DisplayN(1,4,i/100);
i=0;
}
}
lastkeytime=nowkeytime;
因为这个计时器功能只用一个按键实现,即按下一次开始计时,然后再按下一次计时结束。
因此使用了一种根据计数取余来实现状态翻转的方法,这个也是在吴老师的课程上学到的方法,非常的巧妙。
计时的方法与上文提到过的大题相同,根据控制开关x的值来控制计时的启停。
我个人认为这个计时器是我们程序的亮点之一,因为真的是非常精巧而易用。
四、成果展示
1.项目成果展示
初始界面:
此时为开机时的初始界面
测试模式:
按下K2按钮,红色的LED灯亮,即可切换到该模式
测距模式:
按下K4按钮,蓝色的LED灯亮即可切换到该模式。
。
计时器模式:
按下K3按钮,绿色的LED灯亮(直到计时结束),即可切换到该模式,开始计时,再次按下K3按钮,完成计时。
2.项目存在的问题以及提升方面
这个项目虽然只是中等难度,但是对于从来没有过机器人与外设基础的我们是一个不小的挑战。
说来有点害羞,我们遇到的第一个瓶颈就是项目的建立。
KEIL的项目文件建立操作繁杂,而且要导入大量资源文件,设置各项参数,实际上在项目建立时真的是弄得我们非常头大。
而且有时在课堂上笔记本里建立好的项目,拷贝到寝室里的台式机上时却又怎么都编译下载不了。
其实后面也是一点点的看,逐渐熟悉了资源的导入,而且也知道了转移文件时也要顺便将工程文件里资源的路径进行修改,才能在其它机器或者其它路径里正常使用工程文件。
其它问题在于因为对于C语言还是不够熟练,实际上代码也有很多可以优化的地方,有一些代码写得确实比较啰嗦,而且自查的时候也会发现很多重复段,这应该会影响运行效率吧。
现在的程序做过了不少的检查,但是可能还是会有不少写的不够好的地方。
这个项目下一步,我其实还是希望可以克服连续测距的瓶颈,做出雷达功能。
我认为屏幕连续刷新显示不良的问题可能是出在UC1701的显示原理上,我们如果能找到其它更适合屏幕连续刷新的显示方式,比如其它小组制作游戏用的刷新方式,应该就会使显示效果更佳。
而且因为我们调用了一个计时器来进行测距计时,我们其实可以调用另一个计时器来控制雷达的刷新频率,从而尽量加大扫描间隔。
因为超声波的任意两次测距需要一定的间隔时间,如果能很好的利用另一个计时器来调整扫描间隔的话,应该其实也可以用超声波模块做出一个很像样的雷达功能。
而且我发现超声波传感器如果没有放置好,比如测量了超量程的距离,或者是测量的角度不对的话会测量失败,此时屏幕上会显示一些很大的值,但是很明显是无意义的错值,而且实际上测量超出量程时单片机上自带的一个白灯会闪烁,我们暂时不太清楚它闪烁的原因,但是觉得这样是不太体面的。
因此我觉得应该在程序内写一个错值识别的部分,如果测量值超出了量程,或者是对回响的信号异常能够进行分辨,然后在屏幕上提示超出量程,测量失败等语句,而不是显示一个没