这篇代码并未运用到DSC12C887时钟芯片,而是运用了单片机自带的定时器0完成,经试验在清翔开发板上误差为每小时快4s,且可通过硬件(矩阵键盘)更改时间!
前言
通过写万年历来达到熟练使用定时器、LCD1602(甚至是OLED12864)和矩阵键盘的练习。
提示:以下是本篇文章正文内容,下面案例可供参考
一、目标
1.熟练掌握矩阵键盘的用法
2.熟练定时器安装初值
3.熟练1602的使用方法
二、教学
1.矩阵键盘
郭天祥老师在《新概念51单片机C语言教程》一书中给了他的例程,但我认为太过于繁琐。
这里我给出另一个解决方案:
1.先给低四位一个高电平(0x0f),检测是哪一个I/O口被拉低,确定出某行Y
2.立马给高四位一个高电平(0xf0),检测是哪一个I/O口被拉低,确定出某列X
3.得出按键坐标(X,Y)
2.LCD1602
想要学会1602就应该掌握它的基本时序操作:
读状态 RS=Low,R/W=High,EN=H
读数据 RS=H,R/W=H,EN=H
写指令 RS=L,R/W=L,D0~D7=指令码,EN=高脉冲
写数据 RS=H,R/W=L,D0~D7=数据,EN=H
其控制器内部的RAM区域有80B的缓冲区可供我们写显示数据(00—0F、10—27、40—4F、50—67),其中在00—0F、40—4F中写入的数据可以直接显示出来,而另外两个地址的数据需要通过移动。可以使用“0x18”或者“0x07”,读者可自己探索。
关于1602将在例程中继续为大家讲解
3.定时器0
一般建立一个函数“void T0_time() interrupt 1”,其中interrupt 1是定时器0的中断序号,单片机通过它来识别。C51一般用的是11.0592MHz的晶振,装入初值方式如下:(例如50ms)
TH1=(65536-N)/256 ,TL1=(65536-N)%256
50mS=50 000uS
周期为12*(1/11059200)约等于1.09uS
N=50 000 /1.09 约等于 45872
三、例程(各个函数的作用我将在头文件为大家说明)
1.矩阵键盘(我使用的I/O口为P1)
Key.h
#ifndef __KEY_H
#define __KEY_H
#include "reg52.h"
/*宏定义*/
#define uchar unsigned char
#define uint unsigned int
#define Keyy P1
void key_scan();//负责扫描矩阵键盘
void delay(uint xms);//毫秒级延时
extern uchar Key_C;//列
extern uchar Key_R;//行
extern uint Key_Num;//对按键赋予名称
#endif
Key.c
#include "Key.h"
uchar Key_C;//列
uchar Key_R;//行
uint Key_Num;
void key_scan()
{
Keyy = 0x0f;//检测低四位某行
if(Keyy != 0x0f)//检测是否被按下
{
delay(1);//消抖
if(Keyy != 0x0f)//再次检测是否被按下
{
switch(Keyy)//确定某行
{
case(0x07): Key_R = 0; break;//第一行
case(0x0b): Key_R = 1; break;//第二行
case(0x0d): Key_R = 2; break;//第三行
case(0x0e): Key_R = 3; break;//第四行
}
}
}
Keyy = 0xf0;//检测高四位某列
if(Keyy != 0xf0)
{
delay(1);
if(Keyy != 0Xf0)
{
switch(Keyy)
{
case(0x70): Key_C = 0; break;//第一列
case(0xb0): Key_C = 1; break;//第二列
case(0xd0): Key_C = 2; break;//第三列
case(0xe0): Key_C = 3; break;//第四列
}
}
}
/*确定出按键后为其命名*/
if(Key_R==0&&Key_C==0) Key_Num=16;
else if(Key_R==0&&Key_C==1) Key_Num=15;
else if(Key_R==0&&Key_C==2) Key_Num=14;
else if(Key_R==0&&Key_C==3) Key_Num=13;
else if(Key_R==1&&Key_C==0) Key_Num=12;
else if(Key_R==1&&Key_C==1) Key_Num=11;
else if(Key_R==1&&Key_C==2) Key_Num=10;
else if(Key_R==1&&Key_C==3) Key_Num=9;
else if(Key_R==2&&Key_C==0) Key_Num=8;
else if(Key_R==2&&Key_C==1) Key_Num=7;
else if(Key_R==2&&Key_C==2) Key_Num=6;
else if(Key_R==2&&Key_C==3) Key_Num=5;
else if(Key_R==3&&Key_C==0) Key_Num=4;
else if(Key_R==3&&Key_C==1) Key_Num=3;
else if(Key_R==3&&Key_C==2) Key_Num=2;
else if(Key_R==3&&Key_C==3) Key_Num=1;
}
void delay(uint xms)
{
uint i,j;
for(i=xms ;i>0 ;i--)
for(j=100 ;j>0 ;j--);
}
2.LCD1602
1602.h
#ifndef _1602_H
#define _1602_H
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit lcdrs=P3^5;
sbit lcdrw=P3^6;
sbit lcden=P3^4;
sbit dula=P2^6;
sbit wela=P2^7;
void init();//初始化
void Write_Com(uchar com);//写指令
void Write_Data(uchar date);//写数据
void Write_Str(uchar addr,uchar length,uchar *pbuf);/*显示字符串,第一个为地址,第二个为
长度,第三个为字符*/
void CGRAM_set();//将自定义字符数据写入CGRAM ,用户可以自定义8个字符
void lcd_wdat(uchar addr,int date);//写4个字符
void lcd_wdat1(uchar addr,int date);//写2个字符
#endif
1602.c
#include "1602.h"
#include "delay.h"
void init()
{
dula=0;
wela=0;
lcden=0;//关闭数码管显示,因为我在开发板上使用,I/O口与数码管重复
Write_Com(0x38);
Write_Com(0x0c);
Write_Com(0x06);
Write_Com(0x01);
}
void Write_Com(uchar com)
{
lcdrs=0;
lcdrw=0;
P0=com;
Delay_Ms(5);
lcden=1;
Delay_Ms(5);
lcden=0;
}
void Write_Data(uchar date)
{
lcdrs=1;
lcdrw=0;
P0=date;
Delay_Ms(5);
lcden=1;
Delay_Ms(5);
lcden=0;
}
void Write_Str(uchar addr,uchar length,uchar *pbuf)
{
uchar i;
Write_Com(addr);
for(i=0;i<length;i++)
{
Write_Data(pbuf[i]);
}
}
void lcd_wdat(uchar addr,int date)
{
uchar a,b,c,d;
a=date/1000;
b=date%1000/100;
c=date%100/10;
d=date%10;
Write_Com(addr);
Write_Data(0x30+a);//0x30是属于ASCII码表,加上它就能显示数值
Write_Data(0x30+b);
Write_Data(0x30+c);
Write_Data(0x30+d);
}
void lcd_wdat1(uchar addr,int date)
{
uchar a,b;
a=date/10;
b=date%10;
Write_Com(addr);
Write_Data(0x30+a);
Write_Data(0x30+b);
}
3.延时
delay.h
#ifndef _DELAY_H_
#define _DELAY_H_
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
void Delay_Ms(uint xms);
void Delay_Us(uint xus);
#endif
delay.c
#include "delay.h"
void Delay_Ms(uint xms)//毫秒级
{
int x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
void Delay_Us(uint xus)//微妙级
{
while(xus--);
}
3.主函数
按键:1时2分3秒加 5时6分7秒减
9年10月11日期加 13年14月15日期减
更改日期摁4键,先摁4再摁修改,再摁4,再修改
#include "reg52.h"
#include "1602.h"
#include "delay.h"
#include "Key.h"
uchar num;
int s;
void main()
{
int y,m,d;
int h,min;
TMOD=0x01;
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
EA=1;
ET0=1;
TR0=1;
init();
y=2020;
m=12;
d=12;
h=16;
min=33;
s=0;
/*****************************|
|******日期逻辑设置***********|
|*****************************/
A:/*goto标记位置*/
while(1)
{
/******************************|
|***********扫描键盘***********|
|******************************/
key_scan();
if(Key_Num==1)
{
y++;
}
if(Key_Num==2)
{
m++;
}
if(Key_Num==3)
{
d++;
}
if(Key_Num==5)
{
y--;
}
if(Key_Num==6)
{
m--;
}
if(Key_Num==7)
{
d--;
}
if(Key_Num==9)
{
h++;
}
if(Key_Num==10)
{
min++;
}
if(Key_Num==11)
{
s++;
}
if(Key_Num==13)
{
h--;
}
if(Key_Num==14)
{
min--;
}
if(Key_Num==15)
{
s--;
}
goto B;
}
/******************************|
|*************显示*************|
|******************************/
B:while(1)/*goto标记位置*/
{
key_scan();
lcd_wdat(0x80,y);
Write_Str(0x84,1,"-");
lcd_wdat1(0x85,m);
Write_Str(0x87,1,"-");
lcd_wdat1(0x88,d);
lcd_wdat1(0x80+0x40,h);
Write_Str(0x82+0x40,1,":");
lcd_wdat1(0x83+0x40,min);
Write_Str(0x85+0x40,1,":");
lcd_wdat1(0x86+0x40,s);
/******************************|
|***********判断进位***********|
|******************************/
if(m>12)
{
m=1;
y++;
}
else if(m<1)
{
m=12;
y--;
}
if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
{
if(d>31)
{
d=1;
m++;
}
else if(d<1)
{
if(m==5||m==7||m==10||m==12)
{
d=30;
m--;
}
else if(m==2||m==4||m==6||m==8||m==9||m==11||m==1)
{
d=31;
m--;
}
else if(m==3&&(y%4==0)&&(y%100!=0))
{
d=29;
m--;
}
else
{
d=28;
m--;
}
}
}
if(m==4||m==6||m==9||m==11)
{
if(d>30)
{
d=1;
m++;
}
else if(d<1)
{
if(m==5||m==7||m==10||m==12)
{
d=30;
m--;
}
else if(m==2||m==4||m==6||m==8||m==9||m==11||m==1)
{
d=31;
m--;
}
else if(m==3&&(y%4==0)&&(y%100!=0))
{
d=29;
m--;
}
else
{
d=28;
m--;
}
}
}
if(m==2)
{
if((y%4==0)&&(y%100!=0))
{
if(d>29)
{
d=1;
m++;
}
if(d<1)
{
d=31;
m--;
}
}
else
{
if(d>28)
{
d=1;
m++;
}
if(d<1)
{
d=31;
m--;
}
}
}
if(h>23)
{
h=0;
d++;
}
else if(h<0)
{
h=23;
}
if(min>59)
{
min=0;
h++;
}
else if(min<0)
{
min=59;
h--;
}
if(s>59)
{
s=0;
min++;
}
else if(s<0)
{
s=59;
min--;
}
if(Key_Num==4)
goto A;
}
}
void T0_time() interrupt 1
{
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
num++;
if(num==20)
{
s++;
num=0;
}
}
总结
这里的总结交给goto
虽然不提倡使用goto,因为会使代码结构不清晰,但是合理的运用goto更能为代码锦上添花,这里巧妙的使用了goto反而使结构思路清晰。
欢迎大家关注我
微信公众号:小白写编程
交流群:1033131250