实验内容:
使用51单片机控制LCD1602液晶显示屏显示 时间/日期/星期/温度 信息,并可通过按键设置值。
仿真效果展示:
proteus仿真图:
硬件测试图:
说明:
硬件测试按键使用左侧的四个独立按键
key1-设置键(选设置位)
key2:加一
key3:减一
key4:切换到日期/时间
主要硬件:
- AT89C51主控芯片
- LM016L/LCD1602 液晶显示模块
- RESPACK-8 排阻
- DS18B20 温度传感器
- BUTTON 按键
注:LM016L和LCD1602 原理是一样的,用法也相同。只是protues中lm0161没有显示调节亮度的那两个端口
对比
此次代码采用模块化的方式,硬件初始化和相关代码单独封装在一个文件中,使用时将其包含就好。
项目结构如图:
列出几个比较重要的函数:
主函数:
#include <reg52.h>
#include "LM016L.h"
#include "Timer.h"
#include "Data.h"
#include "Button.h"
#include "temp.h"
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数 程序入口
* 参数 :无
*******************************************************************************/
void main()
{
LcdInitiate(); //液晶屏初始化
TimerInit(); //定时器初始化
// Ds18b20Init(); //温度传感器不在这里初始化,在每次温度转化时和读取温度时都要初始化
while(1)
{
KeyScan(); //按键检测
button_setting(); //按键 设置处理
button_up_down(); //按键 加一/减一 处理
ButtonSwitchDT(); //按键 切换时间/日期 处理
//想显示哪个就显示哪个,不想要的可以注释掉
TimerShow(); //时间显示 星期显示在时间里
DataShow(); //日期显示
Lcd_Tem_Display(Ds18b20ReadTemp()); //温度显示
}
}
定时器中断函数:
/*******************************************************************************
* 函 数 名 : Timer0()
* 函数功能 : 定时器0中断函数
* 参数 :无
*******************************************************************************/
void Timer0() interrupt 1
{
TH0=0x4C;
TL0=0x00;
i++;
//使得setting_flag 0.2秒转换一次
if(i%10<5)
{
blink_flag = 0;
}
if(i%10>=5)
{
blink_flag = 1;
}
if(i==20)//20个50毫秒即一秒
{
i=0;
if(button_num1==0)
{
num1++;
}
if(num1==60)
{
num1=0;
num2++;
if(num2==60)//定时一小时自动清零
{
num2=0;
num3++;
if(num3==24)
{
num3=0;
day++; //一天过去星期加一
weekday++;
if(weekday==8)
{
weekday=1;
}
//日期处理
DataDeal1(); //一天过去年月日相应变化
}
}
}
}
}
/*******************************************************************************
* 函 数 名 : DataDeal1
* 函数功能 : 日期处理函数,计算日期的当前的日期值
* 参数 :无
*******************************************************************************/
void DataDeal1()
{
if(day>=30) //默认一个月30天
{
month++;
day=1;
if(month>=13)
{
month = 1;
year2++;
if(year2>=100)
{
year2 = 0; //由于年数太大后int型变量无法存储,使用两个变量存储高两位和低两位
year1++;
if(year1>=100)
{
year1=0;
}
}
}
}
}
按键处理函数:
/***********************************************************
*函数名 :KeyScan
*功能 : 按键扫描,返回按下的按键对应的位,并防止连按
*返回值 :返回按键按下的位置
*参数 :无
************************************************************/
char KeyScan()
{
static unsigned char klast = 0; //记录上一次的按键值
unsigned char trg = 0,key = 0; //trg:得到的返回值,返回值中8位只有一位为1,为1的位代表按下的位,其余位为零
key = P2 & 0xF0; //将按下的位转换为0 没按下的位依然为1
key ^= 0xF0; //异或之后没按下的位转换为1,按下的位转换为0
trg = key & (key ^ klast); //这句是最关键的一句,需要自己理解
klast = key;
button_flag = trg;
return trg; //最终返回值是按下哪个按键,返回值对应就是1,没按对应的就是0 eg:按下button3 则返回0x01 0000 0100
}
温度传感器初始化函数:
/*******************************************************************************
* 函数名 : 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;//初始化成功
}
这里需要注意的一点是,温度传感器并不需要一直转换温度,读取温度。而是在需要温度的时候再去初始化一下(转化和读取时),如果只在最开始初始化,转化时没有初始化则无法读取温度值。具体请看项目源码。
完整项目下载地址:
[仿真版源码] (https://github.com/Little-Water-Bottle/LCD1602ShowDataAndTime)
(补充链接:仿真版源码 密码:7jdc)
51普中开发板源码 密码:aeg3
proteus文件下载 密码:guqw
参考资料下载(!):
LCD和DS18B20参考资料 密码:64l7
排版真的是垃圾,算了记录一下就行了。。