89C51电子欧式铃

89C51电子欧式铃

一、目的及要求

基于89c51、LCD1602A以及蜂鸣器,实现带有温度显示和计数功能的电子欧式拨铃。

二、环境

  • KELI uVision4、
  • 2.STC89C51的单片机
  • 3.LCD1602A显示屏
  • 4.蜂鸣器、
  • 5.温度传感器

三、内容

编程实现按键对显示器LCD1602A的开机控制并在屏幕打印欢迎语句,同时初始化温度传感器并持续显示当前温度(每秒刷新一次)。每1.0s对K1、K2按键进行一次扫描,判断k1按键是否被按下,若K1按下则蜂鸣器发声提示有客人需要被服务且待服务数加一;判断k2键是否被按下,若有,则显示当前等待的访客数;否则一直循坏。

四、接口引脚图

  • 89C51芯片引脚接口图

  • LCD1602A引脚图

  • LCD1602A引脚功能表

  • K1、K2按键引脚图

  • 蜂鸣器

  • 温度传感器

五、原理

1.LCD1602A具体功能

DDRAM 就是显示数据 RAM,用来寄存待显示的字符代码。共 80 个字节,其地 址和屏幕的对应关系如下表:

也就是说想要在 LCD1602 屏幕的第一行第一列显示一个”A”字,就要向 DDRAM 的 00H 地址写入“A”字的代码就行了。但具体的写入是要按 LCD 模块的指令格 式来进行的,后面我会说到的。那么一行可有 40 个地址呀?是的,在 1602 中我 们就用前 16 个就行了。第二行也一样用前 16 个地址。对应如下:

刚才我说了想要在 LCD1602 屏幕的第一行第一列显示一个”A”字,就要向 DDRAM 的 00H 地址写入“A”字的代码 41H 就行了,可 41H 这一个字节的代码如 何才能让 LCD 模块在屏幕的阵点上显示“A”字呢?同样,在 LCD 模块上也固化 了字模存储器,这就是 CGROM 和 CGRAM。HD44780 内置了 192 个常用字符的字模, 存于字符产生器 CGROM(Character Generator ROM)中,另外还有 8 个允许用户 自定义的字符产生 RAM,称为 CGRAM(Character Generator RAM)。下图说明了 CGROM 和 CGRAM 与字符的对应关系。

从上图可以看出,“A”字的对应上面高位代码为 0100,对应左边低位代码 为 0001,合起来就是 01000001,也就是 41H。可见它的代码与我们 PC 中的字符 代码是基本一致的。因此我们在向DDRAM写C51字符代码程序时甚至可以直接用 P1=’A’这样的方法。PC 在编译时就把“A”先转为 41H 代码了。

2.温度传感器具体功能

DS1820 通过门开通期间内低温度系数振荡器经历的时钟周期个数计数来测量温度 而门开通 期由高温度系数振荡器决定 计数器予置对应于-55 的基数 如果在门开通期结束前计数器达到 零 那么温度寄存器 它也被予置到-55 的数值 将增量 指示温度高于-55
同时 计数器用钭率累加器电路所决定的值进行予置 为了对遵循抛物线规律的振荡器温度特 性进行补偿 这种电路是必需的 时钟再次使计数器计值至它达到零 如果门开通时间仍未结束 那么此过程再次重复
钭率累加器用于补偿振荡器温度特性的非线性 以产生高分辩率的温度测量 通过改变温度每 升高一度 计数器必须经历的计数个数来实行补偿 因此 为了获得所需的分辩率 计数器的数值
以及在给定温度处每一摄氏度的计数个数 此计算在DS1820内部完成以提供0.5 的分辩率

钭率累加器的值 二者都必须知道温度读数以 16 位读数形式提供 表 1 说明输出数据对测量温度的关系 数据在单线接口上串行发送 DS1820 可以0.5C的符号扩展的二进制补码 的增量值 在 0.5 至+125 的范围内测量温度 对于应用华氏温度的场合 必须使用查 找表或变换系数

注意 在DS1820中温度是以1/2 LSB 最低有效位 形式表示时 产生以下9位格式

以下的过程可以获得较高的分辩率 首先 读温度 并从读得的值截去0.5 位(最低有效位) 这个值便是 TEMP_READ 然后可以读留在计数器内的值 此值是门开通期停止之后计数剩余

COUNT_REMAIN 所需的最后一个数值是在该温度处每一摄氏度的计数个数 COUNT_PER_C 于 是 用户可以使用下式计算实际温度

DS1820指令集

六、代码

dooring.c

/* 下载程序后按键K1按下就会触发蜂鸣器门铃“叮咚”响  */

#include "reg52.h"
#include "LCD.h"
#include "stdlib.h"
#include "stdio.h"
#include "temp.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

int pressflag = 0;

u8 Disp[11] = "New Guests";
u8 Disp_Hello[14] = "Welcome!!!!!";
u8 Disp_Test[9] = "Guests:";

sbit beep=P1^5;
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;

u8 ding,dong,flag,stop;
u16 n;


u16 j=0;
u32 q=0;
u16 k;

uchar CNCHAR[7] = "Celsius";
void LcdDisplay(int);
void UsartConfiguration();


void delayl(u16 k)
{
    while(k--);
}


void delayus(u16 k)
{
       while(k--);
}


void delayms(u16 k)//
{
     for(q=0;q<k;q++)
     {
            delayus(100);
            }    
}

void delays(u16 k) //i=1?,???1s
{     
     for(q=0;q<k;q++)
     {
                delayms(1000);
     }
}

void time0init()      //定时器0初始化
{
    TMOD=0X01;     //定时器0 方式1
    TH0=0Xff;
    TL0=0X06; //定时250us
//    TR0=1;
    EA=1;
    ET0=1;
}
void biaohaoinit()       //各个标号初始化
{
    ding=0;        //叮声音  计数标志
    dong=0;        //咚声音  计数标志
    n=0;        //定时0.5s标志
    flag=0;
    stop=0;       //结束标志
}
void main()
{
    u8 i;
    u8 pressk3_flag = 0;
    char temp_now[3];

    biaohaoinit();

    LcdInit();
    LcdWriteCom(0x88);    
    LcdWriteData('C'); 

    while(1)
    {
        time0init();
        delayms(500);
        LcdInit();
        for(i=0;i<11;i++)
        {
            LcdWriteData(Disp_Hello[i]);    
        }
        LcdDisplay(Ds18b20ReadTemp());
        delays(1); //延时1秒
        if(k1==0)       //判断按键是否按下
        {
            delayl(1000);  //消抖
            if(k1==0)
            {
                pressflag = pressflag + 1;
                LcdInit();
                TR0=1;    //打开定时器0
                for(i=0;i<10;i++)
                {
                    LcdWriteData(Disp[i]);    
                }
                delays(8);
                LcdInit();
                while(!stop);
            }
        }
        if(k2==0)
        {
            delayl(1000);  //消抖
            if(k2==0)
            {
                sprintf(temp_now,"%d",pressflag);
                LcdInit();
                for(i=0;i<7;i++)
                {
                    LcdWriteData(Disp_Test[i]);    
                }
                for(i=0;i<2;i++)
                {
                    LcdWriteData(temp_now[i]);    
                }
                delays(5);
                LcdInit();
            }
        }
        if(k3==0)
        {
            pressk3_flag = pressk3_flag + 1;
            UsartConfiguration();
            delayl(1000);  //消抖
            if(k3==0)
            {
                LcdInit();
                //LcdDisplay(Ds18b20ReadTemp());
                delays(1);
            }
        }
    }    
}

void time0() interrupt 1 //定时器0中断1
{
    n++;
    TH0=0Xff;
    TL0=0X06; //250us
    if(n==2000)        //定时0.5s  叮响0.5秒,咚响0.5秒
    {
        n=0;
        if(flag==0)
        {
            flag=~flag;
        }
        else
        {
            flag=0;
            stop=1;
            TR0=0;      //关闭定时器0
        }
    }
    if(flag==0)
    {                       //通过改变定时计数时间可以改变门铃的声音
        ding++;              //叮
        if(ding==1)
        {
            ding=0;
            beep=~beep;
        }
    }
    else
    {
        dong++;
        if(dong==2)          //咚
        {
            dong=0;
            beep=~beep;
        }    
    }
}



/*******************************************************************************
* 函数名         : LcdDisplay()
* 函数功能           : LCD显示读取到的温度
* 输入           : v
* 输出              : 无
*******************************************************************************/

void LcdDisplay(int temp)      //lcd显示
{
      unsigned char i, datas[] = {0, 0, 0, 0, 0, 0, 0}; //定义数组
    float tp;  
    if(temp< 0)                //当温度值为负数
      {
          LcdWriteCom(0x80+0x40);        //写地址 80表示初始地址 +
        SBUF='-';//将接收到的数据放入到发送寄存器
        while(!TI);                     //等待发送数据完成
        TI=0;                         //清除发送完成标志位
        LcdWriteData('-');          //显示负
        //因为读取的温度是实际温度的补码,所以减1,再取反求出原码
        temp=temp-1;
        temp=~temp;
        tp=temp;
        temp=tp*0.0625*100+0.5;    
        //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
        //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
        //算由�0.5,还是在小数点后面。

      }
     else
      {            
          LcdWriteCom(0x80+0x40);        //写地址 80表示初始地址
        LcdWriteData('+');         //显示正
        SBUF='+';//将接收到的数据放入到发送寄存器
        while(!TI);                     //等待发送数据完成
        TI=0;                         //清除发送完成标志位
        tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
        //如果温度是正的那么,那么正数的原码就是补码它本身
        temp=tp*0.0625*100+0.5;    
        //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
        //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
        //算加上0.5,还是在小数点后面。
    }
    datas[0] = temp / 10000;
    datas[1] = temp % 10000 / 1000;
    datas[2] = temp % 1000 / 100;
    datas[3] = temp % 100 / 10;
    datas[4] = temp % 10;
    datas[5] = 223;
    datas[6] = 'C';

    LcdWriteCom(0x82+0x40);          //写地址 80表示初始地址
    LcdWriteData('0'+datas[0]); //百位 
    SBUF = '0'+datas[0];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x83+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[1]); //十位
    SBUF = '0'+datas[1];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x84+0x40);        //写地址 80表示初始地址
    LcdWriteData('0'+datas[2]); //个位 
    SBUF = '0'+datas[2];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x85+0x40);        //写地址 80表示初始地址
    LcdWriteData('.');         //显示 ‘.’
    SBUF = '.';//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x86+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[3]); //显示小数点  
    SBUF = '0'+datas[3];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x87+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[4]); //显示小数点 
    SBUF = '0'+datas[4];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x88+0x40);         //写地址 80表示初始地址
    LcdWriteData(datas[5]); //显示小数点 
    SBUF = '0'+datas[5];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x89+0x40);         //写地址 80表示初始地址
    LcdWriteData(datas[6]); //显示小数点 
    SBUF = '0'+datas[6];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    for(i=0; i<8; i++)
    {
         SBUF = CNCHAR[i];//将接收到的数据放入到发送寄存器
        while (!TI);                     //等待发送数据完成
        TI = 0;
    }
}
/*******************************************************************************
* 函 数 名         :UsartConfiguration()
* 函数功能           :设置串口
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

void UsartConfiguration() 
{
    SCON=0X50;            //设置为工作方式1
    TMOD=0X20;            //设置计数器工作方式2
    PCON=0X80;            //波特率加倍
    TH1=0XF3;                //计数器初始值设置,注意波特率是4800的
    TL1=0XF3;
//    ES=1;                        //打开接收中断
//    EA=1;                        //打开总中断
    TR1=1;                    //打开计数器
}

LCD.c

#include "LCD.h"

/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能           : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
    for (; c>0; c--)
    {
         for (b=199;b>0;b--)
         {
              for(a=1;a>0;a--);
         }      
    }

}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能           : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
#ifndef     LCD1602_4PINS     //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)      //写入命令
{
    LCD1602_E = 0;     //使能
    LCD1602_RS = 0;       //选择发送命令
    LCD1602_RW = 0;       //选择写入

    LCD1602_DATAPINS = com;     //放入命令
    Lcd1602_Delay1ms(1);        //等待数据稳定

    LCD1602_E = 1;              //写入时序
    Lcd1602_Delay1ms(5);      //保持时间
    LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)      //写入命令
{
    LCD1602_E = 0;     //使能清零
    LCD1602_RS = 0;     //选择写入命令
    LCD1602_RW = 0;     //选择写入

    LCD1602_DATAPINS = com;    //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;     //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;

    LCD1602_DATAPINS = com << 4; //发送低四位
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;     //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能           : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/           
#ifndef     LCD1602_4PINS           
void LcdWriteData(uchar dat)            //写入数据
{
    LCD1602_E = 0;    //使能清零
    LCD1602_RS = 1;    //选择输入数据
    LCD1602_RW = 0;    //选择写入

    LCD1602_DATAPINS = dat; //写入数据
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;   //写入时序
    Lcd1602_Delay1ms(5);   //保持时间
    LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)            //写入数据
{
    LCD1602_E = 0;      //使能清零
    LCD1602_RS = 1;      //选择写入数据
    LCD1602_RW = 0;      //选择写入

    LCD1602_DATAPINS = dat;    //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;      //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;

    LCD1602_DATAPINS = dat << 4; //写入低四位
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;      //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能         : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/           
#ifndef        LCD1602_4PINS
void LcdInit()                          //LCD初始化子程序
{
     LcdWriteCom(0x38);  //开显示
    LcdWriteCom(0x0c);  //开显示不显示光标
    LcdWriteCom(0x06);  //写一个指针加1
    LcdWriteCom(0x01);  //清屏
    LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()                          //LCD初始化子程序
{
    LcdWriteCom(0x32);     //将8位总线转为4位总线
    LcdWriteCom(0x28);     //在四位线下的初始化
    LcdWriteCom(0x0c);  //开显示不显示光标
    LcdWriteCom(0x06);  //写一个指针加1
    LcdWriteCom(0x01);  //清屏
    LcdWriteCom(0x80);  //设置数据指针起点
}
#endif

LCD.h

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include<reg52.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/    
void LcdWriteData(uchar dat)    ;
/*LCD1602初始化子程序*/        
void LcdInit();                          

#endif

temp.h

#ifndef __TEMP_H_
#define __TEMP_H_

#include "reg52.h"

sbit DSPORT=P3^7;

void Delay1ms(unsigned int );
unsigned char Ds18b20Init();
void Ds18b20WriteByte(unsigned char com);
unsigned char Ds18b20ReadByte();
void  Ds18b20ChangTemp();
void  Ds18b20ReadTempCom();
int Ds18b20ReadTemp();

#endif

temp.c

#include"temp.h"
#include "LCD.h"
/*******************************************************************************
* 函数名         : Delay1ms
* 函数功能           : 延时函数
* 输入           : 无
* 输出              : 无
*******************************************************************************/

void Delay1ms(unsigned int y)
{
    unsigned int x;
    for(y;y>0;y--)
        for(x=110;x>0;x--);
}
/*******************************************************************************
* 函数名         : Ds18b20Init
* 函数功能           : 初始化
* 输入           : 无
* 输出              : 初始化成功返回1,失败返回0
*******************************************************************************/

unsigned char Ds18b20Init()
{
    unsigned int i;
    DSPORT=0;             //将总线拉低480us~960us
    i=70;    
    while(i--);//延时642us
    DSPORT=1;            //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
    i=0;
    while(DSPORT)    //等待DS18B20拉低总线
    {
        i++;
        if(i>5000)//等待>5MS
            return 0;//初始化失败    
    }
    return 1;//初始化成功
}

/*******************************************************************************
* 函数名         : Ds18b20WriteByte
* 函数功能           : 向18B20写入一个字节
* 输入           : com
* 输出              : 无
*******************************************************************************/

void Ds18b20WriteByte(unsigned char dat)
{
    unsigned int i,j;
    for(j=0;j<8;j++)
    {
        DSPORT=0;            //每写入一位数据之前先把总线拉低1us
        i++;
        DSPORT=dat&0x01; //然后写入一个数据,从最低位开始
        i=6;
        while(i--); //延时68us,持续时间最少60us
        DSPORT=1;    //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
        dat>>=1;
    }
}
/*******************************************************************************
* 函数名         : Ds18b20ReadByte
* 函数功能           : 读取一个字节
* 输入           : com
* 输出              : 无
*******************************************************************************/


unsigned char Ds18b20ReadByte()
{
    unsigned char byte,bi;
    unsigned int i,j;    
    for(j=8;j>0;j--)
    {
        DSPORT=0;//先将总线拉低1us
        i++;
        DSPORT=1;//然后释放总线
        i++;
        i++;//延时6us等待数据稳定
        bi=DSPORT;     //读取数据,从最低位开始读取
        /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
        byte=(byte>>1)|(bi<<7);                          
        i=4;        //读取完之后等待48us再接着读取下一个数
        while(i--);
    }                
    return byte;
}
/*******************************************************************************
* 函数名         : Ds18b20ChangTemp
* 函数功能           : 让18b20开始转换温度
* 输入           : com
* 输出              : 无
*******************************************************************************/

void  Ds18b20ChangTemp()
{
    Ds18b20Init();
    Delay1ms(1);
    Ds18b20WriteByte(0xcc);        //跳过ROM操作命令         
    Ds18b20WriteByte(0x44);        //温度转换命令
//    Delay1ms(100);    //等待转换成功,而如果你是一直刷着的话,就不用这个延时了

}
/*******************************************************************************
* 函数名         : Ds18b20ReadTempCom
* 函数功能           : 发送读取温度命令
* 输入           : com
* 输出              : 无
*******************************************************************************/

void  Ds18b20ReadTempCom()
{    

    Ds18b20Init();
    //LcdWriteCom(0x80);
    LcdWriteCom(0xC0);   
    Delay1ms(1);
    Ds18b20WriteByte(0xcc);     //跳过ROM操作命令
    Ds18b20WriteByte(0xbe);     //发送读取温度命令
}
/*******************************************************************************
* 函数名         : Ds18b20ReadTemp
* 函数功能           : 读取温度
* 输入           : com
* 输出              : 无
*******************************************************************************/

int Ds18b20ReadTemp()
{
    int temp=0;
    unsigned char tmh,tml;
    Ds18b20ChangTemp();                 //先写入转换命令
    Ds18b20ReadTempCom();            //然后等待转换完后发送读取温度命令
    tml=Ds18b20ReadByte();        //读取温度值共16位,先读低字节
    tmh=Ds18b20ReadByte();        //再读高字节
    temp=tmh;
    temp<<=8;
    temp|=tml;
    return temp;
}

七、实物