• 基于单片机的自行车码表


    最近毕业设计中接了别人几个项目,忙完一阵是时候做一些总结。

    项目名:基于单片机的自行车码表设计

    功能需求: 1可以测量当前的温度和湿度并显示出来。

                       2时间显示。

                       3实时速度和行驰里程显示。

                       4设定里程提醒。

    根据功能需求决定硬件:单片机使用STC125A60S2具有足够的资源来进行开发(使用51系列的单片也可行,并且价格更低),显示使用LCD1602,速度使用磁力传感器,温度和湿度使用DHT11传感器。时间计数应该使用外部的时钟芯片(由于没有芯片只能使用单片内部的定时器实现^^误差较大)。提醒声音使用了无源蜂鸣器(PWM波控制).

     整体硬件设计图

    IO脚定义

    #ifndef  _CURRENCY_H_
    #define  _CURRENCY_H_
    #include "stc12c5a60s2.h    "
    //currency.h
    typedef unsigned char unchar;
    typedef unsigned int  unint;
    
    
    sbit Data=P3^5;                 
    sbit rs=P2^7;     
    sbit rw=P2^6;     
    sbit e =P2^5;
    sbit buzzer = P3^4;//蜂鸣器     
    
    #endif

    主函数部分

    /*******************************************************************************
    *  描述:                                                                       *
    *         1602字符型LCD显示演示程序                                            *
    *         在第一行显示  里程 时间                                                *
    *         在第二行显示  速度 温度                                         *
    *                                                                              *
    ********************************************************************************/
    
    #include <stc12c5a60s2.h>
    #include "lcd1602.h"
    #include "DHT11.h"
    #include "delay.h"
    #include "currency.h"
    
    unchar  test[10];
    unsigned long time_ms;    
    unsigned long last_time;
    unsigned long distance_cm;        //厘米
    unsigned int  speed;           //百米/时
    unsigned int  tempr;              //0.1度
    unsigned int  n ;
    unsigned char tm ;
    unsigned int  distance;
    bit           menu=0;
    bit           clear_flag;
    unsigned long time_menu;
    
    
    void Timer0Init(void)        //10毫秒@12.000MHz
    {
        AUXR &= 0x7F;          //定时器时钟12T模式
        TMOD &= 0xF0;          //设置定时器模式
        TMOD |= 0x01;          //设置定时器模式
        TL0 = 0xF0;           //设置定时初值
        TH0 = 0xD8;            //设置定时初值
        TF0 = 0;               //清除TF0标志
        TR0 = 1;
        ET0 = 1;              //定时器0开始计时
        EA  = 1;
    }
    //--------------------------------------------------------
    //算法实现
    //--------------------------------------------------------
    void show()
    {
        unsigned char c;
        unsigned char h,m,s;
        unsigned int time;
        
    
            lcd_pos(0);
        distance = distance_cm / 10000;
        c = (distance / 1000) % 10 + '0';  //百位
        write_data(c);             //写入数据
        c = (distance / 100) % 10 + '0';   //十位
        write_data(c);
        c = (distance / 10) % 10 + '0';     //个位
        write_data(c);
        write_data('.');
        c = distance  % 10 + '0';          //小数一位                                     
        write_data(c);
        //进行位提取
        write_data('k');
        write_data ('m');
    
        time = time_ms/1000;
        h = time / 3600;
        m = (time % 3600) / 60;
        s = time % 60;
            lcd_pos(9);
        c = h % 10 + '0';
        write_data(c);
        write_data(':');
        c = (m / 10)  + '0';
        write_data(c);
        c = (m % 10)  + '0';
        write_data(c);
        write_data(':');
        c = (s / 10)  + '0';
        write_data(c);
        c = (s % 10)  + '0';
        write_data(c);
        //时间提取
            lcd_pos(0x40);           //定位第二行
        c = (speed / 100) % 10 + '0';      //取百位
        write_data(c);                     
        c = (speed / 10) % 10 + '0';       //取十位
        write_data(c);
        write_data('.');
        c = speed  % 10 + '0';           //取个位
        write_data(c);
        write_data('k');
        write_data('m');
        write_data('/');
        write_data('h');
        //实时提取
    
    } 
    
    //开始
    void main()
    {
          int i;
          init_1602();          //lcd1602初始化
          delay_ms(1500);      //DHT11上电后要等待1.5S以越过不稳定状态在此期间不能发送任何指令
          Timer0Init();
          IT0=1;               //外部中断0下降沿触发
          EX0=1;
          IT1=1;                  //外部中断1下降沿触发
          EX1=1;               //外部中断开启动        
          distance_cm = 0;
           time_ms = 0;
          last_time = 0;
          tempr = 324;
          while(1)
         {
             
            if((time_ms - last_time) > 5000)
                speed = 0;    
                P1 = 0xff;
                if((P1 & 0x02) == 0)
                   {
                   distance_cm = 0;
                   time_ms = 0;
                    }
    
                if(menu == 0)              //菜单0显示
                     {
                 if(clear_flag==1)
                      {      
                                      write_com(0x01);
                          clear_flag = 0;
                      }
    
                  show();
                  if(distance ==15)              //单位是百米
                    {
        
                        Buzzer_Alert();         
        
                    }
                  }
    
    
                  else    if(menu == 1)
                   {
                         if(clear_flag==0)
                         { 
                         wrire_com(0x01);
                         clear_flag = 1;
                              }
                         ET0 = 0;
                         DHT11_receive();
                    }
                          }
    
     }
    
     void T0_Interrupt(void)    interrupt 1      //3定时器1的中断号  1定时器0的中断号 0外部中断1 2外部中断2  4串口中断
    {
             
             TL0 = 0xF0;                    //设置定时初值
             TH0 = 0xD8;                    //设置定时初?
             time_ms += 10;
    //     if(menu)
    //     {
    //      time_menu+= 10;
    //test:
    //      if(time_menu==10000)
    //      {
    //      menu=0;
    //      time_menu=0;
    //      }
    //     }
         
    
    }
    //比较重要------------------------------------
    void  counter(void)         interrupt 0 
    {
       unsigned int intervel = 0;    //
       static unsigned char cnt = 0;
       EX0=0;
       distance_cm+=218;                 //一圈218厘米
       cnt++; 
       if(last_time == 0)
       {
           last_time = time_ms;
           cnt = 0;
       }
       else if(cnt >= 5)
       {
           intervel = time_ms -  last_time;
           last_time = time_ms;
           speed = 360 * 5 * 218 / intervel;   //实时速度统计
           if(speed > 350)
           else 
           cnt = 0;
       }
       EX0=1;
    }
    
    void  key_menu(void)         interrupt 2 
    {
    
               menu=~menu;
            ET0 = 1;
    }
     LCD1602模块函数
    #include "lcd1602.h"
    #include "currency.h"    
    #include "delay.h"
    #include "intrins.h"
    
     /***********************lcd1602写命令函数************************/
    void write_com(unchar com)
    {
        e=0;
        rs=0;
        rw=0;
        P0=com;
        delay_unint(3);
        e=1;
        delay_unint(25);
        e=0;
    }
    
    /***********************lcd1602写数据函数************************/
    void write_data(unchar dat)
    {
        e=0;
        rs=1;
        rw=0;
        P0=dat;
        delay_unint(36);
        e=1;
        delay_unint(300);
        e=0;    
    }
    /***********************lcd1602写数据函数************************/
    void write_data_1(unchar dat)
    {
        e=0;
        rs=1;
        rw=0;
        P0=dat+48;
        delay_unint(432);
        e=1;
        delay_unint(3600);
        e=0;    
    }
    
    
    /*********************光标控制***********************/
    void lcd1602_guanbiao(unchar open_off,unchar add)
    {
        if(open_off == 1)   //开光标
        {
            write_com(0x80+add);              //将光标移动到秒个位
            write_com(0x0f);                  //显示光标并且闪烁
        }
        else 
        {
            write_com(0x0c);           //关光标
        }        
    }
    
    
    /***********************lcd1602上显示两位十进制数************************/
    void write_sfm2(unchar hang,unchar add,unchar date)
    {
        unchar shi,ge;
        if(hang==1)   
            write_com(0x80+add);
        else
            write_com(0x80+0x40+add);
        shi=date%100/10;
        ge=date%10;          
        write_data(0x30+shi);
        write_data(0x30+ge);    
    }
    
    /***********************lcd1602上显示这字符函数************************/
    void write_string(unchar hang,unchar add,unchar *p)
    {
        if(hang==1)   
            write_com(0x80+add);
        else
            write_com(0x80+0x40+add);
            while(1)                                                         
            {
                if(*p == '\0')  break;
                write_data(*p);
                p++;
                delay_unint(600);
            }    
    }
    
    /***********************lcd1602上显示这字符函数************************/
    void write_string_1(unchar hang,unchar add,unchar *p)
    {
        if(hang==1)   
            write_com(0x80+add);
        else
            write_com(0x80+0x40+add);
            while(1)                                                         
            {
                if(*p == '\0')  break;
                write_data_1(*p);
                p++;
                delay_unint(600);
            }    
    }
    
    /***********************lcd1602初始化设置************************/
    void init_1602()
    {
        write_com(0x38);    //
        write_com(0x0c);
        write_com(0x06);
        delay_unint(12000);
        //write_string(1,0," Welcome to use ");    
        //write_string(2,0," Bicycle speed  ");
        //lcd1602_guanbiao(1,7+0x40);  //开光标
    }
    
    
     unchar Lcd1602_ReadBusy()   //判断lcd1602是否处于忙的状态,即读忙
        {
           unchar temp;
           rs=0;
           rw=1;
           _nop_();
           P0=0xff;     //读某IO口数据前,先将该口置为1            
    
        /*原因:电路中存在的一个普遍的现象:高电平很容易被低电平拉低,而低电平一般不可能被高电平拉高。所以在读数据之前将单片机IO口拉高才不会影响原来数据线上的数据!*/
    
           _nop_();
           e=1;
           _nop_();
           temp=P0;   //读取此时lcd1602的状态字
           _nop_();
           e=0;
           return (temp&0x80);  //如果忙
    
           /*状态字为temp(8位2进制数)的最高位,最高位为1表示禁止读写,为0表示允许读写,即temp&0x80得1表示忙,得0表示不忙*/
    
        }
    
    
    
    
     void Lcd1602_WriteData(unchar dat)  //写数据
        { 
                 
           rs=1;   //数据
           rw=0;   //
           _nop_();
           P2=dat;
           _nop_();
           e=1;
           _nop_();
           _nop_();
           e=0;
           _nop_();
           _nop_();   
        }
    
     void lcd_pos(unchar pos)
     {
           write_com(pos | 0x80);
    
     }

    由于DHT11需要比较准确的延时误差在5%

    delay.c延时模块

    #include "delay.h"
    #include "intrins.h"
    #include  "DHT11.h" 
    //--------------------------------------------------------
    //延时1ms           实际0.99ms
    //--------------------------------------------------------
    void delay_ms(unsigned int x)
    {
     while(x--)
     {    
        unsigned char i, j;
    
        _nop_();
        _nop_();
        i = 9;
        j = 120;
        do
        {
            while (--j);
        } while (--i);
    }
    }
    
    
    void delay_unint(unsigned int q)
    {    
        while(q--);
    }
    //--------------------------------------------------------
    //延时60s 实际63us
    //--------------------------------------------------------
    void Delay60us()        //@12.000MHz
    {
        unsigned char i, j;
    
        _nop_();
        _nop_();
        i = 1;
        j = 180;
        do
        {
            while (--j);
        } while (--i);
    }
    
     //--------------------------------------------------------
    //延时80s 实际87us
    //--------------------------------------------------------
    
    void Delay80us()        //@12.000MHz
    {
        unsigned char i, j;
    
        _nop_();
        _nop_();
        i = 1;
        j = 255;
        do
        {
            while (--j);
        } while (--i);
    }
    
    //--------------------------------------------------------
    //延时30s 实际31us
    //--------------------------------------------------------
    
    void Delay30us()        //@12.000MHz
    {
        unsigned char i;
    
        i = 87;
        while (--i);
    }
    
    
        void Delay50us()        //@12.000MHz
    {
        unsigned char i, j;
    
        _nop_();
        _nop_();
        i = 1;
        j = 145;
        do
        {
            while (--j);
        } while (--i);
    }

    DHT11.C 温度采集模块

    #include  "DHT11.h" 
    #include  "currency.h" 
    #include   "stc12c5a60s2.h"
    #include   "delay.h" 
    #include   "lcd1602.h" 
    #include  <intrins.h> 
    
    //------------------------------------
    //function:rec_dat数据组清零
    //------------------------------------
     unchar  rec_dat[9];
    //------------------------------------
    //function:DHT11启动
    //------------------------------------
    void DHT11_start()
    {
       Data=1;
       Delay30us();    
       Data=0;
       delay_ms(25);   //延时18ms以上
       Data=1;
       Delay30us();
    }
    
    //------------------------------------
    //function:DHT11接收一个字节
    //------------------------------------    
    
    unchar DHT11_rec_byte()      //接收一个字节
    {
      
      unchar i,dat=0;
      for(i=0;i<8;i++)    //从高到低依次接收8位数据
       {          
          while(!Data);   ////等待50us低电平过去
          Delay60us();    //延时60us,如果还为高则数据为1,否则为0 
          dat<<=1;        //移位使正确接收8位数据,数据为0时直接移位
          if(Data==1)     //数据为1时,使dat加1来接收数据1
             dat+=1;
          while(Data);    //等待数据线拉低  
        } 
        return dat;
    
    }
    
    
    //------------------------------------
    //function:      接收DHT11的40位的数据
    //------------------------------------
    void DHT11_receive()      //接收40位的数据
    {
        unchar i, R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise;     
        DHT11_start();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        if(Data==0)
    
        {   
        
                
            while(Data==0);          //等待拉高
            Delay80us();             //拉高后延时80us            
            R_H=DHT11_rec_byte();    //接收湿度高八位;
            R_L=DHT11_rec_byte();    //接收湿度低八位  
            T_H=DHT11_rec_byte();    //接收温度高八位  
            T_L=DHT11_rec_byte();    //接收温度低八位
            revise=DHT11_rec_byte(); //接收校正位
            Delay30us();             //结束
            
            if((R_H+R_L+T_H+T_L)==revise)      //校正
            {
             
                RH=R_H;
                RL=R_L;
                TH=T_H;
                TL=T_L;
                 
            } 
            /*数据处理,方便显示*/
            rec_dat[0]='0'+(RH/10);
            rec_dat[1]='0'+(RH%10);
            rec_dat[2]='R';
            rec_dat[3]='H';
            rec_dat[4]=' ';
            rec_dat[5]=' ';
            rec_dat[6]='0'+(TH/10);
            rec_dat[7]='0'+(TH%10);
            rec_dat[8]='C';
            lcd_pos(40);   //定位第二行的第一个
             for(i=0;i<9;i++)
          {
    
            write_data(rec_dat[i]);
    
          }    
        }
            
        
    }

    buzzer,c蜂鸣器模块

    #include  "buzzer.h"
    #include  "currency.h"
    #include  "delay.h"
    
    void Buzzer_Alert()      //PWM 500hz
    {
        long int i=2000;
        while(i--)
        {
        buzzer=~buzzer;
        delay_ms(1);
        }
    }

    代码难度不大,具体注释想必有点单片机的基础都可以看懂^^

    DHT11的硬件

    注:这个上拉电阻主要是普通的单片机的上拉能力不强,当数据进行长距离传输时容易有较大的寄生电容造成RC放电,所以要加上 上拉电阻。短距离不用加也可以。

     

     

    DHT11的操作比较简单,就是时序操作,没有什么要注意的。

    总结:

    按键最好要消抖,一般有两种做法,软件消抖和硬件消抖两种方法,在单片机资源足够充裕并且系统对实时性要求不高时,建议使用软件消抖,延时20ms后进行判断。不然利用硬件消抖,一般在按键端接入4.7K电阻加0.1uF的电容,在有时间后加上GPS定位功能。

  • 相关阅读:
    KnowYoueSelf
    计算机组成原理--海明码的编码和校验方法(易懂)
    html5新特性
    web前端性能优化
    web标准
    《王者归来》笔记-安全&性能01
    vue核心最基本功能
    BOM&DOM
    JavaScript-语法
    前端04
  • 原文地址:https://www.cnblogs.com/Kroner/p/9073651.html
Copyright © 2020-2023  润新知