DS1302.c
:该函数主要实现把数据写入DS1302中从而实现时间初始化和时间写入,以及从时钟芯片中读取时间。Timer0Init.c
:实现定时器T0的初始化,此处定时器用于调整时间时,调整位闪烁。Key.c
:扫描按键,在主函数中体现在:当按键K1按下时,时钟暂停,此时接着按键K2按下进入时钟调节函数(同时LCD1602屏幕上“年”开始以1s为周期闪烁),调节完成后再按下按键K1,则时钟继续运行。当时钟运行时按下按键K1没有反应,想要调整时钟则必须先暂停。(此处时钟暂停并不是真正的暂停,只是显示在LCD1602显示屏上是静止状态,但其实只是没有读取DS1302芯片的寄存器而已)Clock_Adjust.c
:时钟调整函数,当进入时钟调整函数后再次按下按键K2,则LCD1602显示屏上从“年”闪烁变为“月”闪烁,再次按下按键K2,“月”闪烁变为“日”闪烁,以此类推,往复。在次函数中若按下按键K1,则break
跳出此函数,时钟继续运行。在此函数中若按下按键K3,则所闪烁位数值加一,若按下按键K4,则所闪烁位数值减一。从而实现时钟调节功能。LCD1602
:显示函数,将从DS1302芯片中读取到的时间显示在屏幕上。main
:主函数,通过调用上面函数模块以及相应逻辑,从而实现时钟显示以及可调性。DS1302.c
)DS1302.h
头文件中代码如下:#ifndef __DS1302_H__
#define __DS1302_H__
extern int DS1302_Time[]; //在DS1302.c中定义,此处声明外部可调用,用于主函数以及Clock_Adjust.c
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ShowTime(void);
//寄存器写入地址/指令定义
#define SECOND 0x80
#define MINUTE 0x82
#define HOUR 0x84
#define DATE 0x86
#define MONTH 0x88
#define DAY 0x8A
#define YEAR 0x8C
#define WP 0x8E
#endif
DS1302.c
函数代码及注释如下:#include <STC89C5xRC.H>
#include "DS1302.h"
//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
int DS1302_Time[]={22,5,2,23,59,55,4};
/**
* @brief DS1302初始化
* @param 无
* @retval 无
*/
void DS1302_Init(void)
{
DS1302_SCLK=0;
DS1302_CE=0;
}
/**
* @brief DS1302写一个字节
* @param Command 命令字/地址
* @param Data 要写入的数据
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
/**
* @brief DS1302读一个字节
* @param Command 命令字/地址
* @retval 读出的数据
*/
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command=Command|0x01; //将写指令转换为读指令
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data=Data|(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
return Data;
}
/**
* @brief DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
* @param 无
* @retval 无
*/
void DS1302_SetTime(void)
{
DS1302_WriteByte(WP,0x00);
//十进制转BCD码后写入
DS1302_WriteByte(YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
DS1302_WriteByte(MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
//DS1302_WriteByte(WP,0x80); //此处为写保护,这里关闭写保护是为了便于写入数据
}
/**
* @brief DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
* @param 无
* @retval 无
*/
void DS1302_ShowTime(void)
{
//BCD码转十进制后读取
DS1302_Time[0]=DS1302_ReadByte(YEAR)/16*10+DS1302_ReadByte(YEAR)%16;
DS1302_Time[1]=DS1302_ReadByte(MONTH)/16*10+DS1302_ReadByte(MONTH)%16;
DS1302_Time[2]=DS1302_ReadByte(DATE)/16*10+DS1302_ReadByte(DATE)%16;
DS1302_Time[3]=DS1302_ReadByte(HOUR)/16*10+DS1302_ReadByte(HOUR)%16;
DS1302_Time[4]=DS1302_ReadByte(MINUTE)/16*10+DS1302_ReadByte(MINUTE)%16;
DS1302_Time[5]=DS1302_ReadByte(SECOND)/16*10+DS1302_ReadByte(SECOND)%16;
DS1302_Time[6]=DS1302_ReadByte(DAY);
}
Timer0Init.c
)#include <STC89C5xRC.H>
void Timer0Init()
{
TMOD=TMOD&0xf0;
TMOD=TMOD|0x01;//配置定时器T0工作模式
TF0=0;
TR0=1;
TH0=56320/256;
TL0=56320%256;//计时10ms,11.0592MHz
EA=1;
ET0=1;
IPH=IPH&0xFD;//中断优先级控制寄存器高,置零PT0H位
PT0=0; //中断优先级控制寄存器低,置零PT0位
}
当程序中只有一种中断时,中断优先级没有意义,IPH
、PT0
也可以不配置。
Key.c
)#include <STC89C5xRC.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P31==0){Delay(20);while(P31==0);Delay(20);KeyNumber=1;}
if(P30==0){Delay(20);while(P30==0);Delay(20);KeyNumber=2;}
if(P32==0){Delay(20);while(P32==0);Delay(20);KeyNumber=3;}
if(P33==0){Delay(20);while(P33==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
Delay
函数代码如下:#include <intrins.h> //_nop_()函数头文件
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
Clock_Adjust.c
)void Timer0_Rountine(void) interrupt 1
{
static unsigned int cont;
cont++;
TH0=56320/256;
TL0=56320%256; //重装载值,溢出率为10ms/次
if(cont>100) //每隔1s转换模式
{
cont=0;
switch(MODE)
{
case 0:MODE=1;break;
case 1:MODE=0;break;
}
}
}
#include <STC89C5xRC.H>
#include "Timer0Init.h"
#include "Key.h"
#include "DS1302.h"
#include "LCD1602.h"
unsigned char cont2,MODE; //cont2为记录按键K2按下的次数,同时也是所调时钟的闪烁位置
extern unsigned char KeyNum;
void Clock_Adjust(void)
{
Timer0Init();
while(1)
{
if(MODE==0)
{
LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
LCD_ShowNum(2,16,DS1302_Time[6],1);
}
if(MODE==1)
{
switch(cont2)
{
case 0:LCD_ShowString(1,1," "),LCD_ShowNum(2,16,DS1302_Time[6],1);break;
case 1:LCD_ShowString(1,4," "),LCD_ShowNum(1,1,DS1302_Time[0],2);break;
case 2:LCD_ShowString(1,7," "),LCD_ShowNum(1,4,DS1302_Time[1],2);break;
case 3:LCD_ShowString(2,1," "),LCD_ShowNum(1,7,DS1302_Time[2],2);break;
case 4:LCD_ShowString(2,4," "),LCD_ShowNum(2,1,DS1302_Time[3],2);break;
case 5:LCD_ShowString(2,7," "),LCD_ShowNum(2,4,DS1302_Time[4],2);break;
case 6:LCD_ShowString(2,16," "),LCD_ShowNum(2,7,DS1302_Time[5],2);break;
}
}
KeyNum=Key();
if(KeyNum==2)
{
cont2++; //记录按键K2按下的次数,同时定位调节时钟所调整的位置
if(cont2>6)
cont2=0;
}
if(KeyNum==3)
{
switch(cont2)
{
case 0: //调节年份++
{
DS1302_Time[0]++;
if(DS1302_Time[0]>99)
DS1302_Time[0]=0;
if(DS1302_Time[0]%4!=0&&DS1302_Time[1]==2&&DS1302_Time[2]>28)
DS1302_Time[2]=1;
break;
}
case 1: //调节月份++
{
DS1302_Time[1]++;
if(DS1302_Time[1]>12)
DS1302_Time[1]=1;
if((DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11)&&DS1302_Time[2]>30)
{
DS1302_Time[2]=1;
}
else if(DS1302_Time[0]%4==0&&DS1302_Time[1]==2&&DS1302_Time[2]>29)
{
DS1302_Time[2]=1;
}
else if(DS1302_Time[0]%4!=0&&DS1302_Time[1]==2&&DS1302_Time[2]>28)
{
DS1302_Time[2]=1;
}
break;
}
case 2: //调节日++
{
DS1302_Time[2]++;
if(DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11)
{
if(DS1302_Time[2]>30)
DS1302_Time[2]=1;
}
else if(DS1302_Time[1]==1||DS1302_Time[1]==3||DS1302_Time[1]==5||DS1302_Time[1]==7||DS1302_Time[1]==8||DS1302_Time[1]==10||DS1302_Time[1]==12)
{
if(DS1302_Time[2]>31)
DS1302_Time[2]=1;
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0&&DS1302_Time[2]>29)
{
DS1302_Time[2]=1;
}
else if(DS1302_Time[0]%4!=0&&DS1302_Time[2]>28)
{
DS1302_Time[2]=1;
}
}
break;
}
case 3: //调节时++
{
DS1302_Time[3]++;
if(DS1302_Time[3]>23)
DS1302_Time[3]=0;
break;
}
case 4: //调节分++
{
DS1302_Time[4]++;
if(DS1302_Time[4]>59)
DS1302_Time[4]=0;
break;
}
case 5: //调节秒++
{
DS1302_Time[5]++;
if(DS1302_Time[5]>59)
DS1302_Time[5]=0;
break;
}
case 6: //调节星期++
{
DS1302_Time[6]++;
if(DS1302_Time[6]>7)
DS1302_Time[6]=1;
break;
}
}
}
if(KeyNum==4)
{
switch(cont2) //调节年份--
{
case 0:
{
DS1302_Time[0]--;
if(DS1302_Time[0]<0)
DS1302_Time[0]=99;
if(DS1302_Time[0]%4!=0&&DS1302_Time[1]==2&&DS1302_Time[2]>28)
DS1302_Time[2]=1;
break;
}
case 1: //调节月份--
{
DS1302_Time[1]--;
if(DS1302_Time[1]<1)
DS1302_Time[1]=12;
if((DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11)&&DS1302_Time[2]>30)
{
DS1302_Time[2]=1;
}
else if(DS1302_Time[0]%4==0&&DS1302_Time[1]==2&&DS1302_Time[2]>29)
{
DS1302_Time[2]=1;
}
else if(DS1302_Time[0]%4!=0&&DS1302_Time[1]==2&&DS1302_Time[2]>28)
{
DS1302_Time[2]=1;
}
break;
}
case 2: //调节日--
{
DS1302_Time[2]--;
if(DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11)
{
if(DS1302_Time[2]<1)
DS1302_Time[2]=30;
}
else if(DS1302_Time[1]==1||DS1302_Time[1]==3||DS1302_Time[1]==5||DS1302_Time[1]==7||DS1302_Time[1]==8||DS1302_Time[1]==10||DS1302_Time[1]==12)
{
if(DS1302_Time[2]<1)
DS1302_Time[2]=31;
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0&&DS1302_Time[2]<1)
{
DS1302_Time[2]=29;
}
else if(DS1302_Time[0]%4!=0&&DS1302_Time[2]<1)
{
DS1302_Time[2]=28;
}
}
break;
}
case 3: //调节时--
{
DS1302_Time[3]--;
if(DS1302_Time[3]<0)
DS1302_Time[3]=23;
break;
}
case 4: //调节分--
{
DS1302_Time[4]--;
if(DS1302_Time[4]<0)
DS1302_Time[4]=59;
break;
}
case 5: //调节秒--
{
DS1302_Time[5]--;
if(DS1302_Time[5]<0)
DS1302_Time[5]=59;
break;
}
case 6: //调节星期--
{
DS1302_Time[6]--;
if(DS1302_Time[6]<1)
DS1302_Time[6]=7;
break;
}
}
}
if(KeyNum==1) //跳出闪烁时钟循环
{
cont2=0;
break;
}
}
}
LCD1602.c
)#include <STC89C5xRC.H>
#include <intrins.h>//延时函数_nop_()头文件
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,11.0592MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
- @brief 在LCD1602指定位置开始显示所给数字
- @param Line 起始行位置,范围:1~2
- @param Column 起始列位置,范围:1~16
- @param Number 要显示的数字,范围:0~65535
- @param Length 要显示数字的长度,范围:1~5
- @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
main.c
)主函数实现的内容,以及调用函数逻辑:1.初始化LCD1602;2.初始化DS1302;3.设置初始时钟数值;4.进入死循环开始循环扫描DS1302时钟芯片中的时钟数值并读出,直到K1按键按下,停止读数据,此时若再次按下K1则重新开始继续读取数据,若按下按键K2,则进入调节时钟函数完成相应功能(详情见Clock_Adjust.c
函数)
主函数代码如下:
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Clock_Adjust.h"
extern int DS1302_Time[];
unsigned char KeyNum;
void main()
{
LCD_Init();
DS1302_Init();
LCD_ShowString(1,3,"- -");
LCD_ShowString(2,3,": :");
DS1302_SetTime();
while(1)
{
DS1302_ShowTime();
LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
LCD_ShowNum(2,16,DS1302_Time[6],1);
KeyNum=Key();
if(KeyNum==1) //满足条件时进入死循环
{
while(1)
{
KeyNum=Key();
if(KeyNum==1) //死循环中K1键再次按下时跳出死循环
break;
if(KeyNum==2) //死循环中K2键按下时进入时钟调节函数
{
Clock_Adjust(); //进入用按键调节时钟函数
break; //跳出暂停时钟循环
}
}
DS1302_SetTime();
}
}
}