摘 要
传统的车速里程表的设计是机械式的,在日常的使用中避免不了磨损,而且不好保养。随着使用时间的增大,
会使实时速度产生误差,显示不准确。还有可能会使仪表功能失效,而电子车速里程表就克服了这些劣势。为了让
人们更清楚、准确的知道车辆当前的速度、里程,所以在此次设计中我将想办法把速度里程等数据通过LCD显示出
来。
本文章主要讲述一种基于51单片机的电子车速里程表的设计。用STC89C52单片机作为核心,用霍尔传感器测车
轮的转数,实现对车速和里程的测量,最后将所测量的结果通过1602LCD显示屏显示出来。
论文中介绍了电子车速里程表的硬件电路和软件设计。硬件部分采用霍尔元件将车轮所转的圈数转变为脉冲数
传入单片机系统,然后通过单片机系统将信号经过处理、计算后显示在显示屏上。软件方面使用Keil uVision4和
Proteus8.6。
关键词:里程;速度;霍尔传感器;单片机
一 车速里程表概述
(一) 课题研究背景
随着科技的进步和人们不断地创新发展,越来越多的电子化、智能化产品出现在我们的日常生活中,我国电子
技术水平逐渐提高。传统的按键手机现在已被大屏智能手机取代,电子手表等等产品逐渐替代传统的产品。电子产
品相比传统的一些产品相比,电子式产品使用更加便捷,在功能上更加强大。而一个国家要有大的发展,工业发展
至关重要,工业发展更少不了交通运输业。随着第二次工业革命内燃机的出现,卡尔本茨发明了世界上第一辆汽
车,从此全世界走入了汽车社会。各式各样的汽车出现在人们生活中,摩托车、三轮车、货车等等,各种车辆在不
同的领域发挥着不同的作用。但是汽车发展速度过快,而人们又不能够很好地解决发展过快导致的一些遗留问题,
两者之间不能协调。虽然每条道路都设有超速抓拍摄像头,但还是有超速驾驶造成的交通事故频繁发生,车路里程
表作为车上不可缺少的一种仪器,他能帮助驾驶员获取实时车速的信息,避免超速行驶。电子式车速里程表能直观
的把信息显示出来,方便驾驶员获取信息,更有助于提高行车安全。
单片机以其造价低,体积小,高可靠性,控制功能强的特点,被用于各个领域,比如智能仪表领域,航空航天领
域,工业领域等等。此次设计正是看中了单片机的这些特点,所以使用单片机来设计。
(二) 车速里程表的发展
1、传统车速里程表
(1)原理
以往的速度计和里程表都是机械的,完全是机械的,是用各种零件组成的。速度计主要由齿轮、钢丝、磁铁、
游丝和指示器构成。传动装置由软轴钢索连接在软轴另一头的环圈中,由磁体驱动游丝,再由游丝驱动指针。车子
静止的时候,指针就是零,当车子开始移动的时候,指针就会跟着移动,这样才能显示出实时的速度。至于里程
表,则是使用转盘来累积里程,而非指针。计数机的鼓轮由传动齿轮和蜗杆啮合而旋转,其特征是:若上一级旋转10次,则下一级旋转仅为1次。
(2)优劣势
这个传统的机械里程表可以给人们一个车速里程的参考,从某种意义上来说,它的确可以保证驾驶员的行车安
全。但是因为都是机械的,在使用中避免不免磨损,维修起来也很麻烦。传动装置在高速运行时,由软轴驱动罩圈
磁体,因软轴的滞后,导致实时速度出现偏差。如车辆未正确地使用或维护,超过了钢丝缆的极限张力,将导致钢
丝缆断裂,使速度计失去作用。尤其是摩托车的仪表板,更是如此。在一定程度上制约了人们的行车安全。这也是
为什么这些年,逐渐淡出了市场的原因。
2、电子车速里程表
(1)原理
电子车速里程表的原理是用传感器将车轮的转数转变为脉冲数,然后将数据送入单片机中,单片机将速度和里
程计算出来。将传感器安装在车轮的变速箱蜗杆上,变速箱齿轮转动后,安装在变速箱上的磁钢也会跟着转动。磁
钢和传感器每相遇一次,也就是车轮每转一周,就会产生一次信号。速度是根据传感器测量到信号后,发送到单片
机中,经单片机处理之后显示出来。里程是根据车辆的行驶时间来计算的。
(2)优劣势
与传统的速度里程表相比,它的核心部件是采用传感器代替有经常有损耗的软轴,从而克服了传统车速里程表
指针不稳、摇摆大和软轴钢丝容易断裂等缺陷。与此相比,电子式比指针的显示更加直观。但也有不好的地方,因
为传感器与被动轮直接接触,所以传感器易损坏。同样的,有机械磨损,就会减少传感器的正常工作时间。霍尔传
感器被用于这次设计。
(三) 设计的主要任务和内容
要想完成车速里程表的设计,软件和硬件必不可少,二者是一种是相辅相成的关系。电子车速里程表的硬件部
分是由车速表、里程表和显示器这三部分组成。本次设计将单片机作为处理核心,将传感器所测得的数据进行处理
计算后,将其结果在LCD显示屏上显示出来。
在进行设计前,必须明白电子车速里程表的工作原理,掌握传感器脉冲的计算原理。学会使用proteus 8.6仿真
软件和Keil uVision4软件。了解每个模块的功能和工作方式,编译出能让各个模块互相协同工作的代码。对电路方
面的知识也要掌握,便于设计电路原理图。
二 系统控制方案设计
(一) 设计任务的分析与实现
1、任务分析
这一次的设计,主要依据三条原则,即“简单、直观、高可靠性”。为了在复杂、体积要求相对较高的工业设
备中应用更广泛,单片机将CPU与许多外围器件整合到一块,以提高系统对设备的适应性。这一次的设计,只需要一
个单片机,一个脉冲发生器,一个显示屏就能完成。目前,单片机已经发展到了一个非常成熟的阶段,它具有体积
小、价格低、功能强、功耗低、扩展灵活、微型化等特点。由于这次任务的里程程和速度都是由单片机直接进行计
算得出的,因此,硬件电路的设计比较简单。同时,其体积小巧、损耗低,也能很好地适应此次设计。现在的传感
器在灵敏度、精度、抗干扰等方面都有了很大的进步。因为是使用传感器获取的脉冲数量来计算速度,所以其测量
误差很小。实际误差在数米之内,与整体行程相比,误差并不大。而所得出的数据,则是由 LCD显示,使用者可以
直接进行数据的读取。该方法将计算机技术与控制技术结合起来,以达到“简单、直观、可靠”的目的。
2、总体设计方案说明
本设计的思路是:用单片机作为数据处理的核心,数据的测量是通过霍尔传感器所测出来的,霍尔传感器测车
轮的转数,当车轮每转一周,霍尔传感器就会产生一次脉冲信号,脉冲信号将送入单片机,单片机接收到脉冲信号
后,其内部的定时中断器会产生一次中断,在经过单片机计算得出结果,最终将速度和里程所计算出来的结果,显
示在LCD显示屏上。在此次设计中,单片机型号选用的是STC89C52,传感器使用的是A44E霍尔传感器,显示器使用的
是LCD1602。
本设计结果的计算是这样的,在车轮上安装一个磁铁,车子在行驶的过程中,车轮的外圈是车轮每转一圈的所
行驶的里程,我们取其周长为L。当车轮每转动一圈时,安装在车轮上的此贴就会经过传感器一次,传感器就会产生
一次脉冲信号,单片机接收到信号后,产生一次中断。总共得到的中断数乘上车轮的周长就会得出车辆所行驶的里
程。计数器采集到车轮每转一圈所用的时间,车轮的周长除以车轮每转一圈所用的时间就会得出车辆的行驶速度。
图2.1 系统框图
三 系统的硬件电路设计
(一) 单片机最小系统
1、STC89C52单片机
在此次设计中使用的是STC89C52单片机,STC89C52单片机是51系列单片机的一种,是功耗低,性能强的的8位微
控制器。此单片机支持在线编程,硬件方面由于程序问题运行不正常时,不必把单片机单独取下来再进行程序的烧
写。引脚图如图3.1所示
图3.1 STC89C52单片机引脚图
2、时钟电路
STC89C52系列是1T的8051单片机,STC89C52系统时钟兼容传统8051。系列单片机有两个时钟源:内部R/C振荡时
钟和外部晶体时钟。
在单片机内有一个高增益反相放大器,反相放大器的输入端为XTAL,输出端为XTAL2,由该放大器、晶振和两个
20PF的电容构成的振荡电路做单片机的时钟电路。
(二) 显示模块
在不影响系统性能的前提下,为了降低设计成本,选用了LCD1602显示器,此显示器支持阿拉伯数字,英文字
母,和一般符号的显示。唯一遗憾就是不能够显示中文,但这并不影响此次设计。显示器主要用来显示实时车速和
车辆行驶里程,并且能通过按键的控制切换显示的内容。
1602引脚说明如下表:
编号 符号 引脚说明 编号 符号 引脚说明
1 Vss 电源地 9 D2 数据口
2 VDD 电源正极 10 D3 数据口
3 VO 液晶显示对比度调节端 11 D4 数据口
4 RS 数据/命令选择端(H/L) 12 D5 数据口
5 R/W 读写选择端(H/L) 13 D6 数据口
6 E 使能信号 14 D7 数据口
7 D0 数据口 15 BLA 背光电源正极
8 D1 数据口 16 BLK 背光电源负极
(三) 霍尔传感器的测量原理
霍尔传感器是一种以霍尔效应为基础的磁敏传感器。测量结果的精准度与传感器的零名都有着很大的关系,此
次设计正是看中了其灵敏度高,抗干扰能力强,体积小,使用寿命长等特点,在此次设计中,
(四) DS1302时钟芯片
该系统采用DS1302时钟芯片,它具有实时计时功能,可实现年、月、周、日、时、分、秒等计时功能,工作电
压在2.5-5.5 V之间。是通过串口进行数据传送,在电源断电时不会损失。
DS1302与STC89C52的连接线有三条线:RST引脚、SCLK串行时钟引脚、I/O串行数据引脚,Vcc2作为备用电源,
芯片外接晶振X2,为芯片提供计时脉冲。
图3.4 时钟芯片
四 系统的软件设计与实现
(一)主程序流程图
在程序的运行时首先要进行初始化,整个程序运行的过程之中需要不停的扫描有没有中断的产生。
图4.1 主程序流程图
(二) 显示流程图
该子程序用LCD动态扫描显示方式。先将单片机的P2.2口连接使能端口E。接着将单片机的P2.0口连接数据/命令
选择端RS,P0口连接数据端D0~D7,然后将要显示的数字的值发送给P0口。然后调用延时,接着将P2.2口置0,P2.0
口置1,写指令,将P2.2口置1,P2.0口置1,写数据,直到要显示的数字全部显示在液晶上。显示流程图如图4.2所
示。
图4.2 显示流程图
(三) 速度处理流程图
在设计中,我们增加了一个蜂鸣器报警电路,并且可以手动设置报警速度,当计算出来的车速超过蜂鸣器设置
的报警速度时,蜂鸣器会发出“嘀嘀嘀”的响声,车速降到设置值以下时,蜂鸣器不在发出响声。
图4.3 速度处理流程图
(四) 电路仿真
1、仿真软件介绍
在设计的仿真中,我使用了Proteus 8.6版本进行仿真。先将使用的各种零部件有规则的排列出来,然后将各个
部件连接起来。在编译方面,使用Keil uVision4软件进行程序的编译,无误后将编译生成的hex文件导入仿真中运
行。
2、仿真结果
仿真结果如图所示
图4.4 仿真结果
源程序
#include <reg52.h> //调用单片机头文件
#define uchar unsigned char //无符号字符型 宏定义 变量范围0~255
#define uint unsigned int //无符号整型 宏定义 变量范围0~65535
sbit clk = P1^3; //ds1302时钟线定义
sbit io = P1^4; //数据线
sbit rst = P1^5; //复位线
//秒 分 时 日 月 年 星期
uchar code write_add[]={0x80,0x82,0x84,0x86,0x88,0x8c,0x8a}; //写地址
uchar code read_add[] ={0x81,0x83,0x85,0x87,0x89,0x8d,0x8b}; //读地址
uchar miao,fen,shi,ri,yue,week,nian;
float f_hz ,speed_m; //速度
uchar TH11,TL11;
uchar flag_en; //开始计算速度使能
uint juli_s; //每秒走的距离
uint juli_z; //总路程
float zhijing = 0.55; //直径 0.55M
uint s_zhijing = 55;
long zong_lc; //总量程
bit flag_1s = 1; //1s
uchar menu_1,menu_2; //设置不同参数的变量
bit flag_200ms ; //200毫秒的变量
uint shudu; //定义速度的变量
uint bj_shudu = 50; //报警速度
sbit key1 = P3^6; //按键IO口定义
sbit key2 = P3^5; //按键IO口定义
11
sbit key3 = P3^4; //按键IO口定义
sbit key4 = P3^3; //按键IO口定义
//这三个引脚参考资料
sbit rs=P1^0; //寄存器选择信号 H:数据寄存器 L:指令寄存器
sbit rw=P1^1; //寄存器选择信号 H:数据寄存器 L:指令寄存器
sbit e =P1^2; //片选信号 下降沿触发
sbit beep = P3^7; //蜂鸣器IO口定义
/******************1ms 延时函数*******************/
void delay_1ms(uint q)
{
uint i,j;
for(i=0;i<q;i++)
for(j=0;j<120;j++);
}
/********************************************************************
* 名称 : delay_uint()
* 功能 : 小延时。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void delay_uint(uint q)
{
while(q--);
}
/********************************************************************
* 名称 : write_com(uchar com)
* 功能 : 1602指令函数
* 输入 : 输入的指令值
* 输出 : 无
***********************************************************************/
void write_com(uchar com)
{
rs=0; //写指令
rw=0; //对1602写操作
P0=com; //P0口对1602写指令
delay_uint(25);
e=1; //e=1使能信号
delay_uint(100); //延时一下等1602完成操作
e=0;
}
/********************************************************************
* 名称 : write_data(uchar dat)
* 功能 : 1602写数据函数
* 输入 : 需要写入1602的数据
* 输出 : 无
***********************************************************************/
void write_data(uchar dat)
12
{
rs=1; //写数据
rw=0; //对1602写操作
P0=dat; //P0口对1602写数据
delay_uint(25);
e=1; //e=1使能信号
delay_uint(100); //延时一下等1602完成操作
e=0;
}
/********************************************************************
* 名称 : write_lcd2(uchar hang,uchar add,uchar date)
* 功能 : 显示2位十进制数,如果要让第一行,第五个字符开始显示"23" ,调用该函数如下
write_lcd1(1,5,23)
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void write_lcd2(uchar hang,uchar add,uint date)
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
write_data(0x30+date/10%10);
write_data(0x30+date%10);
}
/********************************************************************
* 名称 : write_lcd4(uchar hang,uchar add,uchar date)
* 功能 : 显示2位十进制数,如果要让第一行,第五个字符开始显示"23" ,调用该函数如下
write_lcd4(1,5,23)
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void write_lcd4(uchar hang,uchar add,uint date)
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
write_data(0x30+date/10000%10); //显示万位数
write_data(0x30+date/1000%10); //显示千位数
write_data(0x30+date/100%10); //显示百位数
write_data(0x30+date/10%10); //显示十位数
write_data(0x30+date%10); //显示个位数
write_data('k');
write_data('m');
}
void write_lcd7(uchar hang,uchar add,uint date)
13
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
write_data(0x30+date/100000%10);
write_data(0x30+date/10000%10); //显示万位数
write_data(0x30+date/1000%10); //显示千位数
write_data(0x30+date/100%10); //显示百位数
write_data('.');
write_data(0x30+date/10%10); //显示十位数
write_data(0x30+date%10); //显示个位数
write_data('k');
write_data('m');
}
/***********************lcd1602上显示两位十进制数************************/
void write_lcd1(uchar hang,uchar add,uchar date)
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
write_data(0x30+date % 10); //显示个位数
}
/********************************************************************
* 名称 : write_string(uchar hang,uchar add,uchar *p)
* 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符开始显示"ab cd ef" ,调用该函数如下
write_string(1,5,"ab cd ef;")
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void write_string(uchar hang,uchar add,uchar *p)
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
while(1)
{
if(*p == '\0') break; //\0字符串的结尾标志 break结束while循环 结束写字符
write_data(*p); //写数据
p++; //指针地址加1
}
}
/***********************lcd1602上显示两位十进制数************************/
void write_lcd2_ds1302(uchar hang,uchar add,uchar date)
14
{
if(hang==1)
write_com(0x80+add); //写1602第一行的地址
else
write_com(0x80+0x40+add); //写1602第二行的地址
write_data(0x30+date / 16);
write_data(0x30+date % 16);
}
/*****************控制光标函数********************/
void write_guanbiao(uchar hang,uchar add,uchar date)
{
if(hang==1)
write_com(0x80+add); //1602写第一行的地址
else
write_com(0x80+0x40+add); //1602写第二行的地址
if(date == 1)
write_com(0x0f); //显示光标并且闪烁
else
write_com(0x0c); //关闭光标
}
/********************************************************************
* 名称 : init_1602()
* 功能 : 初始化1602液晶
* 输入 : 无
* 输出 : 无
***********************************************************************/
void init_1602() //1602初始化
{
write_com(0x38); //显示模式设置:16×2显示,5×7点阵,8位数据接口 不检测忙信号
write_com(0x0c); //开显示 不显示光标
write_com(0x06); //当写一个字符是,地址指针加 1
write_string(1,0,"sd:00km/h 00:00"); //初始化1602显示
write_string(2,0,"lc:00.00km "); //初始化1602显示
}
uchar i;
/*************写一个数据到对应的地址里***************/
void write_ds1302(uchar add,uchar dat)
{
rst = 1; //把复位线拿高
for(i=0;i<8;i++)
{ //低位在前
clk = 0; //时钟线拿低开始写数据
io = add & 0x01;
add >>= 1; //把地址右移一位
clk = 1; //时钟线拿高
}
for(i=0;i<8;i++)
15
{
clk = 0; //时钟线拿低开始写数据
io = dat & 0x01;
dat >>= 1; //把数据右移一位
clk = 1; //时钟线拿高
}
rst = 0; //复位线合低
}
/*************从对应的地址读一个数据出来***************/
uchar read_ds1302(uchar add)
{
uchar value,i;
rst = 1; //把复位线拿高
for(i=0;i<8;i++)
{ //低位在前
clk = 0; //时钟线拿低开始写数据
io = add & 0x01;
add >>= 1; //把地址右移一位
clk = 1; //时钟线拿高
}
for(i=0;i<8;i++)
{
clk = 0; //时钟线拿低开始读数据
value >>= 1;
if(io == 1)
value |= 0x80;
clk = 1; //时钟线拿高
}
rst = 0; //复位线合低
return value; //返回读出来的数据
}
/*************把要的时间 年月日 都读出来***************/
void read_time()
{
miao = read_ds1302(read_add[0]); //读秒
fen = read_ds1302(read_add[1]); //读分
shi = read_ds1302(read_add[2]); //读时
ri = read_ds1302(read_add[3]); //读日
yue = read_ds1302(read_add[4]); //读月
nian = read_ds1302(read_add[5]); //读年
week = read_ds1302(read_add[6]); //读星期
}
/*************把要写的时间 年月日 都写入ds1302里***************/
void write_time()
{
write_ds1302(0x8e,0x00); //打开写保护
16
write_ds1302(write_add[0],miao); //写秒
write_ds1302(write_add[1],fen); //写分
write_ds1302(write_add[2],shi); //写时
write_ds1302(write_add[3],ri); //写日
write_ds1302(write_add[4],yue); //写月
write_ds1302(write_add[5],nian); //写星期
write_ds1302(write_add[6],week); //写年
write_ds1302(0x8e,0x80); //关闭写保护
}
/*************液晶显示时间程序***************/
void init_1602_ds1302()
{
write_lcd2_ds1302(1,11,shi); //显示时
write_lcd2_ds1302(1,14,fen); //显示分
}
/***********外部中断0初始化程序****************/
void init_int0()
{
EX0=1; //允许外部中断0中断
EA=1; //开总中断
IT0 = 1; //外部中断0负跳变中断
}
/*************定时器0初始化程序***************/
void time_init() //定时器0初始化程序
{
EA = 1; //开总中断
TMOD = 0X11; //定时器0、工作方式1
ET0 = 1; //开定时器0中断
TR0 = 1; //允许定时器0定时
ET1 = 0; //关定时器1中断
TR1 = 1; //允许定时器1定时
TH0 = 0x3c;
TL0 = 0xb0; //定时50ms中断一次
}
/***********计算速度函数**************/
void menu_dis() //计算速度函数
{
if(menu_1 == 0)
{
if(flag_1s == 1)
{
flag_1s = 0;
if((flag_en == 1))
{
flag_en = 0;
//1s = 1 / 1000000us; // 1m/s=0.001km除以1/3600h=3.6km/h
f_hz = 1/(TH11 * 256 + TL11) / 1000000 ; //算出来就是秒
17
speed_m = f_hz * zhijing * 3.14 ; //算出来的是m/s
juli_z = juli_z + speed_m; //里程
shudu = speed_m * 3.6 ; //(带个小数点) km/s
zong_lc += speed_m; //总路程m
}
write_lcd2(1,3,shudu); //显示速度
write_lcd4(2,3,juli_z); //显示里程
}
}
}
/********************独立按键程序*****************/
uchar key_can; //按键值
void key() //独立按键程序
{
if(key1 == 0 || key2 == 0 || key3 == 0 || key4 == 0) //有按键按下
{
delay_1ms(1); //按键延时消抖动
if(key1 == 0) //确认是按键按下
key_can = 1; //得到按键值
if(key2 == 0) //确认是按键按下
key_can = 2; //得到按键值
if(key3 == 0) //确认是按键按下
key_can = 3; //得到按键值
if(key4 == 0) //确认是按键按下
key_can = 4; //得到按键值
}
}
/**********************设置函数************************/
void key_with()
{
if(key_can == 1) //设置键
{
menu_1++;
if(menu_1 == 1) //设置时间
{
write_string(1,0," : : W: ");
write_string(2,0," 20 - - ");
}
if(menu_1 == 2) //设置报警速度
{
write_string(1,0,"set-sd:00km/h ");
write_string(2,0,"zlc: ");
}
if(menu_1 == 3) //设置直径
{
write_string(1,0," Set Zhijing ");
write_string(2,0," ");
18
}
if(menu_1 > 3) //回到正常显示
{
menu_1 = 0; //menu_1 = 0 退出设置了,在正常显示界面下
init_1602(); //1602初始化 //初始化液晶显示
}
}
if(key_can == 2) //选择键
{
if(menu_1 == 1) //设置时间
{
menu_2 ++;
if(menu_2 > 7)
menu_2 = 1;
}
if(menu_1 == 2) //设置
{
menu_2 ++;
if(menu_2 > 2)
menu_2 = 1;
}
}
if(menu_1 == 1)
{
if(menu_2 == 1) //设置时
{
if(key_can == 3) //加键
{
shi+=0x01; //设置时钟加1
if((shi & 0x0f) >= 0x0a)
shi = (shi & 0xf0) + 0x10;
if(shi >= 0x24)
shi = 0;
}
if(key_can == 4) //减键
{
if(shi == 0x00)
shi = 0x24;
if((shi & 0x0f) == 0x00)
shi = (shi | 0x0a) - 0x10;
shi -- ; //设置时钟减1
}
}
if(menu_2 == 2) //设置分
{
if(key_can == 3) //加键
{
19
fen+=0x01; //设置分钟加1
if((fen & 0x0f) >= 0x0a)
fen = (fen & 0xf0) + 0x10;
if(fen >= 0x60)
fen = 0;
}
if(key_can == 4) //减键
{
if(fen == 0x00)
fen = 0x5a;
if((fen & 0x0f) == 0x00)
fen = (fen | 0x0a) - 0x10;
fen -- ; //设置分钟减1
}
}
if(menu_2 == 3) //设置秒
{
if(key_can == 3) //加键
{
miao+=0x01; //设置秒钟加1
if((miao & 0x0f) >= 0x0a)
miao = (miao & 0xf0) + 0x10;
if(miao >= 0x60)
miao = 0;
}
if(key_can == 4) //减键
{
if(miao == 0x00)
miao = 0x5a;
if((miao & 0x0f) == 0x00)
miao = (miao | 0x0a) - 0x10;
miao -- ; //设置秒减1
}
}
if(menu_2 == 4) //设置星期
{
if(key_can == 3) //加键
{
week+=0x01; //设置星期加1
if((week & 0x0f) >= 0x0a)
week = (week & 0xf0) + 0x10;
if(week >= 0x08)
week = 1;
}
if(key_can == 4) //减键
{
if(week == 0x01)
20
week = 0x08;
if((week & 0x0f) == 0x00)
week = (week | 0x0a) - 0x10;
week -- ; //设置星期减1
}
}
if(menu_2 == 5) //设置年
{
if(key_can == 3) //加键
{
nian+=0x01; //设置年加1
if((nian & 0x0f) >= 0x0a)
nian = (nian & 0xf0) + 0x10;
if(nian >= 0x9a)
nian = 1;
}
if(key_can == 4) //减键
{
if(nian == 0x01)
nian = 0x9a;
if((nian & 0x0f) == 0x00)
nian = (nian | 0x0a) - 0x10;
nian -- ; //设置年减1
}
}
if(menu_2 == 6) //设置月
{
if(key_can == 3) //加键
{
yue+=0x01; //设置月加1
if((yue & 0x0f) >= 0x0a)
yue = (yue & 0xf0) + 0x10;
if(yue >= 0x13)
yue = 1;
}
if(key_can == 4) //减键
{
if(yue == 0x01)
yue = 0x13;
if((yue & 0x0f) == 0x00)
yue = (yue | 0x0a) - 0x10;
yue -- ; //设置月减1
}
}
if(menu_2 == 7) //设置日
{
if(key_can == 3) //加键
21
{
ri+=0x01; //设置日加1
if((ri & 0x0f) >= 0x0a)
ri = (ri & 0xf0) + 0x10;
if(ri >= 0x32)
ri = 0;
}
if(key_can == 4) //减键
{
if(ri == 0x01)
ri = 0x32;
if((ri & 0x0f) == 0x00)
ri = (ri | 0x0a) - 0x10;
ri -- ; //设置日减1
}
}
write_lcd2_ds1302(1,2,shi); //显示时
write_lcd2_ds1302(1,5,fen); //显示分
write_lcd2_ds1302(1,8,miao); //显示秒
write_lcd1(1,14,week); //显示星期
write_lcd2_ds1302(2,3,nian); //显示年
write_lcd2_ds1302(2,6,yue); //显示月
write_lcd2_ds1302(2,9,ri); //显示日
switch(menu_2) // 光标显示
{
case 1: write_guanbiao(1,2,1); break;
case 2: write_guanbiao(1,5,1); break;
case 3: write_guanbiao(1,8,1); break;
case 4: write_guanbiao(1,14,1); break;
case 5: write_guanbiao(2,3,1); break;
case 6: write_guanbiao(2,6,1); break;
case 7: write_guanbiao(2,9,1); break;
}
write_time(); //把时间写进去
}
if(menu_1 == 2)
{
if(menu_2 == 1) //设置报警速度
{
if(key_can == 3) //加键
{
bj_shudu++; //设置报警速度加1
if(bj_shudu >= 99)
bj_shudu = 99;
}
if(key_can == 4) //减键
{
22
if(bj_shudu != 0)
bj_shudu -- ; //设置报警速度减1
}
}
if(menu_2 == 2) //把总量程清零
{
if(key_can == 3)
{
zong_lc= 0;
}
if(key_can == 4)
{
zong_lc= 0;
}
}
write_lcd2(1,7,bj_shudu); //显示报警速度
write_lcd7(2,4,zong_lc); //显示总量程
switch(menu_2) // 光标显示
{
case 1: write_guanbiao(1,6,1); break;
case 2: write_guanbiao(2,3,1); break;
}
}
if(menu_1 == 3) //设置直径
{
if(key_can == 3) //加键
{
s_zhijing++;
if(s_zhijing >= 999)
s_zhijing = 999;
}
if(key_can == 4) //减键
{
if(s_zhijing != 0)
s_zhijing -- ;
}
zhijing = s_zhijing ;
write_lcd2(2,5,s_zhijing); //显示直径
}
}
/****************报警函数***************/
void clock_h_l()
{
if(shudu >= bj_shudu) //速度大于等于报警速度
{
beep = ~beep; //蜂鸣器报警
}
23
else
{
beep = 1; //关闭蜂鸣器
}
}
/******************主程序**********************/
void main()
{
init_1602(); //1602初始化
init_int0(); //外部中断0初始化程序
time_init(); //定时器0初始化程序
while(1)
{
key(); //按键程序
key_with(); //按键设置程序
if(flag_200ms == 1)
{
flag_200ms = 0;
read_time(); //读时间
init_1602_ds1302(); //显示时钟
menu_dis(); //计算速度函数
clock_h_l(); //报警函数
}
}
}
/*********************外部中断0中断服务程序************************/
void int0() interrupt 0
{
uchar value;
value ++;
if(value >= 2)
value =0;
switch(value)
{
case 0:
TH1 = 0;
TL1 = 0;
break;
case 1:
TH11 = TH1;
TL11 = TL1;
flag_en = 1;
break;
}
}
/*************定时器0中断服务程序***************/
void time0_int() interrupt 1
{
uchar value;
TH0 = 0x3c;
TL0 = 0xb0; //定时50ms中断一次
value++;
if(value % 4 == 0)
flag_200ms = 1;
if(value >= 20) //1秒 才是一秒钟的速度
{
value = 0;
flag_1s = 1;
}
}
五 系统可靠性设计和功能实现
(一) 硬件系统的可靠性设计方法
在设计电子速度里程表时,我遵守了如下的原则:
1.在设计中尽可能地使用与单片机相适应的典型电路,使得我在进行模块化系统时更加方便。这一次的电路设计,
在教科书上基本都能找到相关知识,理论上也和教科书上的差不多,有了参照,就能省去很多麻烦。我将整个电路
模块化,就是为了更加简洁明了,但是一旦系统出了问题,就能有针对性的解决。
2.总体上,为简化硬件架构、降低成本、增加可靠性,将软件系统所能完成的功能尽量用软件来完成,也就是尽量
采用软件代替硬件。但是也要注意,有软件实现的功能其反应速度要大于使用硬件的。所以,在一些功能的选择
上,必须从响应速度、实时性等方面考虑。
(二) 软件系统的可靠性设计
为了提高系统的可靠性,进行代码的编写时,是按照每个模块的功能去编写的,每个模块的代码单独编写,当
某个模块不能正常运行时,方便去查找错误。