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

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

功能需求: 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  "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的电容。

 

版权声明:本文为Kroner原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Kroner/p/9073651.html