基於單片機的自行車碼表


最近畢業設計中接了別人幾個項目,忙完一陣是時候做一些總結。

項目名:基於單片機的自行車碼表設計

功能需求: 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定位功能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM