基于MCS51单片机的计数器和出租车计费器设计.docx
- 文档编号:7273951
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:15
- 大小:418.53KB
基于MCS51单片机的计数器和出租车计费器设计.docx
《基于MCS51单片机的计数器和出租车计费器设计.docx》由会员分享,可在线阅读,更多相关《基于MCS51单片机的计数器和出租车计费器设计.docx(15页珍藏版)》请在冰豆网上搜索。
基于MCS51单片机的计数器和出租车计费器设计
单片机课程设计
课程名称:
单片机课程设计
设计题目:
基于外部脉冲自动计数的出租车计费器设计
专业:
测控技术与仪器
学生姓名:
湖南工业大学
电气与信息工程学院
一、设计任务:
1.1外部脉冲自动计数,自动显示。
1.1.1设计一个255计数器:
0-255计数,计满后自动清0,重新计数(在数码管中显示)。
1.1.2设计一个50000计数器:
0-50000计数,计满后自动清0,重新计数(在数码管中显示)。
1.2设计一个出租车计费系统:
起步价为5元(2km以内),2km后,0.8元/0.5km;要求每500m刷新计费一次,在6位数码管中,前3位显示数码管显示里程数,后3位数码管显示价钱(角,元,十元,百元)
二、设计思路及源程序:
2.1、0-255计数器设计:
设计思路:
十进制数0-255可用8位二进制数来表示,定时器/计数器的四种工作方式均可实现0-255的计数功能(工作方式3只有定时器0可以实现)。
在此为了设计方便,我采用定时器/计数器T1工作方式2(可自动重装初值),外部脉冲由AT89S51的P3.5管脚引入,每来一个外部脉冲,计数器T1计数加一。
由此,只需对TL1的计数值进行相应的二进制---十进制转换,通过算法进行运算可得到百位,十位,各位,并存入相应变量中(对C51语言而言)。
选通相应数码管,再将各位数字的相应显示编码输入即可在数码管上显示计数结果。
源程序:
/***********************************************************************************程序功能描述:
计数器接受外部脉冲实现自动计数,计数范围0-255
计满数后自动清零,再重新循环计数。
说明:
主要硬件设备:
6位共阳极数码管,晶振频率:
12MHZ
**************************************************************************/
#include"reg51.h"//C51头文件
unsignedcharcodeled_num[10]={0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90};//数码管显示数字的十六进制代码
unsignedcharcodeled_bit[6]={0xf8,0xf9,0xfa,0xfb,0xfc,0xfd};//位选代码,注意语句后的分号不能丢
/*********************************************************************************函数名称:
延时子程序
**************************************************************************/
voiddelay(unsignedintdat)
{
unsignedintk,j;//定义局部无符号整形变量
for(k=0;k { for(j=0;j<2000;j++);//空循环,延时语句 } } /************************************************************************** ********函数名称: 计数器初始设置函数 **************************************************************************/ voidinit_timer1(void) { TMOD=0x60;//设置为计数器模式,工作方式2,自动重装初值 TH1=0x00;//计数器初值设为0 TL1=0x00; TR1=1;//开启计数器 } /**********************************************************************************函数名称: 主函数 说明: 先将计数器1的TL1中数据百位、十位、个位分离,并存于数组led[]中,再将对应数字的编码送入数码器显示出来 **************************************************************************/ voidmain(void) { unsignedchari,j; unsignedcharled[3];//用来存放要显示数字的百、十和个位上的数字 init_timer1();//计数器初始化 while (1) { led[0]=TL1%10;//取个位数字存于字符数组 led[1]=TL1%100/10;//取十位数字 led[2]=TL1/100;//取百位数字 for(j=0;j<10;j++)//在显示上消耗时间,使三个数码管显示不中断. { for(i=0;i<3;i++) { P2=led_bit[i];//把led[i]表示的位置的位选通 P0=led_num[led[i]];//把相应位置的数显式出来 delay (1);//延时 } } } } Proteus仿真效果图 图2.1、255计数器仿真效果图 2.2、0-50000计数器设计: 设计思路: 0-50000计数器设计方法与0-255计数器设计方法有很多地方相似,主要在一些地方作了一些技术处理。 由于十进制数0-50000用8位二进制已不能表示出来,在此只能选择定时器/计数器工作方式1。 在用16位计数器处理0-50000计数时,我们既可用单纯的定时器/计数器模式,也可采用定时器/计数器中断模式。 中断模式的优越之处在于它可在计数溢出时打断主程序的执行,通过在中断服务程序中编写显示清零程序,可以很精确的实现0-50000的计数。 而用定时器/计数器模式时有可能在计数满50000时显示不能及时清零,造成这种现象的原因主要是外部脉冲频率太大,程序来不及对每个脉冲进行处理。 因此非中断模式要受到外部脉冲的限制,考虑到现实中有些情况下脉冲频率也可能会很大,故本程序及下面的出租车计费器均采用中断模式,把计数清零语句放到中断服务程序中,以保证能精确实现0-50000的计数。 源程序: /***************************************************************************************程序功能: 此程序为计数器1接受外部脉冲实现0-50000自动计数,计数快慢由外部脉冲的大小决定 说明: 主要硬件设备: 6位共阳极数码管。 **************************************************************************/ #include"reg51.h"//头文件 unsignedcharcodeled_num[10]={0xc0,0xf9,0xa4,0xb0,0x99, 0x92,0x82,0xf8,0x80,0x90};//数码管显示数字的十六进制代码 unsignedcharcodeled_bit[6]={0xf8,0xf9,0xfa,0xfb,0xfc,0xfd};//位选代码 /*********************************************************************************函数名称: 延时子程序函数 **************************************************************************/ voiddelay(unsignedintdat) { unsignedintk,j; for(k=0;k { for(j=0;j<2000;j++);//空循环,延时语句 } } /************************************************************************ *******函数名称: 定时器1初始设置函数 **************************************************************************/ voidinit_timer1(void) { TMOD=0x50;//设置为计数器模式,工作方式1 TH1=0x00;//计数器初值设为0 TL1=0x00; TR1=1;//开启计数器 } /************************************************************************** *********函数名称: 主函数 说明: 当第50001个脉冲来临时将计数器清零 **************************************************************************/ voidmain(void) { unsignedchari,j;//定义控制循环的变量 unsignedinttemp;//定义一个整型变量用来存储两个字节的数据 unsignedcharled[5];//用来存放要显示数字的万、千、百、十和个位init_timer1();//计数器初始化 while (1) { if((TH1==0xc3)&&(TL1>=0x51))//第50001个脉冲来临时将计数器清零 { TH1=0x00; TL1=0x00; } temp=TH1; temp=temp<<8;//将TH1中数字左移8位再赋给temp temp=temp|TL1;//将TH1和TL1中数据合到一个整形变量temp中 led[0]=temp%10;//存取个位数字 led[1]=temp%100/10;//存取十位数字 led[2]=temp%1000/100;//存取百位数字 led[3]=temp%10000/1000;//存取千位数字 led[4]=temp/10000;//存取万位数字 for(j=0;j<10;j++)//在显示上消耗时间,使三个数码管显示不中断 { for(i=0;i<5;i++) { P2=led_bit[i];//把led[i]表示的位置的位选通 P0=led_num[led[i]];//把相应位置的数显式出来 delay (1);//延时一个微小时 } } } } Proteus仿真效果图 图2.2、50000计数器仿真效果图 2.3、出租车计费设计: 设计思路: 出租车计费器的设计可在0-50000计数器的基础上扩充改写而成。 出租车上安装有一个霍尔传感器,车子轮胎每转动一圈,霍尔传感器便产生一个脉冲。 因此计数脉冲跟轮胎有直接的对应关系,考虑到车轮是按一定的规格生产的,在此我们将车程(汽车行驶距离)与车轮的直径及计数脉冲数联系起来。 因此车程可用计数脉冲的个数表示出来,因而车程的显示可转化为计数器显示问题。 而车程与收费之间的数学关系在设计要求中已明确。 我采用6位数码管来显示车程和收费。 其中前三个数码管显示车程,后三个数码管显示收费。 特别指出最好用中断处理计数脉冲数的临界值,否则很容易不能达到预期目标。 /*******************************************************************************程序功能: 此为一个出租车计费器,计数器接受外部脉冲自动计数,在两千米以内收费5元,之后每500m刷新一次计费,每500米收费0.8元。 数码管前三位显示里程数,显示精确到0.1km;数码管后三位显示价钱,从低位到高位一次表示角,元,十元。 说明: 车子的轮胎直径用宏定义,方便根据不同车型修改参数,用软件仿真时为了更快看到结果,可将车的轮胎直径参数设的小一点。 ************************************************************************/ #include"reg51.h"//C51头文件 #definePI3.14159//宏定义圆周率为3.14159 #defineDIA1.0//宏定义出租车轮胎的直径为1.0米 unsignedcharcodeled_num[10]={0xc0,0xf9,0xa4,0xb0,0x99, 0x92,0x82,0xf8,0x80,0x90};//数码管显示数字的十六进制代码 unsignedcharcodeled_bit[6]={0xf8,0xf9,0xfa,0xfb,0xfc,0xfd};//位选代码 /************************************************************************** *******函数名称: 延时子程序 **************************************************************************/ voiddelay(unsignedintdat)//延时大小由形参的大小决定 { unsignedintk,j; for(k=0;k { for(j=0;j<2000;j++);//空循环,耗时语句 } } /************************************************************************** ********函数名称: 定时器1初始化设置函数 **************************************************************************/ voidinit_timer1(void) { TMOD=0x50;//设置为计数器模式,工作方式1 TH1=0x00;//计数器初值设为0 TL1=0x00; TR1=1;//开启计数器 } /************************************************************************** **********函数名称: 主函数 说明: 因为用三位数码管表示角,元,十元,在所要求中最大显示数据为99.4元,对应计数器接收到9708个脉冲计费达到最大值,故在设计程序时,当第9709个脉冲来临时要将计数器清零,并且计费也要清零. **************************************************************************/ voidmain(void) { unsignedchari,j;//定义循环控制变量 unsignedcharcnt=0; unsignedinttemp=0; unsignedinttab=0,wap=0; doubleway=0,money=0;//定义浮点型数据,way表示路程,money表示收费 unsignedcharled[6];//用来存放要显示数字的各位数字 init_timer1();//计数器初始化 while (1) { if((TH1==0x25)&&(TL1>=0xed))//第9709个脉冲来临时将计数器清零 { TH1=0x00; TL1=0x00; money=0;//计费清零 } temp=TH1; temp=temp<<8;//将TH1中数字左移8位再赋给temp temp=temp|TL1;//将TH1和TL1中数据合到一个整形变量temp中 way=temp*PI*DIA/1000.0;//车子行驶的路程存入浮点型变量way中 if((way>0)&&(way<=2.0)) { money=5.0;//两千米之内收费5元 } else { cnt=2*(way-2);//将超出2km部分转化为0.5km的整数倍并存于cnt中 money=cnt*0.8+5;//算出应收的费用并存于money中 } wap=money*10;//将收费数据转化为整形,最低位表示角,其次为元,十元 tab=way*10;//将路程数据转化为整形,最低位表示百米,其次表示千米,万米 led[0]=wap%10;//角位上的数字存led[0] led[1]=wap%100/10;//得出元位上的数字 led[2]=wap/100;//得出十元位上的数字 led[3]=tab%10;//得出百米上的数字 led[4]=tab%100/10;//得出千米上的数字 led[5]=tab/100;//得出万米上的数字 for(j=0;j<10;j++)//在显示上消耗时间,使三个数码管显示不中断 { for(i=0;i<6;i++) { P2=led_bit[i];//把led[i]表示的位置的位选通 if((i==1)||(i==4)) { P0=led_num[led[i]]&0x7f;//把相应位置的数显式出来,带小数点 } else { P0=led_num[led[i]];//把相应位置的数显式出来,不带小数点 } delay (1);//延时一个微小时间 } } } } Proteus仿真效果图 注: 高三位数码管显示车程,最低位表示百米;低三位数码管显示收费,最低位表示角 图1.2、出租车计费仿真效果图 三、单片机最小系统硬件资源介绍: ※89S51单片机; ※6位共阳极数码管; ※段码驱动器74HC573,位选译码器74HC138; ※4路独立式按键; ※外部晶振电路; ※ISP下载接口(Insystemprogram,在系统编程); ※DC+5V电源试配器(选配); ※ISP下载线(选配)。 单片机最小系统实训板硬件资源分布见图1元件布局图和图2电路原理图: 其中P1为外部电源输入座(DC+5V),S8为电源最小系统的电源开关,E1和C3为电源滤波,去耦电容。 D1为系统电源指示灯。 J2为ISP下载接口,S7系统复位按键。 CRY1,C1,C2为外部时针电路。 IC1为89S51(DIP-40),左上角为第一脚。 PRE1,PRE2。 为上拉排阻(阻值4.7k—10k)。 J5,J9,J6,J10分别对应单片机的P0,P1,P2,P3口。 便于二次开发。 6路共阳极数码管动态显示电路,P0口通过74HC573(起驱动和隔离作用,让电流通过74CH573流入公共地),来控制数码管的8路段码,P20-P22通过74HC138译码器(使用其中的6路)控制数码管的公共端(中间还有三极管做驱动器)。 这样设计的理由: 为了保证该单片机最小系统的二次开发的资源充足和合理性。 如果单只做一个动态显示电路,可以省去74HC573,和74HC138。 图3.1元件布局图 图3.2单片机最小系统原理图 四、课程设计总结 这次单片机课程设计历时两周,是一次把理论融于实践的教学行动。 基于我们基础还比较薄弱,这次课程设计老师给我们布置了三个设计课题,分别是0-255计数器,0-50000计数器以及出租车计费器。 这三个课题都是定时器/计数器接收外部脉冲实现计数,程序设计层层相扣,由浅入深。 255计数器设计是这次课程设计最简单的一道程序题,它也是后续程序设计的基础。 由于255计数器数据显示范围是0-255,所以由定时器T1的TLI寄存器就可完全实现计数。 定时器有四种工作方式,它们都可以满足255计数要求(工作方式3只能用定时器T0),为了设计方便,老师建议使用定时器T1工作方式2(计数器自动重装初值)。 老师又说不使用中断也可完全实现编程要求,而且告诉了我们数模转换的方法,并帮我们理清了设计思路,这样我们编程基本上不存在什么难点。 50000计数器设计比255计数器设计要难一点,也是这次课程设计比较关键的一环。 50000计数器只能使用定时器工作方式1(16位计数器),这里需要处理16位数字,用汇编语言编程,在这里将是一个难点,用教科书中双字节除法运算程序显然程序冗长晦涩,不适合这种课程设计。 在这里我们先把TH1和TL1中的数据分别用数模转换按位先分离,再采用满十向高位进一的办法解决。 在这次设计中我采用C51语言编程,定义一个双字节整形变量,很简单解决了装下TH1和TL1中总共两个字节数据并实现数模转换的问题。 由于16位计数器最大计数范围为0-65535,所以在设计50000计数器的时候要注意计数满50000显示清零。 出租车计费器可在计数50000的程序上修改而成。 在这里我们要明白车子轮胎每转一圈,霍尔传感器发送一次脉冲,计数器接受脉冲计数加一。 由此我们可得出车程与轮胎直径及计数器计数值三者之间的关系,从而由计数器计数转换为车程,并将车程用数码管显示出来。 另一个问题是找出计费与车程的关系,这在题目中已经给出。 最后还要注意当计费显示达到最大值时要将数码管显示清零。 由上可以知道已将出租车计费器转换为计数问题,从而使问题得到圆满解决。 编程思路虽然理清了,但在编程过程中的一些细节问题还是要引起足够的重视。 就我本身而言,虽然自己在之前已经有一些C51编程的经验,但在编程时还是出了一个细节问题而导致在计费器显示在开始一段车程内乱码。 这个细节问题就是用于表示费用的一个浮点型变量在定义时未给其赋初值0,而这个问题的出现就是因为我觉得系统会自动给其赋初值0,因而没必要多此一举。 遗憾的是正是这个“多次一举”,让我花费了三个多小时才找出错误之处,这给我上了重要的一课。 这次课程设计虽然是大学生涯中众多课程设计中的一次,但因为我很喜欢单片机这门课,而且也愿意在毕业之后从事这方面的工作。 所以这次独立完成的课程设计既给了我从事这门工作的信心,但也让我明白我离现实社会的要求还有很大的差距。 它将激励我勇敢的前行!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 MCS51 单片机 计数器 出租车 计费 设计
![提示](https://static.bdocx.com/images/bang_tan.gif)