基于单片机的里程速度测试表设计

系统简介

为了能够有效的应用霍尔传感器实现转速直接转化成为数字脉冲信号,就能够将该信息传输给单片机进行处理,并且通过规定程序能够计算出数字脉冲的频率,然后再实现计数数值能够在显示器中展示出来。同时该装置内安装有蜂鸣器装置,在速度达到规定要求后就会发生声响,使得操作者可以按照要求减速,考虑到信号的衰减、干扰等影响,利用单片机在信号输入前应对其进行放大整形[1]。
本文所设计的系统中,将STC89C52单片机作为主控核心,霍尔传感器能够直接获取转速信息,就能够确定自行车速度/里程等信号,并且通过其内部处理器EEPROM能够保存所有信息,然后将速度、里程等信息通过LED显示,同时还能够应用DS1302来显示日期和时间。本文主要目的就是设计自行车速度里程表硬件与软件部分。硬件有传感器、单片机等部件设计,还有各个部分的电路设计;软件采用的是C语言完成各个系统的变成,达到模块化设计标准。整个系统结构简单,性能优越,符合正常的使用标准要求。
结果表明,测量脉冲频率的方式来确定速度数据,精度较高,且稳定性好,可以满足多种条件的使用要求。该系统硬件电路简单,子程序具有通用性,完全符合设计要求。

关键词:单片机;霍尔传感器;液晶显示;脉冲信号

1系统方案设计

1.1 硬件方案的选择
硬件设计是非常关键的,就是进行各个结构部件的分配和组合,并且按照特定的原理进行连接,使得整个系统可以稳定的运行,达到人们所需要的性能标准要求。因此,在硬件搭建之前,首先要进行方案的确定,明确各个模块选择最佳的方案,以达到最佳的使用效果。
1.1.1主控芯片的选择
方案一:
STC89C52RC这是一款低耗高能的芯片,为STC公司生产,主要的优势就是功耗低、性能高,为CMOS8位微控制器。其内部采用的是最为经典的MCS-51内核,但是对其进行了必要的改进和完善,较之传统51单片机来说,性能更加的优越,兼容性也比较好,可以满足多种条件使用要求。对于单芯片来说,其内部具备相对比较灵活的8位CPU 和可编程Flash,能够保证在系统运行中更加的高效和灵活。该单片机最为主要的功能如下所示: 8k字节Flash,512字节RAM,32 位I/O 口线,内置 EEPROM,MAX810复位电路以及3个16 位定时器/计数器。在系统切换到空闲模式的条件之下运行,此时的CPU 不工作,RAM、定时器/计数器等能够继续工作,也具备一定的功能。在突然断电的情况下,由于掉电保护RAM并不会丢失,而是能够被保存到系统内,但此时的单片机任何工作都不能进行[4]。
方案二:
选择使用MSP430单片机为系统中的主控芯片。该装置通常也被叫做是混合信号处理器的装置,能够根据不同的要求应用到不同功能电路中,使用范围也比较多,是一款美国生产的单片机部件,最为重要的优势就是其功耗非常低、指令集比较简单,信号处理的精度非常高。
方案三:
选择使用PIC16F877A单片机作为系统的主控芯片。这是一款新研发和投入使用的芯片部件,并且是8位单片机微机,主要的作用就是能够实现程序内存,并且可以根据使用者的需要进行程序擦写,以满足不同使用的需求。
方案二中的单片机一般都是使用到有电池作为供电装置的便携式设备中,并且系统开发难度是比较高的,成本比较高。因此,很多简单系统设计都不会选择这种方式。方案三的部件价格是比较高的,设计难度也比较大,所以很多情况也不会选择该方案。
经过综合对比分析,考虑到设计开发的成本、资源利用等方面,最终确定是用方案一作为本次设计的依据,应用STC89C52单片机为主控芯片。
1.1.2显示器件的选择
方案一:
使用LED进行数码管动态显示。该部件的价格相对比较适中,现实数字、字母都比较好,但是要进行动态扫描法应用时,会需要占用CPU的I/O口较多,而此时的电路中电流是明显不足的,还需要配置驱动电路才能满足该装置的电流运行要求。
方案二:
采用LCD1602液晶显示屏。该装置的功能是非常高的,能够按照使用者的需求显示出162即32个字符,可任何形式的字符都能够显示出来,准确、清晰都是非常好的。该显示器中所显示的字符全部都是通过57点阵来形成,并且能够通过串行数据传输的方式,系统控制操作也比较简单,与应用最为普遍的HD44780原理是相同的,所以操作非常简单。
方案三:
采用LCD12864液晶显示屏,这种显示装置的分辨率是比较高的,通常可以达到128*64。主要的优势就是连接比较方便,接口设置非常的灵活,操作指令也比较简单,可以根据系统的设置来形成人机交互图形,并且功耗非常低。
虽然在方案三中所选择的部件形式功能性是最强的,但是显示内容较大,所以就是其内部空间浪费相对严重,且成本是非常高的。方案一虽然其成本比较低,操作也简单,但是要进行焊接连接,所以容易导致焊接操作中存在错误的情况。
经过上述对比分析,最终该系统设计中选择使用方案二LCD1602液晶显示器来作为本次系统显示装置,应用比较合理。
1.1.3时钟器件的选择
方案一:
选择使用单片机中的内部定时器来进行定时处理,此时如果系统因为外力作用中断运行,就会导致所有的数据都会被清零,无法继续及时,并且精度也是比较差的,在短时间内精度还行,如果时间长了误差累计的越来越多时间也就偏差多了。
方案二:
选择使用DS1302时钟芯片作为系统设计的显示时钟部分,该装置是美国生产的,功耗较低,充电性能也比较优越,还能够按照闰年补偿,同时能够准确的记录各个时间,系统的工作电压为2.0V~5.5V。与主控部件的接口部分采用的是三线接口。这是一款新型的产品,较之以往的产品,具备更高的兼容性,还有主、备用电源引脚,充电能力比较强,可以达到多方面使用功能的要求。
虽然采用方案一可以节省电路的搭建也节省了成本,但是不能在系统掉电的情况下也正常的工作。方案二中设置有主、备用电源引脚,所以能够达到涓流充电的效果,其主要的优势就是在于串行数据传输,可以根据需要关闭充电功能,达到系统连续控制的效果。经过多方面的对比分析,考虑到功能优劣、产品成本等因素,最终选择使用DS130时钟芯片作为本次时钟部件。
1.1.4测速器件的选择
方案一:
选择使用光电速度传感器,光电的速度传感器是基于光电子的速度传感器。转换原理基本可以分为透明速度传感器和反射式。如果光电池被照射,其反电阻低。因此,是电脉冲信号。如果光源被板覆盖着。光伏的逆电阻相对较大,且没有信号输出[5]。这样,可以根据盘上的孔和凹陷的数量来测定研究对象的速度。通常盘上的孔或凹进去的数量是静态的,每次光电转换器旋转时会出现60个脉冲信号。电子计数器的时间信号1s,可以直接读取测量的波速。这个反射式速度传感器的原理与上述的类型原理较为接近。类似感测到的光也被光学元件转换成电信号,但不同于光信号。对光的反射脉冲信号中将测量轴的测量部分施加反射材料,得到反射面。这个普通的反射材料是特殊的速度反射纸,也被铝箔和其他的反射材料代替。有时可以作为反射面涂在测定部件上[6]。该投影仪与反射面的距离为5-15m,当测定波旋转时,接收脉冲,为了测定测定对象的速度,将对应的电信号发送到电子计数器。
方案二:
霍尔传感器用于测量速度。在待测旋转体的轴上安装有一个磁盘,磁盘上安装有几对小磁钢。小磁钢对数越多,能展现出的分辨力越高。霍尔开关将被装配在小磁钢周围。当旋转体通过某一个角速度m发生旋转的时候,小磁钢每次转动霍尔开关,霍尔开关都会输出对应的脉冲数量值,并可以得到在单位时间内所输出的对应脉冲数,从而来获得旋转体的实时速度。
综上所述,光电式速度传感器受外部光源影响的波动很高,不适用于运动物体的速度测量;集成霍尔开关传感器具有灵敏度高、可靠性好、体积小、无接触、无磨损、使用年限久等优势,消耗低,对外界环境依赖性小,基于此,霍尔传感器被选用来对速度进行监测。
1.2总体设计方案选择
经过上述的对本设计中涉及到的每一个模块进行了分析,最后的方案为:以STC89C52单片机为控制核心、DS1302做计时任务、LCD1602发挥显示数字的作用、通过霍尔传感器感应进行测速记录里程、数据存储部分通过STC89C52单片机内部的EEPROM进行存储。系统还会通过设定的超速限值与实测值进行对比当超过限值系统会发出鸣叫提示超速。整体的硬件设计框图如图1.1所示。

图1.1 系统硬件框图

2.系统硬件电路设计

2.1ST89C52单片机系统设计
2.1.1ST89C52的概述
STC89C52共有32个I/O口,其中LCD1602液晶显示屏模块需要11个,蜂鸣器报警模块需要1个,按键模块需要1个,RFID模块需要5个,下载口需要2个。该单片机采用经典的MCS-51内核,但由于受到了很多的改造的影响,使该芯片与传统51单片机所具备的功能有很大的区别。STC89C52为许多定制的微型计算机应用、智能8位处理器和闪存编程系统,并且可以提供多样、自由、高效的具体实施例[7]。相应的单片机功能性引脚见图2.1。

图2.1STC89C52单片机引脚
2.1.2单片机的最小系统
单片机的最小系统简单来说就是通过最小的元件与模块的搭建,使单片微机具有和普通单片机一样的功能,即被视为最小系统。其基本的原理见图2.2。

图2.2单片机最小系统原理图

源程序代码
#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;
uchar i;

unsigned long speed1,juli,time2;
float f_hz ,speed_km,speed_m;

uchar TH11,TL11;
uchar flag_en; //开始计算速度使能

uint juli_s; //每秒走的距离
uint juli_z; //总路程
float zhijing = 0.55; //直径 0.55M
uint s_zhijing = 55;

bit flag_1s = 1; //1s
uchar menu_1; //菜单设置变量
uchar menu_2; //菜单设置变量

long zong_lc; //总量程

uchar flag_200ms;
uint shudu; //定义速度的变量
uint bj_shudu = 50; //报警速度

uchar f_pwm_l = 20; // //f_pwm_l
sbit pwm = P2^0;

//这三个引脚参考资料
sbit rs=P1^0; //寄存器选择信号 H:数据寄存器 L:指令寄存器
sbit rw=P1^1; //寄存器选择信号 H:数据寄存器 L:指令寄存器
sbit e =P1^2; //片选信号 下降沿触发

uchar code table_num[]=“0123456789abcdefg”;
uchar i;

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)
    {
    i =0;
    e=0;
    rs=0;
    rw=0;
    P0=com;
    delay_uint(3);
    e=1;
    delay_uint(25);
    e=0;
    }
  • /********************************************************************

  • 名称 : write_data(uchar dat)
  • 功能 : 1602写数据函数
  • 输入 : 需要写入1602的数据
  • 输出 : 无
    ***********************************************************************/
    void write_data(uchar dat)
    {
    i =0;
    e=0;
    rs=1;
    rw=0;
    P0=dat;
    delay_uint(3);
    e=1;
    delay_uint(25);
    e=0;
    }
  • /********************************************************************

  • 名称 : write_sfm2(uchar hang,uchar add,uchar date)
  • 功能 : 显示2位十进制数,如果要让第一行,第五个字符开始显示"23" ,调用该函数如下
    write_sfm1(1,5,23)
  • 输入 : 行,列,需要输入1602的数据
  • 输出 : 无
    ***********************************************************************/
    void write_sfm2(uchar hang,uchar add,uint date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    write_data(0x30+date/10%10);
    write_data(0x30+date%10);
    }
  • /********************************************************************

  • 名称 : write_sfm4(uchar hang,uchar add,uchar date)
  • 功能 : 显示2位十进制数,如果要让第一行,第五个字符开始显示"23" ,调用该函数如下
    write_sfm1(1,5,23)
  • 输入 : 行,列,需要输入1602的数据
  • 输出 : 无
    ***********************************************************************/
    void write_sfm4(uchar hang,uchar add,uint date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    write_data(0x30+date/10000%10);
    write_data(0x30+date/1000%10);
    write_data(‘.’);
    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_sfm7(uchar hang,uchar add,uint date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    write_data(0x30+date/100000%10);
    write_data(0x30+date/100000%10);
    write_data(0x30+date/10000%10);
    write_data(0x30+date/1000%10);
    write_data(‘.’);
    write_data(0x30+date/100%10);
    write_data(0x30+date/10%10);
    write_data(‘k’);
    write_data(‘m’);
    }

    /lcd1602上显示两位十进制数*/
    void write_sfm1(uchar hang,uchar add,uchar date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    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);
    else
    write_com(0x80+0x40+add);
    while(1)
    {
    if(*p == ‘\0’) break;
    write_data(*p);
    p++;
    }
    }
  • /lcd1602上显示两位十进制数*/
    void write_sfm2_ds1302(uchar hang,uchar add,uchar date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    write_data(table_num[date / 16]);
    write_data(table_num[date % 16]);
    }

    /控制光标函数***/
    void write_guanbiao(uchar hang,uchar add,uchar date)
    {
    if(hang==1)
    write_com(0x80+add);
    else
    write_com(0x80+0x40+add);
    if(date == 1)
    write_com(0x0f); //显示光标并且闪烁
    else
    write_com(0x0c); //关闭光标
    }

    /********************************************************************

  • 名称 : init_1602()
  • 功能 : 初始化1602液晶
  • 输入 : 无
  • 输出 : 无
    ***********************************************************************/
    void init_1602() //1602初始化
    {
    write_com(0x38);
    write_com(0x0c);
    write_com(0x06);
    delay_uint(1000);
    write_string(1,0,“sd:00km/h 00:00”);
    write_string(2,0,"lc:00.00km ");
    }
  • 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++)
    {
    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); //打开写保护
    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); //关闭写保护
    }

    /定时器0初始化程序**/
    void init_1602_ds1302()
    {
    write_sfm2_ds1302(1,11,shi);
    write_sfm2_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 = 1; //开定时器1中断
    TR1 = 1; //允许定时器1定时
    }

    /计算速度函数***/
    void menu_dis() //计算速度函数
    {
    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 ; //算出来就是秒
    speed_m = f_hz * zhijing * 3.14 ; //算出来的是m/s
    juli_z = juli_z + speed_m; //总路程m
    speed_km = speed_m * 3.6 ; //(带个小数点) km/s
    shudu = speed_km;
    zong_lc += speed_m;
    }
    write_sfm2(1,3,shudu);
    write_sfm4(2,3,juli_z);
    }
    }

    /***独立按键程序/
    uchar key_can; //按键值

    void key() //独立按键程序
    {
    if((P3 & 0x78) != 0x78) //按键按下
    {
    delay_1ms(1); //按键消抖动
    if((P3 & 0x78) != 0x78)
    { //确认是按键按下
    switch(P3 & 0x78)
    {
    case 0x70: key_can = 4; break; //得到按键值
    case 0x68: key_can = 3; break; //得到按键值
    case 0x58: key_can = 2; break; //得到按键值
    case 0x38: key_can = 1; break; //得到按键值
    }
    }
    }
    }

    /设置函数**/
    void key_with()
    {
    //手动调速
    if(key_can == 4) //键
    {
    if(f_pwm_l == 0)
    {
    f_pwm_l = 60;
    }
    f_pwm_l -= 10;
    write_sfm1(2,15,f_pwm_l/10); //显示
    }
    if(key_can == 3) //键
    {
    f_pwm_l += 10;
    if(f_pwm_l >= 60)
    {
    f_pwm_l = 0;
    }
    write_sfm1(2,15,f_pwm_l/10); //显示
    }
    if(key_can == 1) //设置键
    {
    menu_1++;
    if(menu_1 == 1) //设置时间
    {
    menu_2 = 1;
    write_string(1,0," : : W: “);
    write_string(2,0,” 20 – – ");
    }
    if(menu_1 == 2) //设置报警速度
    {
    menu_2 = 1;
    write_string(1,0,"set-sd:00km/h ");
    write_string(2,0,"zlc: “);
    }
    if(menu_1 == 3) //设置直径
    {
    menu_2 = 1;
    write_string(1,0,” Set Zhijing “);
    write_string(2,0,” ");
    }
    menu_2 = 1;

    	if(menu_1 > 2)    //回到正常显示
    	{
    		menu_1 = 0;
    		write_guanbiao(1,2,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;
    			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 -- ; 
    		}	  				
    	}
    	if(menu_2 == 2)		  //设置分
    	{
    		if(key_can == 3)	//加
    		{
    			fen+=0x01;
    			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 -- ;
    		}	
    	}
    	if(menu_2 == 3)		  //设置秒
    	{
    		if(key_can == 3)	//加
    		{
    			miao+=0x01;
    			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 -- ;			
    		}
    	}
    	if(menu_2 == 4)		  //设置星期
    	{
    		if(key_can == 3)	//加
    		{
        		week+=0x01;
    			if((week & 0x0f) >= 0x0a)
    				week = (week & 0xf0) + 0x10;
    			if(week >= 0x08)
    				week = 1;
    		}		
    		if(key_can == 4)	//减	  
    		{
    			if(week == 0x01)
    				week = 0x08;
    			if((week & 0x0f) == 0x00)
    				week = (week | 0x0a) - 0x10;
    			week -- ;
    		}	
    	}
    	if(menu_2 == 5)		  //设置年
    	{
    		if(key_can == 3)	//加
    		{
    	    	nian+=0x01;
    			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 -- ;		
    		}	
    	}
    	if(menu_2 == 6)		  //设置月
    	{
    		if(key_can == 3)	//加
    		{
    	    	yue+=0x01;
    			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 -- ;					
    		}	
    	}
    	if(menu_2 == 7)		  //设置日
    	{
    		if(key_can == 3)	//加
    		{
        	ri+=0x01;
    		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 -- ;			
    		}	
    	}
    	write_sfm2_ds1302(1,2,shi);	   //显示时
    	write_sfm2_ds1302(1,5,fen);	   //显示分
    	write_sfm2_ds1302(1,8,miao);	   //显示秒
    	write_sfm1(1,14,week);	   //显示星期					
    	write_sfm2_ds1302(2,3,nian);	   //显示年
    	write_sfm2_ds1302(2,6,yue);	   //显示月
    	write_sfm2_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++;
    			if(bj_shudu >= 99)
    				bj_shudu = 99;
    		}	
    		if(key_can == 4)	//减	  
    		{
    			if(bj_shudu != 0)
    				bj_shudu -- ;			
    		}
    	}	
    	if(menu_2 == 2)		  //把总量程清零
    	{
    		if(key_can == 3)	
    		{
    			zong_lc= 0;
    		}	
    		if(key_can == 4)		  
    		{
    			zong_lc= 0;			
    		}
    	}	
    
    
    	write_sfm2(1,7,bj_shudu);   //显示报警速度
    	write_sfm7(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_sfm2(2,5,s_zhijing);		//显示
    }
    

    }

    /*报警函数/
    void clock_h_l()
    {
    if((shudu >= bj_shudu))
    {
    beep = ~beep; //蜂鸣器报警
    }
    else
    {
    beep = 1;
    }
    }

    /主程序****/
    void main()
    {
    P0 = P1 = P2 = P3 = 0xff; //IO口初始为电平
    init_1602(); //1602初始化
    init_int0();
    time_init(); //定时器0初始化程序
    while(1)
    {
    key(); //按键程序
    key_with();
    if(flag_200ms == 1)
    {
    flag_200ms = 0;
    read_time(); //读时间
    if(menu_1 == 0)
    init_1602_ds1302(); //显示时钟
    menu_dis(); //计算速度函数
    clock_h_l(); //报警函数
    }
    }
    }
    /外部中断0中断服务程序***/
    void int0() interrupt 0
    {
    static uchar value=1;
    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=1;
    uchar value_l;
    TH0 = 0x3c;
    TL0 = 0xb0; // 50ms
    value++;
    if(value % 4 == 0)
    flag_200ms = 1;

    if(value >= 20)  //1秒   才是一秒钟的速度
    {
    	value = 0;
    	flag_1s = 1;
    }
    if(pwm==1)				   //输出PWM高电平时间	 
    {
    	value_l += 1;
    	if(value_l  > 100 - f_pwm_l)  //f_pwm_l越小就越快速度	
    	{
    		value_l=0;
    	    if(f_pwm_l != 0)
    		{
    			pwm  = 0;	 	
    				//输出PWM低电平时间
    		}
    	}
    }
    else		//输出PWM低电平时间	 		
    {
    	value_l += 1;
    	if(value_l> f_pwm_l)		 //f_pwm_l越大就越慢速度
    	{
    		value_l=0;				 //当value_l  > f_pwm_l时 
    		pwm=1;				 //输出PWM高电平时间
    	}
    }
    

    }

    
    
    
    
    ## 4.系统整体调试
    
    4.1系统硬件测试
    对硬件电路进行检测的过程当中,一般检查有没有一些漏焊、断路等情况,同时检查原件有没有出现方向错误以及设计错误等一些问题。
    在漏焊以及元件方向错误方面,主要是把相应的电路板和PCB图当中存在的一些线路进行对照,查看在实物当中有没有出现相应的元件,他说没有的话,那么就需要进行重新对照,并作出补焊等一些工作。
    在短路以及断路方面,主要利用数字万能表来进行解决,在二极管档位当中加入所需的数字万能表,利用红表笔以其黑表笔能够使其出现警示信号,以此来对这些问题进行相应的检测。在对元件以及导线进行检测时,使用两根表笔来进行相应的操作,倘若能够导通的话,那么蜂鸣器就会发出一定的声音。因此能够按照相应的一些情况,以及检测时所产生的一些现象检查线路有没有出现一定的问题。
    在进行焊接方面,一般主要使用的是手工焊接,如今有很多的工厂在进行焊接时摒弃了传统的一些方法,不过在一些普通器件方面还是会通过手工焊接的方式来进行相应的操作。焊接的成功时项目是否成功的关键要素之一,如果焊接本质上出现问题,则会影响到整个控制系统的。所以在焊接的过程中一定要十分的小心。焊接的步骤如下:
    (1)检查元器件:在进行焊接时要对所要利用的元器件进行相应的检查,确保没有出现一定的损坏,之后才能够安装和焊件,以防出现在焊接完成后因某个元件有损坏而使系统无法正常运行。
    (2)放置、焊接各元件:检查好元器件之后,要根据原理图排好各元件位置,优先放置或焊接较低的元件,然后再对位置相对比较高的一些元件进行放置,特别是一些容易产生损坏问题的元件应该在结束的时候进行焊接,而且时间要小于10秒。进行焊接时如果焊锡不充足的话,那么就要补焊锡,而且也需要严格把控焊锡的量,防止和另外的一些器件进行连接[19]。另外也存在焊锡太多的现象,此时需要通过电烙铁来对剩余的一些焊锡进行处理,也可以使用吸锡器除焊锡。焊接完成如图4.1、4.2所示。
    ![在这里插入图片描述](https://i3.wp.com/i-blog.csdnimg.cn/direct/54bf46591a0d4018ad506a731ad50d88.png)
    
    图4.1焊接实物图
    ![在这里插入图片描述](https://i3.wp.com/i-blog.csdnimg.cn/direct/da7d36f023a14884ae17072f76f4a234.png)
    
    图4.2电路板图片
    
    ## 结 论
    
    本设计主要采用STC89C52单片机作为核心控制器,包括霍尔元件模块,液晶显示采集模块,蜂鸣器报警等模块,多模块的配合最终实现了里程速度测试表系统。本文主要完成了系统设计方案论证和总体框架的设计,设计了系统整体和各模块工作的程序框图;控制模块采用STC89C52单片机作为系统核心控制器。
    本课题主要任务是以单片机为核心利用霍尔传感器测速来设计一个可用LED显示里程和速度的自行车速度里程表。本文主要介绍了自行车的速度里程表的设计思路、电路原理和元件的选择等各个方面的内容,整体上大致可分为硬件部分设计和软件部分设计。目的是能实时地将所测的速度与累计里程数显示出来,主要是将传感器输入到单片机的脉冲信号的频率 (传感器将不同车速转变成不同频率的脉冲信号) 实时地测量出来, 考虑到信号的衰减、干扰等影响, 在信号送入单片机前应对其进行放大整形,然后通过单片机计算出速度和里程, 再将所得的数据存储到串口数据存储器,并由LED显示模块交替显示所测速度与里程。
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    作者:q_1039692211

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于单片机的里程速度测试表设计

    发表回复