基于51单片机的红外测距阈值报警仪表设计详解

目录

      简介

       一.系统的功能分析及体系结构设计  

       二.STC89C52单片机最小系统

       三.5V电源电路设计

       四.LCD1602液晶显示模块电路设计

       五.按键电路设计

       六.PCF8591 A/D采样电路设计

       七.GP2Y0A21YK0F红外测距传感器模块电路设计

       八.系统源程序(包含原理图,实物图焊接,源代码)

      简介

      由于红外测距是一种非接触检测技术,不受光线、被测对象颜色等的影响,较其它仪器更卫生,更耐潮湿、粉尘、高温、腐蚀气体等恶劣环境,具有少维护、不污染、高可靠、长寿命等特点。因此可广泛应用于纸业、矿业、电厂、化工业、水处理厂、污水处理厂、农业用水、环保检测、食品(酒业、饮料业、添加剂、食用油、奶制品)、防汛、水文、明渠、空间定位、公路限高等行业中。可在不同环境中进行距离准确度在线标定,可直接用于水、酒、糖、饮料等液位控制,可进行差值设定,直接显示各种液位罐的液位、料位高度。

因此,红外在空气中测距在特殊环境下有较广泛的应用。利用红外检测往往比较迅速、方便、计算简单、易于实现实时控制,并且在测量精度方面能达到工业实用的指标要求,因此为了使移动机器人能够自动躲避障碍物行走,就必须装备测距系统,以使其及时获取距障碍物的位置信息(距离和方向)。因此红外测距在移动机器人的研究上得到了广泛的应用。同时由于红外测距系统具有以上的这些优点,因此在汽车倒车雷达的研制方面也得到了广泛的应用。

 一.系统的功能分析及体系结构设计

 系统功能分析

本设计由STC89C52单片机+红外测距模块(发射与接收一体)+1602液晶显示电路+A/D采样电路-PCF8591电路+按键电路+蜂鸣器报警电路+电源电路组成。

1、通过红外测距模块检测距离(测距范围10-80cm),通过PCF8591将红外模块的模拟数值转化为数字数值,然后传给单片机处理。

2、通过1602液晶第一行实时显示距离,第二行显示设置的阈值。

3、阈值可以通过2个按键调节。

4、超过阈值后,蜂鸣器鸣叫报警。

3.1.2系统总体结构

   本系统具体框图如下图所示:

二.STC89C52单片机最小系统说明:

STC89C52单片机最小系统电路由复位电路、时钟电路和电源电路。拥有这三部分电路后,单片机即可正常工作。单片机最小系统原理图如下图所示。

 

三.5V电源电路设计

本系统选择5V直流电源作为系统总电源,为整个系统供电,电路简单、稳定。DC为电源的DC插座,可以直接接USB电源线,一端插在DC插座上,另外一端可以插在5V电源上,如电脑USB、充电宝、手机充电器等等。LED为红色LED灯,作为系统是否有点的指示灯,电阻为1K电阻,起到限流作用,保护LED灯,以防电流过大烧坏LED灯。SW为自锁开关,开关按下后,红灯亮,此时系统电源5V直流输出。开关再次按下后,红灯灭,此时系统电源无5V电源输出。

四.LCD1602液晶显示模块电路设计

LCD显示器分为字段显示和字符显示两种。其中字段显示与LED显示相似,只要送对应的信号到相应的管脚就能显示。字符显示是根据需要显示基本字符。本设计采用的是字符型显示。系统中采用LCD1602作为显示器件输出信息。与传统的LED数码管显示器件相比,液晶显示模块具有体积小、功耗低、显示内容丰富等优点,而且不需要外加驱动电路,现在液晶显示模块已经是单片机应用设计中最常用的显示器件了。LCD1602可以显示2行16个汉字。

 LCD1602液晶模块内部的控制器共有11条控制指令,说明下表所示:

序号

指令

RS

R/W

D7

D6

D5

D4

D3

D2

D1

D0

1

清显示

0

0

0

0

0

0

0

0

0

1

2

光标返回

0

0

0

0

0

0

0

0

1

*

3

置输入模式

0

0

0

0

0

0

0

1

I/D

S

4

显示开/关控制

0

0

0

0

0

0

1

D

C

B

5

光标或字符移位

0

0

0

0

0

1

S/C

R/L

*

*

6

置功能

0

0

0

0

1

DL

N

F

*

*

7

置字符发生存贮器地址

0

0

0

1

字符发生存贮器地址

8

置数据存贮器地址

0

0

1

显示数据存贮器地址

9

读忙标志或地址

0

1

BF

计数器地址

10

写数到CGRA或DDRAM)

1

0

要写的数据内容

11

从CGRAM或DDRAM读数

1

1

读出的数据内容

系统中采用LCD1602作为显示器件输出信息。在本电路中电位器可以调节液晶显示的对比度即清晰度。其具体电路原理图如下图所示。

 五.按键电路设计

轻触按键是按键产品下属的一款分类产品,它其实相当于是一种电子开关,只要轻轻的按下按键就可以是开关接通,松开时是开关就断开连接,实现原理主要是通过轻触按键内部的金属弹片受力弹动来实现接通和断开的。在本系统中,按键作为系统的输入,起到了人机交互的枢纽作用。按键的单片机控制引脚默认为高电平,当按键按下后,单片机的相关引脚则变成低电平。进而实现对系统的手动输入。其电路原理图如下图所示。

 按键电路原理图

六.PCF8591 A/D采样电路设计

本系统选择PCF8591作为A/D采样芯片。PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I²C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。

  • 芯片特性
  • (1)单独供电

    (2)PCF8591的操作电压范围2.5V-6V

    (3)低待机电流

    (4)通过I2C总线串行输入/输出

    (5)PCF8591通过3个硬件地址引脚寻址

    (6)PCF8591的采样率由I2C总线速率决定

    (7)个模拟输入可编程为单端型或差分输入

    (8)自动增量频道选择

    (9)PCF8591的模拟电压范围从VSS到VDD

    (10)PCF8591内置跟踪保持电路

    (11)8-bit逐次逼近A/D转换器

    其具体原理图如下图所示。两个电阻为上拉电阻,让数字信号的读取更稳定。

     

    七.GP2Y0A21YK0F红外测距传感器模块电路设计

    本系统选择GP2Y0A21YK0F红外测距传感器对距离进行检测。GP2Y0A21YK0F是测距传感器单元,是基于PSD的微距传感器,其有效测距距离在10-80cm内,有效测量角度大于40度,输出的信号为模拟电压,在0-8cm内于Juin成正比非线性关系,在10-80cm范围内成反比非线性关系,平均工号约为30mA,反应时间约为5ms,并对背景光和温度的适应性较强,由于输出信号为模拟电压,且价格低廉,因此在工程测量等方面有巨大的应用前景。由于采用三角测量方法,这个装置输出对应于电压检测输出,所以这种传感器还可用来作为接近传感器

  • 模块参数
    1. 测量距离10-80cm。
    2. 测距分辨率为1mm
    3. 电源电压为4.5V-5.5V
    4. 模拟量输出型
    5. 功耗:30mA
    6. 工作温度:-10-+60℃
    7. 存储温度:-40-+70℃
  • 使用范围
    1. 触摸式开关(卫浴设备、照明等)
    2. 节能传感器(ATM取款机、复印机、自动售货机等)
    3. 机器人、吸尘器
    4. 游乐设备(机器人、游戏机)
    5. 智能家居等等。
  • 模块接口说明
    1. 红线 接4.5V-5.5V
    2. 黑线 接GND
    3. 黄线 信号线,接单片机引脚

    模块接口原理图如下图所示。

    传感器接口电路原理图

    模块实物图如下图所示。

     

    传感器传感器实物图

      八.系统源程序(包含原理图,实物图焊接,源代码)

     程序原理图

    实物图实现

    以下为源代码:

    1602.h

    
    ------------------------------------------------*/
    #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
    #include<intrins.h>
    
    #ifndef __1602_H__
    #define __1602_H__
    
    void DispStr(unsigned char x,unsigned char y,unsigned char *ptr);
    void DispNChar(unsigned char x,unsigned char y, unsigned char n,unsigned char *ptr);
    void LocateXY(unsigned char x,unsigned char y);
    void Disp1Char(unsigned char x,unsigned char y,unsigned char data1);
    void LcdReset(void);
    void LcdWriteCommand(unsigned char cmd,unsigned char chk);
    void LcdWriteData( unsigned char data1 );
    void WaitForEnable(void);
    void LcdClear(void);
    
    #define LCD_Init          LcdReset
    #define LCD_Write_Char    Disp1Char
    #define LCD_Write_String  DispStr
    #define LCD_Clear         LcdClear
       
    #endif
    

     1602.c

    #include "1602.h"
    #include "delay.h"
    #include <intrins.h>
    
    #define uchar unsigned char
    #define uint unsigned int
    
    #define _NOP() _nop_()
    sbit RS = P2^4;   //定义端口 
    sbit RW = P2^5;
    sbit EN = P2^6;
    
    #define DataPort    P0				
    #define DataPIN     P0
    
    #define CLR_RS (RS=0) 
    #define SET_RS (RS=1)	
    #define CLR_RW (RW=0)	
    #define SET_RW (RW=1)
    #define CLR_EN (EN=0)
    #define SET_EN (EN=1)	
    
    /*
    	LcdReset();               //LCD1602初始化
    	DelayMs(10);	
    	sprintf(temp,"1111111111111111");//更新显示
    	DispStr(0,0,(unsigned char *)temp);//打印显示
    	sprintf(temp,"1111111111111111");//更新显示
    	DispStr(0,1,(unsigned char *)temp);//打印显示
    */
    /***********************************************
    函数名称:DispStr
    功    能:让液晶从某个位置起连续显示一个字符串
    参    数:x--位置的列坐标
              y--位置的行坐标
              ptr--指向字符串存放位置的指针
    返回值  :无
    ***********************************************/
    void DispStr(uchar x,uchar y,uchar *ptr) 
    {
        uchar *temp;
        uchar i,n = 0;
        
        temp = ptr;
        while(*ptr++ != '\0')   n++;    //计算字符串有效字符的个数
        
        for (i=0;i<n;i++)
        {
            Disp1Char(x++,y,temp[i]);
            if (x == 0x10)
            {
                break;
            }
        }
    }
    
    /*******************************************
    函数名称:DispNchar
    功    能:让液晶从某个位置起连续显示N个字符
    参    数:x--位置的列坐标
              y--位置的行坐标
              n--字符个数
              ptr--指向字符存放位置的指针
    返回值  :无
    *******************************************
    void DispNChar(uchar x,uchar y, uchar n,uchar *ptr) 
    {
        uchar i;
        
        for (i=0;i<n;i++)
        {
            Disp1Char(x++,y,ptr[i]);
            if (x == 0x10)
            {
               x = 0; 
                y ^= 1;
            }
        }
    }
    */
    /*******************************************
    函数名称:LocateXY
    功    能:向液晶输入显示字符位置的坐标信息
    参    数:x--位置的列坐标
              y--位置的行坐标
    返回值  :无
    ********************************************/
    void LocateXY(uchar x,uchar y) 
    {
        uchar temp;
    
        temp = x&0x0f;
        y &= 0x01;
        if(y)   temp |= 0x40;  //如果在第2行
        temp |= 0x80;
    
        LcdWriteCommand(temp,1);
    }
    
    /*******************************************
    函数名称:Disp1Char
    功    能:在某个位置显示一个字符
    参    数:x--位置的列坐标
              y--位置的行坐标
              data--显示的字符数据
    返回值  :无
    ********************************************/
    void Disp1Char(uchar x,uchar y,uchar data1) 
    {
        LocateXY( x, y );			
        LcdWriteData( data1 );		
    }
    
    /*******************************************
    函数名称:LcdReset
    功    能:对1602液晶模块进行复位操作
    参    数:无
    返回值  :无
    ********************************************/
    void LcdReset(void) 
    {
    //    DataDir  = 0xFF;                 //数据端口设为输出状态 
        LcdWriteCommand(0x38, 0);	    //规定的复位操作
        DelayMs(5);
        LcdWriteCommand(0x38, 0);		
        DelayMs(5);
        LcdWriteCommand(0x38, 0);
        DelayMs(5);
    
        LcdWriteCommand(0x38, 1);		//显示模式设置
        LcdWriteCommand(0x08, 1);		//显示关闭
        LcdWriteCommand(0x01, 1);	    //显示清屏
        LcdWriteCommand(0x06, 1);		//写字符时整体不移动
        LcdWriteCommand(0x0c, 1);		//显示开,不开游标,不闪烁
    }
    
    /*------------------------------------------------
                    清屏函数
    ------------------------------------------------*/
    void LcdClear(void) 
    { 
    	LcdWriteCommand(0x01,1); 
    	DelayMs(5);
    }
    
    /*******************************************
    函数名称:LcdWriteCommand
    功    能:向液晶模块写入命令
    参    数:cmd--命令,
              chk--是否判忙的标志,1:判忙,0:不判
    返回值  :无
    ********************************************/
    void LcdWriteCommand(uchar cmd,uchar chk) 
    {
    
        if (chk) WaitForEnable();   // 检测忙信号?
        
        CLR_RS;	
        CLR_RW; 
        _NOP();
    
        DataPort = cmd;             //将命令字写入数据端口 
        _NOP();					
        
        SET_EN;                     //产生使能脉冲信号
        _NOP();
        _NOP();
        CLR_EN;			
    }
    
    /*******************************************
    函数名称:LcdWriteData
    功    能:向液晶显示的当前地址写入显示数据
    参    数:data--显示字符数据
    返回值  :无
    ********************************************/
    void LcdWriteData( uchar data1 ) 
    {
        WaitForEnable();        //等待液晶不忙
        SET_RS;
        CLR_RW; 
    
        SET_EN;
    	
    	_NOP();
        DataPort = data1;        //将显示数据写入数据端口
        _NOP();
                    //产生使能脉冲信号
        _NOP(); 
        _NOP(); 
        CLR_EN;		
    }
    
    /*******************************************
    函数名称:WaitForEnable
    功    能:等待1602液晶完成内部操作
    参    数:无
    返回值  :无
    ********************************************/
    void WaitForEnable(void) 
    {
      	unsigned int later=0;
    	DataPort=0xff;
        CLR_RS;
        SET_RW;
        _NOP();
        SET_EN; 
        _NOP();
        _NOP();
    //    while((DataPIN&Busy)!=0);    
        while(((DataPIN&0x80)!=0)&&(later<1000))  //检测忙标志
        {
          DelayUs2x(2);
          later++;        
        }
        CLR_EN;
    //    DataDir|=0xFF;  //将P4口切换为输出状态
    }		
    
    

    i2c.c

    #include "i2c.h"
    
    #define  _Nop()  _nop_()  //定义空指令
                             
    bit ack;	              //应答标志位
    
    sbit SDA=P2^0;
    sbit SCL=P2^1;
    
    #define AddWr 0x90   //写数据地址 
    #define AddRd 0x91   //读数据地址
    
    /*------------------------------------------------
                        启动总线
    ------------------------------------------------*/
    void Start_I2c()
    {
      SDA=1;   //发送起始条件的数据信号
      _Nop();
      SCL=1;
      _Nop();    //起始条件建立时间大于4.7us,延时
      _Nop();
      _Nop();
      _Nop();
      _Nop();    
      SDA=0;     //发送起始信号
      _Nop();    //起始条件锁定时间大于4μ
      _Nop();
      _Nop();
      _Nop();
      _Nop();       
      SCL=0;    //钳住I2C总线,准备发送或接收数据
      _Nop();
      _Nop();
    }
    /*------------------------------------------------
                        结束总线
    ------------------------------------------------*/
    void Stop_I2c()
    {
      SDA=0;    //发送结束条件的数据信号
      _Nop();   //发送结束条件的时钟信号
      SCL=1;    //结束条件建立时间大于4μ
      _Nop();
      _Nop();
      _Nop();
      _Nop();
      _Nop();
      SDA=1;    //发送I2C总线结束信号
      _Nop();
      _Nop();
      _Nop();
      _Nop();
    }
    
    
    
    
    /*----------------------------------------------------------------
                     字节数据传送函数               
    函数原型: void  SendByte(unsigned char c);
    功能:  将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
         此状态位进行操作.(不应答或非应答都使ack=0 假)     
         发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
    ------------------------------------------------------------------*/
    void  SendByte(unsigned char c)
    {
     unsigned char BitCnt;
     
     for(BitCnt=0;BitCnt<8;BitCnt++)  //要传送的数据长度为8位
        {
         if((c<<BitCnt)&0x80)SDA=1;   //判断发送位
           else  SDA=0;                
         _Nop();
         SCL=1;               //置时钟线为高,通知被控器开始接收数据位
          _Nop(); 
          _Nop();             //保证时钟高电平周期大于4μ
          _Nop();
          _Nop();
          _Nop();         
         SCL=0; 
        }
        
        _Nop();
        _Nop();
        SDA=1;               //8位发送完后释放数据线,准备接收应答位
        _Nop();
        _Nop();   
        SCL=1;
        _Nop();
        _Nop();
        _Nop();
        if(SDA==1)ack=0;     
           else ack=1;        //判断是否接收到应答信号
        SCL=0;
        _Nop();
        _Nop();
    }
    
    
    
    
    
    
    
    /*----------------------------------------------------------------
                     字节数据传送函数               
    函数原型: unsigned char  RcvByte();
    功能:  用来接收从器件传来的数据,并判断总线错误(不发应答信号),
         发完后请用应答函数。  
    ------------------------------------------------------------------*/	
    unsigned char  RcvByte()
    {
      unsigned char retc;
      unsigned char BitCnt;
      
      retc=0; 
      SDA=1;             //置数据线为输入方式
      for(BitCnt=0;BitCnt<8;BitCnt++)
          {
            _Nop();           
            SCL=0;       //置时钟线为低,准备接收数据位
            _Nop();
            _Nop();      //时钟低电平周期大于4.7us
            _Nop();
            _Nop();
            _Nop();
            SCL=1;       //置时钟线为高使数据线上数据有效
            _Nop();
            _Nop();
            retc=retc<<1;
            if(SDA==1)retc=retc+1; //读数据位,接收的数据位放入retc中
            _Nop();
            _Nop(); 
          }
      SCL=0;    
      _Nop();
      _Nop();
      return(retc);
    }
    
    
    
    /*----------------------------------------------------------------
                         应答子函数
    原型:  void Ack_I2c(void);
     
    ----------------------------------------------------------------*/
    /*void Ack_I2c(void)
    {
      
      SDA=0;     
      _Nop();
      _Nop();
      _Nop();      
      SCL=1;
      _Nop();
      _Nop();              //时钟低电平周期大于4μ
      _Nop();
      _Nop();
      _Nop();  
      SCL=0;               //清时钟线,钳住I2C总线以便继续接收
      _Nop();
      _Nop();    
    }*/
    /*----------------------------------------------------------------
                         非应答子函数
    原型:  void NoAck_I2c(void);
     
    ----------------------------------------------------------------*/
    void NoAck_I2c(void)
    {
      
      SDA=1;
      _Nop();
      _Nop();
      _Nop();      
      SCL=1;
      _Nop();
      _Nop();              //时钟低电平周期大于4μ
      _Nop();
      _Nop();
      _Nop();  
      SCL=0;                //清时钟线,钳住I2C总线以便继续接收
      _Nop();
      _Nop();    
    }
    
    /*------------------------------------------------
                 读AD转值程序
    输入参数 Chl 表示需要转换的通道,范围从0-3
    返回值范围0-255
    ------------------------------------------------*/
    unsigned char ReadADC(unsigned char Chl)
     {
       unsigned char Val;
       Start_I2c();               //启动总线
       SendByte(AddWr);             //发送器件地址
         if(ack==0)return(0);
       SendByte(0x40|Chl);            //发送器件子地址
         if(ack==0)return(0);
       Start_I2c();
       SendByte(AddWr+1);
          if(ack==0)return(0);
       Val=RcvByte();
       NoAck_I2c();                 //发送非应位
       Stop_I2c();                  //结束总线
      return(Val);
     }
    
    
    
    
    /*----------------------------------------------------------------
                        向无子地址器件发送字节数据函数               
    函数原型: bit  ISendByte(unsigned char sla,ucahr c);  
    功能:     从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla.
               如果返回1表示操作成功,否则操作有误。
    注意:    使用前必须已结束总线。
    ----------------------------------------------------------------*/
    /*bit ISendByte(unsigned char sla,unsigned char c)
    {
       Start_I2c();               //启动总线
       SendByte(sla);             //发送器件地址
         if(ack==0)return(0);
       SendByte(c);               //发送数据
         if(ack==0)return(0);
      Stop_I2c();                 //结束总线
      return(1);
    }
    */
    
    /*----------------------------------------------------------------
                        向有子地址器件发送多字节数据函数               
    函数原型: bit  ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);  
    功能:     从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
              地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
               如果返回1表示操作成功,否则操作有误。
    注意:    使用前必须已结束总线。
    ----------------------------------------------------------------*/
    /*bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
    {
       unsigned char i;
     for(i=0;i<no;i++)
        { 
       Start_I2c();               //启动总线
       SendByte(sla);             //发送器件地址
         if(ack==0)return(0);
       SendByte(suba);            //发送器件子地址
         if(ack==0)return(0); 
     
         SendByte(*s);            //发送数据
           if(ack==0)return(0);
         Stop_I2c();                  //结束总线
    	 DelayMs(1);               //必须延时等待芯片内部自动处理数据完毕
    	 s++;
    	 suba++;
        } 
      return(1);
    }
    */
    /*----------------------------------------------------------------
                        向无子地址器件读字节数据函数               
    函数原型: bit  IRcvByte(unsigned char sla,ucahr *c);  
    功能:     从启动总线到发送地址,读数据,结束总线的全过程,从器件地
              址sla,返回值在c.
               如果返回1表示操作成功,否则操作有误。
    注意:    使用前必须已结束总线。
    ----------------------------------------------------------------*/
    /*bit IRcvByte(unsigned char sla,unsigned char *c)
    {
       Start_I2c();                //启动总线
       SendByte(sla+1);            //发送器件地址
         if(ack==0)return(0);
       *c=RcvByte();               //读取数据
         NoAck_I2c();              //发送非就答位
         Stop_I2c();               //结束总线
      return(1);
    }
    
    */
    /*----------------------------------------------------------------
                        向有子地址器件读取多字节数据函数               
    函数原型: bit  ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);  
    功能:     从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
              地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
               如果返回1表示操作成功,否则操作有误。
    注意:    使用前必须已结束总线。
    ----------------------------------------------------------------*/
    /*bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
    {
       unsigned char i;
    
       Start_I2c();               //启动总线
       SendByte(sla);             //发送器件地址
         if(ack==0)return(0);
       SendByte(suba);            //发送器件子地址
         if(ack==0)return(0);
    
       Start_I2c();
       SendByte(sla+1);
          if(ack==0)return(0);
    
      for(i=0;i<no-1;i++)
        { 
         *s=RcvByte();              //发送数据
          Ack_I2c();                //发送就答位 
         s++;
        } 
       *s=RcvByte();
        NoAck_I2c();                 //发送非应位
        Stop_I2c();                    //结束总线
      return(1);
    }
    */
    

    i2c.h

     #ifndef __I2C_H__
    #define __I2C_H__  
                  
    #include <reg52.h>          //头文件的包含
    #include <intrins.h>
    
    #define  _Nop()  _nop_()        //定义空指令
    
    void Start_I2c();
    void Stop_I2c();
    void  SendByte(unsigned char c);
    unsigned char  RcvByte();
    
    void Ack_I2c(void);
    void NoAck_I2c(void);
    bit ISendByte(unsigned char sla,unsigned char c);
    bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);
    bit IRcvByte(unsigned char sla,unsigned char *c);
    bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);
    
    unsigned char ReadADC(unsigned char Chl);
    
    #endif

    main.c

    #include<reg52.h> //包含头文件
    #include<stdio.h>
    #include<math.h>
    #include "1602.h"
    #include "delay.h"
    #include "i2c.h"
    
    sbit Buzzer =P1^0;	//引脚定义
    sbit Key1 =P1^1;
    sbit Key2 =P1^2;
    
    char dis0[16];			   //打印数组初始化
    char dis1[16];
    unsigned long time_20ms=0;		   //定时器计数
    
    float Volt=0.0;		  	//检测电压
    unsigned int BatCap=80;			  //容量初始化
    float Shijul= 0,midval ;	//实际距离
    unsigned int Setjul= 25 ;	//测量距离
    unsigned int DisFlag = 0,rekey=0 ;	//更新显示
    
    void Init_Timer0(void);		   //函数声明
    void UART_Init(void);
    void uartSendStr(unsigned char *s,unsigned char length);
    void uartSendByte(unsigned char dat);
    
    void main (void)
    {     
    	unsigned char midvolt;
    
    	Init_Timer0();        //定时器0初始化
    	UART_Init();
    	             
    	LCD_Init();           //初始化液晶
    	DelayMs(20);          //延时有助于稳定
    	LCD_Clear(); 
    
    	sprintf(dis0,"Now:Err cm 1-80",Shijul);//打印电池容量值
    	LCD_Write_String(0,0,dis0);//显示第一行
    	sprintf(dis1,"Set:%2d cm     ",Setjul);//打印电压值			
    	LCD_Write_String(0,1,dis1);//显示第二
    
    	uartSendStr("reday ok!!",10);
    	while (1)         //主循环
    	{
    		if(DisFlag ==1)
    		{
    			DisFlag =0 ;
    			midvolt=ReadADC(0);				//ad转换采集
    	
    			Volt=(float)midvolt*5.10/255;		//计算出电压 *5.10为参考电压 手机适配器
    													
    			if((Volt<0.42)||(Volt>3.13))   //超出测量范围
    			{
    				sprintf(dis0,"Now:%4.1f",Volt);//打印显示内容
    				Buzzer = 1;
    				uartSendStr("ddddddd") ;
    			}
    			else	//正常范围
    			{
    				Shijul=26.527*pow(Volt,-1.225)+0.1;	  //模拟曲线获取 根据手册来 光电型号GP2Y0A21YK0F  测量范围10-80cm	 0.1位匹配
    				midval =  Shijul;
    				sprintf(dis0,"Now:%4.1fcm 1-80",Shijul);//打印实际值
    				if(midval>Setjul)  //实际值和设置值对比
    				{Buzzer = 0;}
    				else
    				{Buzzer = 1;}		
    			}
    			LCD_Write_String(0,0,dis0);//显示第一行
    	
    //			sprintf(dis1,"Set:%2d cm %4.2f",Setjul,Volt);//打印电压值
    			sprintf(dis1,"Set:%2d cm     ",Setjul);//打印电压值			
    			LCD_Write_String(0,1,dis1);//显示第二行
    		}
    		if((Key1==0)||(Key2==0)) //检测按键是否按下
    		{
    		  	if(rekey==0)		//防止重复
    			{
    				DelayMs(10);//延时去抖动
    			 	if(Key1==0)	   //按键1
    				{
    					rekey =1;
    					if(Setjul>10)  //设置值--
    					{
    						Setjul--;
    					}
    				}
    				else if(Key2==0)  //按键2
    				{
    					rekey =1;
    					if(Setjul<80)	   //设置值++
    					{
    						Setjul++;
    					}
    				}
    			
    			}
    		}
    		else
    		{rekey =0;}	//防止重复
    
    	}
    }
    
    
    void Init_Timer0(void)
    {
    	TMOD |= 0x01;	  //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响		     
    	TH0=(65536-20000)/256;		  //重新赋值 20ms
    	TL0=(65536-20000)%256;
    	EA=1;            //总中断打开
    	ET0=1;           //定时器中断打开
    	TR0=1;           //定时器开关打开
    }
    
    void Timer0_isr(void) interrupt 1 
    {
    	TH0=(65536-20000)/256;		  //重新赋值 20ms
    	TL0=(65536-20000)%256;
    	
    	time_20ms++;
    	if(time_20ms%30==0)	  //定时显示
    	{
    		DisFlag = 1;
    	}
    }
    
    
    void UART_Init(void)
    {
        SCON  = 0x50;		        // SCON: 模式 1, 8-bit UART, 使能接收  
        TMOD |= 0x20;               // TMOD: timer 1, mode 2, 8-bit 重装
        TH1   = 0xFD;               // TH1:  重装值 9600 波特率 晶振 11.0592MHz
    	TL1 = TH1;  
        TR1   = 1;                  // TR1:  timer 1 打开                         
        EA    = 1;                  //打开总中断
        ES    = 1;                  //打开串口中断
    }
    
    void uartSendByte(unsigned char dat)
    {
    	unsigned char time_out;
    	time_out=0x00;
    	SBUF = dat;			  //将数据放入SBUF中
    	while((!TI)&&(time_out<100))  //检测是否发送出去
    	{time_out++;DelayUs2x(10);}	//未发送出去 进行短暂延时
    	TI = 0;						//清除ti标志
    }
    
    void uartSendStr(unsigned char *s,unsigned char length)
    {
    	unsigned char NUM;
    	NUM=0x00;
    	while(NUM<length)	//发送长度对比
    	{
    		uartSendByte(*s);  //放松单字节数据
    		s++;		  //指针++
    		NUM++;		  //下一个++
      	 }
    }
    
    void UART_SER (void) interrupt 4 	//串行中断服务程序
    {
    	if(RI)                        //判断是接收中断产生
    	{
    		RI=0;                      //标志位清零
    	}
    	if(TI)  //如果是发送标志位,清零
    	TI=0;
    } 
    
    
    
    
    
    
    

    最后附上自己总结的一些话~

    在这个红外测距仪的实现过程中,我是以各个模块独立化的形式展现出来的,原因在于我想让大家从入门开始就建立起建工程的意识,从硬件原理图再到代码的编译实现,逐步拆开去理解每个模块的代码实现过程。相信大家看完能有一个清晰的认识和对单片机的理解。

    ~如有不足,请补充,谢谢!

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于51单片机的红外测距阈值报警仪表设计详解

    发表回复