51单片机出租车计价器设计.docx
《51单片机出租车计价器设计.docx》由会员分享,可在线阅读,更多相关《51单片机出租车计价器设计.docx(29页珍藏版)》请在冰点文库上搜索。
51单片机出租车计价器设计
第一章概述
1.1设计意义
出租车的计价器应用很广,平时比较常见,理解较为深入,适合作为编程联系的例子。
在设计中能够更加透彻的理解和较为全面的考虑各种情况。
通过这次的练习,可以加深对单片机应用了解,更能灵活的使用51系列单片机的资源解决实际问题。
1.2系统功能
上车按键后开始计价。
有实时时钟、单价、距离和总价钱显示。
可以通过键盘修改单价(元/公里)和时间。
有清除键用以清楚上次费用。
利用LCD分别显示时间、单价、路程和费用。
LED辅助显示说做操作。
主要方法:
原理是,价格=速度时间单价;采用直流电机测速单元模拟出租车速度,通过光电耦合A/D转换进行速度采样,利用单片机的定时器/计数器定时和计数,用LED数码管或LCD显示总价和时间,并通过键盘进行单价设定及时间显示。
附加功能:
当出租车速度低于某一设定值时,自动进行计数,当计时每达到1分钟时,价格按照1公里长度计。
1.3功能详述
1.3.1按键
0~9数字键;
B:
开始计价;
C:
清除数据;
D:
(P)设置单价键,按一次后,再按数字键进行设置;
E:
(T)设置时间键,按一次后,再按数字键进行设置;
F/A:
完成设置键。
1.3.2操作流程
程序运行后,时间照常显示。
此时可以进行单价和时间的设置。
按B键开始计价,运行中也可以进行单价和时间的设置。
按C键清除上次费用和路程
1.4系统所涉及模块
1.4.1定时器与计数器
利用模拟直流定动机模拟出租车,转盘上有孔,利用光电转换变成电脉冲。
利用计数器进行计数,定时器进行定时。
1.4.2路程和费用计算
根据计数值和计时时间换算出速度,乘以单价得出单位计时间隔的费用,加上以前的费用得出即时费用。
即路程=计数个数*计时间隔/转盘上孔的个数。
即时费用=路程*价格。
1.4.3实时时钟
出租车上的显示器上要求有时钟,选择DS12887时钟芯片作为时钟控制单元。
1.4.4键盘及LED
上车开始计价,数据清除,单价及时间的修改都需要使用键盘,数字加上功能键共计15个,因此使用8279来管理4×4键盘及辅助显示的8位LED。
8279是一种通用的可编程的键盘/显示器接口器件,可对64个开关组成的键盘矩阵进行自动扫描,接收键盘上的输入信息,并在有键输入时向单片机请求中断,还能对8位或16位LED自动扫描,使显示缓冲器的内容在LED上显示出来。
利用8279对键盘/显示器的自动扫描,可以减轻CPU负担,具有显示稳定、程序简单、不会出现误动作等特点。
利用8279芯片管理键盘,可有效减轻单片机I/O资源负担。
键盘由74LS138译码扫描,当有按键时,有8279自行编值,并引发外中断。
在中断程序中读取键值。
8279采用单±5V电源供电,40脚封装。
其具体管脚介绍如下:
DB0~DB7:
双向数据总线,用来传送8279与CPU之间的数据和命令。
CLK:
时钟输入线,用以产生内部定时的时钟脉冲。
RESET:
复位输入线,8279复位后被置为字符显示左端输入,二键闭锁的触点回弹型式,程序时钟前置分频器被置为31,RESET信号为高电平有效。
CS:
片选输入线,低电平有效,单片机在CS端为低时可以对8279读/写操作。
A0:
缓冲器低位地址,当A0为高电平时,表示数据总线上为命令或状态,当为低电平时,表示数据总线上为命令或状态,当为低电平时,表示数据总线上为数据。
RD:
读信号输入线,低电平有效,将缓冲器读出,数据送往外部总线。
WR:
写信号输入线,低电平有效,将缓冲器读出,将数据从外部数据总线写入8279的缓冲器。
1.4.5LCD液晶显示器
时钟、单价、路程及费用需要使用液晶显示器来显示,选用RT12864hz液晶显示器。
各种参数经过处理后用它来显示。
RT12864HZ汉字图形点阵液晶显示模块可显示汉字和图形。
内置8192个中文汉字(16x16点阵)、128个字符(8X16点阵)及64X256点阵显示(用子图形)。
模块由20个引脚与外界电路相连,其中8条数据线,5条控制线,3条电源线,2条背光电源线,2条没定义。
文本显示RAM提供8个,4行的汉字空间,当写入文本显示RAM时,可以分别显示CGROM、HCGROM和CGRAM字型。
HCGROM为半宽字型(8x16点阵),CGROM为中文字型,CGRAM为自定义的中文字型。
三种字型的选择由写入DDRAM的编码选择
第二章硬件设计
2.1硬件原理图简图
图2.1硬件简图
2.2简要说明
1.外部测速单元有模拟直流电动机(通过直流电机测速以及对速度进行采样)和光电耦合器组成,输出脉冲信号由计数器输入单片机进行处理,其中电动机速度可调;
2.4×4键盘和8位LED由8279管理,IRQ接INT0,CLK接ALE,键盘由十个数字键和五个功能键组成,其中一个键重复定义,LED用字母和数字显示表示当前的状态、进行的操作以及输入的数据。
第三章软件设计流程及描述
3.1主函数程序流程图
先进行初始化,显示计价初始值,然后等待开始计价或者进行设置。
计价分两种情况:
速度超过设定值时,按当时速度进行计价;速度低于设定值时,自动进行计数,当计数每达到一分钟时,价格按照一公里长度计。
修改分、修改时间和单价。
清零用于清除当前路程和费用,以便进行下次计价。
3.2定时器中断服务程序流程图
在定时时间内记录脉冲个数,赋给全局变量,用于计算速度。
3.3按键中断服服务程序流程图
读取键值,转换为可用数据,并且置位相应标志位,为以后操作做好准备。
然后根据状态在LED上显示相应操作。
3.4时钟芯片中断服务程序
由于DS12887时钟芯片一秒钟产生一次中断,因此低速计数在这里进行计数。
读取时间数值,赋给全局变量进行处理。
第四章源程序代码
#include
#include
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
/*DS12887实时时钟时间地址设置*/
#definesecondXBYTE[0x6000]/*秒寄存器地址*/
#definesec_alarmXBYTE[0x6001]
#defineminuteXBYTE[0x6002]/*分寄存器地址*/
#definemin_alarmXBYTE[0x6003]
#definehourXBYTE[0x6004]/*小时寄存器地址*/
#defineweekXBYTE[0x6006]/*星期寄存器地址*/
#definedateXBYTE[0x6007]/*日期寄存器地址*/
#definemonthXBYTE[0x6008]/*月寄存器地址*/
#defineyearXBYTE[0x6009]/*年寄存器地址*/
#defineregaXBYTE[0x600A]/*A寄存器地址*/
#defineregbXBYTE[0x600B]/*B寄存器地址*/
#defineregcXBYTE[0x600C]/*C寄存器地址*/
#defineregdXBYTE[0x600D]/*D寄存器地址*/
/*RT12864HZ汉字图形点阵液晶地址设置*/
#definew_c_addXBYTE[0x2070]/*写命令字地址*/
#definew_d_addXBYTE[0x2071]/*写数据地址*/
#definer_s_addXBYTE[0x2072]/*读状态字地址*/
#definer_d_addXBYTE[0x2073]/*读数据地址*/
#defineCOMXBYTE[0x2021]/*8279命令口*/
#defineDATXBYTE[0x2020]/*8279数据口*/
#defineL1/*长度系数*/
#defineTI50/*分度数*/
#defineHN30/*孔个数*/
#defineLOW30/*最低速度*/
ucharxdatakey;/*键值*/
uintidatanum=0;/*计数值*/
uintxdataa=1,b=0;
uintidatalen,fa,m,n,o,p,q,r,s,t,COUT,CLO;
uintidatah1,h2,m1,m2,s1,s2;/*时钟参数*/
uintidataprice=10,fate=00,length=0,leng;
uintidatastate=0,clear=1,begin=0,pri,tim,tt=0,cc=0;nn=0;/*几个状态标志位*/
/*设置,清除,开始计价,单价修改,时间修改,两位数监视,六位数监视,数字标志*/
unsignedcharbdatastatus;/*lcd可位寻址片内数据存储器,允许位与字节混合访问*/
sbitbusy=status^7;/*lcd“忙”线的第7位*/
ucharbdatasta_8279;/*8279*/
sbitdu=sta_8279^7;/*8279方式设置命令字*/
unsignedcharrflag;
unsignedcharbdatasta_ds;
sbituip=sta_ds^7;
uchards_h,ds_m,ds_s,ds_t;
unsignedcharbdatads_time;
unsignedcharcodeNO[10]={0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9};
ucharcodetab[16]={0x3f,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
0x71,0x7C,0x39,0x73,0x31,0x71};/*0~F的字型编码*/
ucharxdatahz2[16]={0xB5,0xA5,0xBC,0xDB,0xA3,0xBA,0x20,0x20,0x20,0x20,
/*单价:
空空*/
0xA3,0xB0,0xA3,0xB0,0xD4,0xAA};
/*NN元*/
ucharxdatahz3[16]={0xBE,0xE0,0xC0,0xEB,0xA3,0xBA,0xA3,0xB0,0xA3,0xB0,
/*距离:
空N*/
0xA3,0xB0,0xA3,0xB0,0x4B,0x4D};
/*NNkm*/
ucharxdatahz4[16]={0xBC,0xDB,0xC7,0xAE,0xA3,0xBA,0xA3,0xB0,0xA3,0xB0,
/*价格:
NN*/
0xA3,0xB0,0xA3,0xB0,0xD4,0xAA};
/*NN元*/
ucharxdatahz5[16]={0x20,0x20,0xCA,0xE4,0xC8,0xEB,0xC1,0xBD,0xCE,0xBB,
/*空输入两位*/
0xB5,0xA5,0xBC,0xDB,0x20,0x20};
/*单价空*/
ucharxdatahz6[16]={0x20,0x20,0x20,0x20,0xA3,0xBA,0xA3,0xB0,0xA3,0xB0,
/*空空:
NN*/
0xD4,0xAA,0xA3,0xAF,0x4B,0x4D};
/*元/km*/
voidinitial(void);
voiddelay(void);
voidcheck_lcd(void);
voidshow_lcd(uchar*,uchar*,uchar*);
voidlcd_busy(void);/*LCD忙状态检测函数*/
voidcompute(void);
voidchange_pri(void);
voidchange_time(void);
voidclock(void);
voidcheck_ds12887(void);
voide(void)
{w_c_add=0x80;
delay;lcd_busy();
w_d_add=0xCA;
lcd_busy();
w_d_add=0xE4;
lcd_busy();
w_d_add=0xC8;
lcd_busy();
w_d_add=0xEB;
lcd_busy();
w_d_add=0xB4;
lcd_busy();
w_d_add=0xED;
lcd_busy();
w_d_add=0xCE;
lcd_busy();
w_d_add=0xF3;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
lcd_busy();
w_d_add=0x20;
h1=0;h2=0;m1=0;m2=0;s1=0;s2=0;cc=0;
delay();delay();delay();delay();delay();delay();
}
main()
{
uintinf=0;
initial();
show_lcd(hz2,hz3,hz4);
while
(1)
{
clock();
if(begin==1)
{
if(inf==0)
{
fate=20;
inf=1;
}
if(state==0)
{
if(num>=LOW)
{
COUT=0;
price=a*10+b;
len+=num*L/HN;
if(len/TI>=0)
{
leng=len/TI;
len%=TI;
}
fa=leng*price;
length+=leng;
leng=0;
fate+=fa;
}
else
{
if(COUT>=120)
{
length++;
fate+=(10*a+b);
COUT=0;
}
}
compute();
show_lcd(hz2,hz3,hz4);
}
else
{
if(pri==1)
change_pri();
if(tim==1)
change_time();
}
}
if(clear==1)
{
length=0;
fate=0;
inf=0;
compute();
show_lcd(hz2,hz3,hz4);
if(pri==1)
change_pri();
if(tim==1)
change_time();
}
}//while
(1)
}//main
/********************以下是程序(包括中断)********************************/
voidkeybord(void)interrupt0using1
{
ucharkeyboard=0;
EX0=0;
COM=0x50;
keyboard=DAT;
keyboard&=0x0f;/*保留低四位*/
switch(keyboard)
{case0x0:
key=0;tt++;nn=1;cc++;break;
case0x1:
key=1;tt++;nn=1;cc++;break;
case0x2:
key=2;tt++;nn=1;cc++;break;
case0x3:
key=3;tt++;nn=1;cc++;break;
case0x4:
key=4;tt++;nn=1;cc++;break;
case0x5:
key=5;tt++;nn=1;cc++;break;
case0x6:
key=6;tt++;nn=1;cc++;break;
case0x7:
key=7;tt++;nn=1;cc++;break;
case0x8:
key=8;tt++;nn=1;cc++;break;
case0x9:
key=9;tt++;nn=1;cc++;break;
case0x0B:
key=11;COUT=0;begin=1;clear=0;nn=0;break;/*Bbegin*/
case0x0C:
key=12;clear=1;begin=0;nn=0;break;/*Cclear*/
case0X0D:
key=13;state=1;pri=1;tim=0;nn=0;break;/*Dsetprice*/
case0x0E:
key=14;state=1;tim=1;pri=0;nn=0;break;/*Esettime*/
default:
key=15;nn=0;break;/*Ffinish*/
}
keyboard=tab[keyboard];
if(pri==1)
if(nn==0)
COM=0x80+8;
else
COM=0x80+4+tt;
if(tim==1)
if(nn==0)
COM=0x80+8;
else
COM=0x80+1+cc;
DAT=keyboard;
EX0=1;
}
voidint1serve()interrupt2using2
{
unsignedchartemp2;
COUT++;
EX1=0;
rflag=1;
sta_ds=rega;
while(uip);
ds_h=hour;
ds_m=minute;
ds_s=second;
temp2=regc;
rflag=0;
EX1=1;
}
voidtimer(void)interrupt3using3
{
TR1=0;
TR0=0;
num=TH0*256+TL0;
TH0=0;
TL0=0;
TH1=-(50000/256);
TL1=-(50000%256);
CLO++;
TR1=1;
TR0=1;
}
voidinitial(void)
{
COM=0xd1;/*清除显示*/
ACC=DAT;
if(ACC^1);/*P奇偶标志。
若值为1的位数为奇数,则P置位,否则清除*/
COM=0x00;/*8字符显示左入口*/
COM=0x34;/*34次分频*/
TMOD=0X15;
TH0=0;
TL0=0;
TH1=-(50000/256);
TL1=-(50000%256);
TR0=1;
TR1=1;
EA=1;
EX0=1;
EX1=1;
ET1=1;
check_ds12887();
}
voiddelay(void)
{
unsignedchari,j;
for(i=0;i<100;i++)
{
for(j=0;j<150;j++)
{
;
;
}
}
}
voidcheck_lcd(void)
{
uinti;
lcd_busy();
w_c_add=0x30;/*功能设置:
8位数据*/
for(i=0;i<255;i++)
lcd_busy();
w_c_add=0x0c;/*显示状态:
整体显示,游标位置无效*/
lcd_busy();
w_c_add=0x01;/*清除显示:
将DDRAM填满“20H”,并且设定DDRAM的地址计数器到“00”*/
lcd_busy();
w_c_add=0x02;/*地址归位:
设定DDRAM的地址计数器到“00”,将游标移到开头原点位置*/
}
voidshow_lcd(uchar*b1,uchar*c1,uchar*d1)
{
unsignedcharadd;
unsignedchari;
check_lcd();
lcd_busy();
add=0x90;/*设置显示坐标,90H对应字符的第二行第一列*/
lcd_busy();
w_c_add=add;/*送命令*/
for(i=0;i<8;i++)
{
lcd_busy();
w_d_add=b1[2*i];
lcd_busy();
w_d_add=b1[2*i+1];
}
add=0x88;/*设置显示坐标,88H对应字符的第三行第一列*/
lcd_busy();
w_c_add=add;/*送命令*/
for(i=0;i<8;i++)
{
lcd_busy();
w_d_add=c1[2*i];
lcd_busy();
w_d_add=c1[2*i+1];
}
add=0x98;/*设置显示坐标,98H对应字符的第四行第一列*/
lcd_busy();
w_c_add=add;/*送命令*/
for(i=0;i<8;i++)
{
lcd_busy();
w_d_add=d1[2*i];
lcd_busy();
w_d_add=d1[2*i+1];
}
delay();
clock();
}
voidlcd_busy(void)/*LCD忙状态检测函数*/
{
do
{
status=r_s_add;/*读状态字地址的第7位*/
}
while(busy);
}
voidcompute(void)
{
uintf1,l1;
f1=fate;
l1=length;
p=l1%10;/*数字位拆分**路程*/
l1/=10;
o=l1%10;
l1/=10;
n=l1%10;
l1/=10;
m=l1%10;
t=f1%10;/*数字位拆分**价钱*/
f1/=10;
s=f1%10;
f1/=10;
r=f1%1