51单片机应用----时钟
通过从矩阵按键中分离出来的独立按键,蜂鸣器,1602液晶屏,计时器,AT24C02的控制实现时钟。
文章目录
前言
总结近期51单片机的学习
二、代码讲解
1.头文件
代码如下(示例):
#include<reg52.h> //52单片机头文件
#define uchar unsigned char
#define uint unsigned int
#include"24c02.h"
sbit dula=P2^6; //定义锁存器
sbit wela=P2^7;
sbit rs=P3^5; //1602液晶rs端
sbit lcden=P3^4; //lcden端
sbit s1=P3^0; //功能键
sbit s2=P3^1; //增大键
sbit s3=P3^2; //减小键
sbit rd=P3^7; //矩阵第四列
sbit beep=P2^3; //蜂鸣器
uchar count ,s1num;
char miao,shi,fen;
uchar code table[]=" 2023-3-17 FRI"; //日期
这里是对一些接口的定义,数据的定义以及日期。
2.延时函数
代码如下(示例):
void yanshi(uint max) //延时函数
{
int i,j;
for(i=max;i>0;i--)
for(j=110;j>0;j--);
}
对部分操作之间进行延时,以增加程序的稳定性
3.蜂鸣器函数
void di() //蜂鸣器函数
{
beep=0;
yanshi(100);
beep=1;
}
4.液晶屏写命令函数
void write_com(uchar com) //液晶屏写命令函数
{
rs=0;
lcden=0; //选择写命令模式
P0=com; //将要写的命令送到命令总线上
yanshi(5); //延时稳定
lcden=1; //给lcden高脉冲将命令送进液晶控制器
yanshi(5);
lcden=0;
}
此函数主要对液晶屏进行控制。
5.液晶屏数据的写入函数
void write_date(uchar date) //液晶写数据函数
{
rs=1;
lcden=0; //选择模式
P0=date; //将数据送到数据总线
yanshi(5); //延时稳定
lcden=1; //给lcden高脉冲将数据送进液晶控制器
yanshi(5);
lcden=0;
}
此函数主要将数据写入液晶屏。
6.将数据写入液晶屏函数
void write_sfm(uchar add,uchar date) //写时分秒函数
{
uchar shi,ge;
shi=date/10; //分解数据个十位
ge=date%10;
write_com(0x80+0x40+add); //设置显示位置
write_date(0x30+shi); //十位显示
write_date(0x30+ge); //个位显示
}
7.初始化函数
void init() //初始化函数
{
uchar num;
rd=0; //矩阵第四列一端置低,分解独立按键
dula=0; //关闭锁存器,防止液晶屏乱码
wela=0;
lcden=0;
fen=0; //初始数据
miao=0;
shi=0;
count=0;
s1num=0;
init_24c02();
write_com(0x38); //设置16*2显示,5*7点阵,8位数据接口
write_com(0x0c); //设置开显示,不显示光标
write_com(0x06); //写一个字符后,地址指针+1
write_com(0x01); //显示清零,数据指针清零
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]); //显示年月日
yanshi(5);
}
write_com(0x80+0x40+6); //显示年月日之间的‘:’
write_date(':');
yanshi(5);
write_com(0x80+0x40+9);
write_date(':');
yanshi(5);
miao=read_add(1); //从AT24C02读取数据
fen=read_add(2);
shi=read_add(3);
write_sfm(10,miao); //写入数据
write_sfm(7,fen);
write_sfm(4,shi);
TMOD=0x01; //定时器设置
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
}
此函数主要用于实验板刚上电,将变量归零,显示日期,读入上次时钟数据,计时器和中断器的启动,和防止出现乱码的现象。
8.按键扫描函数
void keyscan() //按键扫描函数
{
if(s1==0) //功能键被按下
{
yanshi(5); //防止抖动
if(s1==0) //功能键确定被按下
{
s1num++;
while(!s1); //等待功能键松开
di();
if(s1num==1)
{
TR0=0; //定时器关闭
write_com(0x80+0x40+10);//光标定位到秒的十位
write_com(0x0f); //光标闪烁
}
if(s1num==2)
{
write_com(0x80+0x40+7);//光标定位到分钟的十位
}
if(s1num==3)
{
write_com(0x80+0x40+4);//光标定位到小时的十位
}
if(s1num==4)
{
s1num=0; //初始功能键计数
write_com(0x0c); //取消光标闪烁
TR0=1; //定时器开启
}
}
}
if(s1num!=0) //秒增加
{
if(s2==0) //增加键按下
{
yanshi(5); //防止抖动
if(s2==0)
{
while(!s2); //增加键释放
di();
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(10,miao); //输出秒到液晶屏
write_com(0x80+0x40+10); //回调显示位置
write_add(1,miao); //将数据存入AT24C02
}
if(s1num==2) //分增加
{
fen++;
if(fen==60)
fen=0;
write_sfm(7,fen); //输入分到液晶屏
write_com(0x80+0x40+7);//回调显示位置
write_add(2,fen); //将数据存入AT24C02
}
if(s1num==3) //时增加
{
shi++;
if(shi==24)
shi=0;
write_sfm(4,shi);//输入时到液晶屏
write_com(0x80+0x40+4);//回调显示位置
write_add(3,shi); //将数据存到AT24C02
}
}
}
if(s3==0) //减小键被按下
{
yanshi(5);
if(s3==0) //确认减小键被按下
{
while(!s3); //等待减小键松开
di();
if(s1num==1)//秒减小
{
miao--;
if(miao==-1)
miao=59;
write_sfm(10,miao); //输入秒到显示屏
write_com(0x80+0x40+10);//回调显示位置
write_add(1,miao);//将数据存入AT24C02
}
if(s1num==2)//分减小
{
fen--;
if(fen==-1)
fen=59;
write_sfm(7,fen); //输入分到显示屏
write_com(0x80+0x40+7);//回调显示位置
write_add(2,fen);//将数据存入AT24C02
}
if(s1num==3) //时减小
{
shi--;
if(shi==-1)
shi=23;
write_sfm(4,shi); //输入到显示屏
write_com(0x80+0x40+4);//回调显示位置
write_add(3,shi);//将数据存入AT24C02
}
}
}
}
}
此函数赋予51单片机时钟大量的功能,主要有功能键,增加键,减少键的设置,和每次调整时的光标闪动和数据的实时保存。
9.主函数
void main()
{
init();
while(1)
{
keyscan();//按键持续扫描
}
}
使用while循环使键盘一直处于扫描的状态。
10.定时器0的中断
void timer0() interrupt 1 //定时器
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==20) //当count=20,为一秒
{
count=0;
miao++;
if(miao==60) //进制转换
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
write_sfm(4,shi); //将数据写入显示屏
write_add(3,shi); //将数据存入AT24C02
}
write_sfm(7,fen);
write_add(2,fen);
}
write_sfm(10,miao);
write_add(1,miao);
}
}
用于时间的变换和实时的数据处理。
11.AT24C02应用函数
bit write=0; //AT24C02的标志
sbit sda=P2^0;
sbit scl=P2^1;
void yanshi0(){ ;; }
void start()//启动信号
{
sda=1;
yanshi0();
scl=1;
yanshi0();
sda=0;
yanshi0();
}
void stop()//停止
{
sda=0;
yanshi0();
scl=1;
yanshi0();
sda=1;
yanshi0();
}
void respons()//应答
{
uchar i;
scl=1;
yanshi0();
while((sda==1)&&(i<250))
i++;
scl=0;
yanshi0();
}
void init_24c02()//总线初始化
{
sda=1;
yanshi0();
scl=1;
yanshi0();
}
void write_byte(uchar date)//写1字节
{
uchar i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0;
yanshi0();
sda=CY;
yanshi0();
scl=1;
yanshi0();
}
scl=0;
yanshi0();
sda=1;
yanshi0();
}
uchar read_byte()//读1字节
{
uchar i,k;
scl=0;
yanshi0();
sda=1;
yanshi0();
for(i=0;i<8;i++)
{
scl=1;
yanshi0();
k=(k<<1)|sda;
scl=0;
yanshi0();
}
return k;
}
void write_add(uchar address,uchar date)//指定地址写1字节
{
start();
write_byte(0xa0);
respons();
write_byte(address);
respons();
write_byte(date);
respons();
stop();
}
char read_add(uchar address)//指定地址读1字节
{
uchar date;
start();
write_byte(0xa0);
respons();
write_byte(address);
respons();
start();
write_byte(0xa1);
respons();
date=read_byte();
stop();
return date;
}
主要使用数据的实时传输已达到数据存储的目的。
全部代码
#include<reg52.h> //52单片机头文件
#define uchar unsigned char
#define uint unsigned int
#include"24c02.h"
sbit dula=P2^6; //定义锁存器
sbit wela=P2^7;
sbit rs=P3^5; //1602液晶rs端
sbit lcden=P3^4; //lcden端
sbit s1=P3^0; //功能键
sbit s2=P3^1; //增大键
sbit s3=P3^2; //减小键
sbit rd=P3^7; //矩阵第四列
sbit beep=P2^3; //蜂鸣器
uchar count ,s1num;
char miao,shi,fen;
uchar code table[]=" 2023-3-17 FRI"; //日期
void yanshi(uint max) //延时函数
{
int i,j;
for(i=max;i>0;i--)
for(j=110;j>0;j--);
}
void di() //蜂鸣器函数
{
beep=1;
yanshi(100);
beep=1;
}
void write_com(uchar com) //液晶屏写命令函数
{
rs=0;
lcden=0; //选择写命令模式
P0=com; //将要写的命令送到命令总线上
yanshi(5); //延时稳定
lcden=1; //给lcden高脉冲将命令送进液晶控制器
yanshi(5);
lcden=0;
}
void write_date(uchar date) //液晶写数据函数
{
rs=1;
lcden=0; //选择模式
P0=date; //将数据送到数据总线
yanshi(5); //延时稳定
lcden=1; //给lcden高脉冲将数据送进液晶控制器
yanshi(5);
lcden=0;
}
void write_sfm(uchar add,uchar date) //写时分秒函数
{
uchar shi,ge;
shi=date/10; //分解数据个十位
ge=date%10;
write_com(0x80+0x40+add); //设置显示位置
write_date(0x30+shi); //十位显示
write_date(0x30+ge); //个位显示
}
void init() //初始化函数
{
uchar num;
rd=0; //矩阵第四列一端置低,分解独立按键
dula=0; //关闭锁存器,防止液晶屏乱码
wela=0;
lcden=0;
fen=0; //初始数据
miao=0;
shi=0;
count=0;
s1num=0;
init_24c02();
write_com(0x38); //设置16*2显示,5*7点阵,8位数据接口
write_com(0x0c); //设置开显示,不显示光标
write_com(0x06); //写一个字符后,地址指针+1
write_com(0x01); //显示清零,数据指针清零
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]); //显示年月日
yanshi(5);
}
write_com(0x80+0x40+6); //显示年月日之间的‘:’
write_date(':');
yanshi(5);
write_com(0x80+0x40+9);
write_date(':');
yanshi(5);
miao=read_add(1); //从AT24C02读取数据
fen=read_add(2);
shi=read_add(3);
write_sfm(10,miao); //写入数据
write_sfm(7,fen);
write_sfm(4,shi);
TMOD=0x01; //定时器设置
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
}
void keyscan() //按键扫描函数
{
if(s1==0) //功能键被按下
{
yanshi(5); //防止抖动
if(s1==0) //功能键确定被按下
{
s1num++;
while(!s1); //等待功能键松开
di();
if(s1num==1)
{
TR0=0; //定时器关闭
write_com(0x80+0x40+10);//光标定位到秒的十位
write_com(0x0f); //光标闪烁
}
if(s1num==2)
{
write_com(0x80+0x40+7);//光标定位到分钟的十位
}
if(s1num==3)
{
write_com(0x80+0x40+4);//光标定位到小时的十位
}
if(s1num==4)
{
s1num=0; //初始功能键计数
write_com(0x0c); //取消光标闪烁
TR0=1; //定时器开启
}
}
}
if(s1num!=0) //秒增加
{
if(s2==0) //增加键按下
{
yanshi(5); //防止抖动
if(s2==0)
{
while(!s2); //增加键释放
di();
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(10,miao); //输出秒到液晶屏
write_com(0x80+0x40+10); //回调显示位置
write_add(1,miao); //将数据存入AT24C02
}
if(s1num==2) //分增加
{
fen++;
if(fen==60)
fen=0;
write_sfm(7,fen); //输入分到液晶屏
write_com(0x80+0x40+7);//回调显示位置
write_add(2,fen); //将数据存入AT24C02
}
if(s1num==3) //时增加
{
shi++;
if(shi==24)
shi=0;
write_sfm(4,shi);//输入时到液晶屏
write_com(0x80+0x40+4);//回调显示位置
write_add(3,shi); //将数据存到AT24C02
}
}
}
if(s3==0) //减小键被按下
{
yanshi(5);
if(s3==0) //确认减小键被按下
{
while(!s3); //等待减小键松开
di();
if(s1num==1)//秒减小
{
miao--;
if(miao==-1)
miao=59;
write_sfm(10,miao); //输入秒到显示屏
write_com(0x80+0x40+10);//回调显示位置
write_add(1,miao);//将数据存入AT24C02
}
if(s1num==2)//分减小
{
fen--;
if(fen==-1)
fen=59;
write_sfm(7,fen); //输入分到显示屏
write_com(0x80+0x40+7);//回调显示位置
write_add(2,fen);//将数据存入AT24C02
}
if(s1num==3) //时减小
{
shi--;
if(shi==-1)
shi=23;
write_sfm(4,shi); //输入到显示屏
write_com(0x80+0x40+4);//回调显示位置
write_add(3,shi);//将数据存入AT24C02
}
}
}
}
}
void main()
{
init();
while(1)
{
keyscan();//按键持续扫描
}
}
void timer0() interrupt 1 //定时器
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==20) //当count=20,为一秒
{
count=0;
miao++;
if(miao==60) //进制转换
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
write_sfm(4,shi); //将数据写入显示屏
write_add(3,shi); //将数据存入AT24C02
}
write_sfm(7,fen);
write_add(2,fen);
}
write_sfm(10,miao);
write_add(1,miao);
}
}
bit write=0; //AT24C02的标志
sbit sda=P2^0;
sbit scl=P2^1;
void yanshi0(){ ;; }
void start()//启动信号
{
sda=1;
yanshi0();
scl=1;
yanshi0();
sda=0;
yanshi0();
}
void stop()//停止
{
sda=0;
yanshi0();
scl=1;
yanshi0();
sda=1;
yanshi0();
}
void respons()//应答
{
uchar i;
scl=1;
yanshi0();
while((sda==1)&&(i<250))
i++;
scl=0;
yanshi0();
}
void init_24c02()//总线初始化
{
sda=1;
yanshi0();
scl=1;
yanshi0();
}
void write_byte(uchar date)//写1字节
{
uchar i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0;
yanshi0();
sda=CY;
yanshi0();
scl=1;
yanshi0();
}
scl=0;
yanshi0();
sda=1;
yanshi0();
}
uchar read_byte()//读1字节
{
uchar i,k;
scl=0;
yanshi0();
sda=1;
yanshi0();
for(i=0;i<8;i++)
{
scl=1;
yanshi0();
k=(k<<1)|sda;
scl=0;
yanshi0();
}
return k;
}
void write_add(uchar address,uchar date)//指定地址写1字节
{
start();
write_byte(0xa0);
respons();
write_byte(address);
respons();
write_byte(date);
respons();
stop();
}
char read_add(uchar address)//指定地址读1字节
{
uchar date;
start();
write_byte(0xa0);
respons();
write_byte(address);
respons();
start();
write_byte(0xa1);
respons();
date=read_byte();
stop();
return date;
}
总结
此代码是我参考51单片机教材书,经过不断的调试,也对程序的功能有了一个详细的理解,也把程序中的注释进行了完善。在编写过程中,有很多的难点,比如在I^2C的部分中,学的还是不是很熟。在调试阶段,经常会出现乱码的现象,不过好在最后都纠正了。