一.DS1302
二.中断
三.矩阵键盘
四.数字时钟
一.DS1302
DS1302是低功耗实时时钟芯片,它可以对年月周时分秒进行计时,且具有闰年补偿等多种功能
1.初始化将CE置于0,SCLK置0
2.数据写入:先写地址后写数据
地址有秒,分,时,日,月,星期,年,写保护等,如下图第二列就是写入时的地址,通过地址将数据写入DS1302,就完成了时间的初始化
开始后将CE置1;
确定要写入的地址和数据data(8位的二进制数);
通过字节运算取出地址和data的每一位(第0位到第七位)赋值给I/O;
将SLCK置1;
将SLCK置0;
这样就完成一个位的写入了。通过for循环将地址和数据共十六位写入了DS1302.
同样读出数据也是先输入地址(第一列,和写入的地址不同),再读出数据。
注意,写入和读出都是按照BCD码
举个例子,想要将23写入秒,首先将22的个位和十位拆开分别是2和3,然后十位的2用四位二进制表示0010,个位2用四位二进制表示0011,再拼起来就是0010 0011,也就是说在BCD码中0010 0011表示23这个数据
二.中断
中断系统包括:
5个中断源(外部中断0,1;定时器中断0,1;串行通信口中断)
中断源寄存器,分定时器/计数器控制寄存器TCON和串行通信口控制寄存器SCON
中断允许寄存器IE
中断优先级控制寄存器IP
中断系统结构如下:
定时器0的使用
代码如下:
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
单片机内部定时器的结构:
三.矩阵键盘
矩阵键盘判断哪个按键被按下是通过行列式扫描
由P1控制,P0~P3控制行,P4~P7控制列,下图就是每个键盘对应的行和列
下列是获取键码值函数:
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 矩阵键盘读取按键键码
* @param 无
* @retval KeyNumber 按下按键的键码值
如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
*/
unsigned char MatrixKey()
{
unsigned char KeyNumber=0;
P1=0xFF;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0){TimeShow();}Delay(20);KeyNumber=1;}
if(P1_6==0){Delay(20);while(P1_6==0){TimeShow();}Delay(20);KeyNumber=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
P1=0xFF;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0){TimeShow();}Delay(20);KeyNumber=2;}
if(P1_6==0){Delay(20);while(P1_6==0){TimeShow();}Delay(20);KeyNumber=6;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
P1=0xFF;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0){TimeShow();}Delay(20);KeyNumber=3;}
if(P1_6==0){Delay(20);while(P1_6==0){TimeShow();}Delay(20);KeyNumber=7;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
P1=0xFF;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0){TimeShow();}Delay(20);KeyNumber=4;}
if(P1_6==0){Delay(20);while(P1_6==0){TimeShow();}Delay(20);KeyNumber=8;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
return KeyNumber;
}
四.数字时钟
利用数码管,DS1302,中断,矩阵键盘设置的数字时钟
解决按键按下未松开时数码管卡住:将时间显示函数TimeShow()插入获取键码函数MatrixKey()的死循环中。
数字时钟代码如下:
#include <REGX52.H>
#include "DS1302.h"
#include "MatrixKey.h"
#include "Timer0.h"
#include "Delay.h"
unsigned char KeyNumber,TimeSetFlashFlag=0;//键码值和时钟闪烁标志
unsigned char b=1,c,d=0;
//nixie()第一个参数是数码管位置loc,第二个参数是数码管某位置显示的数字/不显示
unsigned char table[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00}; //0 1 2 3 4 5 6 7 8 9 空
void nixie(unsigned char loc,num)
{
switch(loc)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0 =table[num];
}
//时间显示函数
void TimeShow(void)
{
//数码管第一,二 位显示的设置:当处于时间设置模式 且按下s1或s5时,闪烁数字
//否则固定数字 并实时更新时间
if((KeyNumber==1||KeyNumber==5)&&TimeSetFlashFlag==1)
{Delay(1);nixie(1,10);Delay(1);nixie(2,10);}
else{
nixie(1,DS1302_Time[3]/10);Delay(2);
nixie(2,DS1302_Time[3]%10);Delay(2);}
//数码管第三位在计时模式时闪烁-,在设置模式时固定-
if(b==1&&TimeSetFlashFlag==1)
{P2_4=1;P2_3=0;P2_2=1;P0=0x40;Delay(2);}
if(b==0){P2_4=1;P2_3=0;P2_2=1;P0=0x40;Delay(2);}
//数码管第四,五 位显示的设置:当处于时间设置模式 且按下s2或s6时,闪烁数字
//否则固定数字 并实时更新时间
if((KeyNumber==2||KeyNumber==6)&&TimeSetFlashFlag==1)
{Delay(1);nixie(4,10);Delay(1);nixie(5,10);}
else{
nixie(4,DS1302_Time[4]/10);Delay(2);
nixie(5,DS1302_Time[4]%10);Delay(2);}
//数码管第五位在计时模式时闪烁-,在设置模式时固定-
if(b==1&&TimeSetFlashFlag==1)
{P2_4=0;P2_3=1;P2_2=0;P0=0x40;Delay(2);}
if(b==0){P2_4=0;P2_3=1;P2_2=0;P0=0x40;Delay(2);}
//数码管第七,八 位显示的设置:当处于时间设置模式 且按下s3或s7时,闪烁数字
//否则固定数字 并实时更新时间
if((KeyNumber==3||KeyNumber==7)&&TimeSetFlashFlag==1)
{Delay(1);nixie(7,10);Delay(1);nixie(8,10);}
else{
nixie(7,DS1302_Time[5]/10);Delay(2);
nixie(8,DS1302_Time[5]%10);Delay(2);}
//DS1302_Time[5]/10用于表示秒的十位;DS1302_Time[5]%10用于表示秒的个位;
//DS1302_Time[4]/10用于表示分的十位;DS1302_Time[4]%10用于表示分的个位;
//DS1302_Time[3]/10用于表示时的十位;DS1302_Time[3]%10用于表示时的个位;
}
void main()
{
//初始化
DS1302_Init();
Timer0Init();
DS1302_SetTime(); //设置显示的起始时间10:00:00
while(1)
{
KeyNumber=MatrixKey(); //通过s1~s8按键被按下的情况更新键码值
if(KeyNumber==4)
b=0; //b=0是设置模式,按s4进入设置模式
if(KeyNumber==8)
{
b=1;
DS1302_SetTime();
}
//b=1是显示模式,按s8,将设置模式调好的时间通过 DS1302_SetTime();写入DS1302,再进入显示模式
//显示模式
if(b==1)
{
DS1302_ReadTime();
TimeShow();
}
//设置模式
if(b==0)
{
TimeShow();
if(KeyNumber==1)
{
c=DS1302_Time[3];
c++;
DS1302_Time[3]=c;
}
if(KeyNumber==5)
{
c=DS1302_Time[3];
c--;
DS1302_Time[3]=c;
}
if(KeyNumber==2)
{
c=DS1302_Time[4];
c++;
DS1302_Time[4]=c;
}
if(KeyNumber==6)
{
c=DS1302_Time[4];
c--;
DS1302_Time[4]=c;
}
if(KeyNumber==3)
{
c=DS1302_Time[5];
c++;
DS1302_Time[5]=c;
}
if(KeyNumber==7)
{
c=DS1302_Time[5];
c--;
DS1302_Time[5]=c;
}
}
//越界判断
if(DS1302_Time[3]>23){DS1302_Time[3]=0;}
if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
if(DS1302_Time[3]<0){DS1302_Time[3]=23;}
if(DS1302_Time[4]<0){DS1302_Time[4]=59;}
if(DS1302_Time[5]<0){DS1302_Time[5]=59;}
}
}
//中断模式
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1;
TL0 = 0x18;
TH0 = 0xFC;
T0Count1++;
if(T0Count1>=450)
{
T0Count1=0;
TimeSetFlashFlag=!TimeSetFlashFlag;
}
}