目录
一、功能介绍
二、主要模块介绍
2.1 DS1302模块
2.1.1 DS1302芯片介绍
2.1.2 DS1302的使用
三、程序汇总
3.1测试文件test.c
3.2 LCD1602模块程序
3.2.1 LCD1602.h
3.2.2 LCD1602.c
3.3 DS1302模块程序
3.3.1 DS1302.h
3.3.2 DS1302.c
3.4 独立按键模块程序
3.4.1 Independentkey.h
3.4.2 Independentkey.c
3.5 T0定时器模块
3.5.1 Timer0_Iint.h
3.5.2 Timer0_Iint.c
3.6 延时函数
3.6.1 Delay.h
3.6.2 Delay.c
四、效果演示
在LCD1602上显示时钟,其中的年,月,日和时钟,分钟,秒钟还有星期都是可调的。
为什么要用DS1302芯片来计时呢?定时器不可以吗?
单片机的CPU三个引脚分别连接DS1302的使能端CE、数据输入/输出端I/O、串行时钟端SCLK三个端口。
操作 DS1302 的大致过程,就是将各种数据写入 DS1302 的寄存器,以设置 它当前的时间的格式。然后使 DS1302 开始运作,DS1302 时钟会按照设置情况 运转,再用单片机将其寄存器内的数据读出。
1.日历/时钟寄存器
DS1302 共有 12 个寄存器,其中有 7 个与日历、时钟相关,存放的数据为 BCD 码形式。
2.控制命令字
命令字简单来说就是,控制时钟寄存器的读写。比如要写入秒钟,那么操作的命令字就是0x80对应上面图中的80h;要读出秒钟,操作的命令字是0x81,对应81h。以此类推,可以控制所有时钟数据的读写。
3.时序图
上面是单字节读,下面是单字节写。根据时序图来写入数据和读出数据。
单字节写,是把单片机内存里的数据,也就是把定义的变量写入DS1302的寄存器中。有两个变量,一个是要操作的命令字(写秒钟还是写分钟...),另一个是要写入的数据。
单字节读,分两个过程,一个是写入命令字,一个是读出数据。
单字节读写中,SCLK的设置方式不同,主要是因为时序脉冲的不同,单字节读只有15个脉冲,单字节写有16个脉冲,平均分成命令字写和数据的读/写两部分。
#include
#include"LCD1602.h"
#include"DS1302.h"
#include"Independentkey.h"
#include"Timer0_Init.h"
unsigned char count;
unsigned char flash;
void showtime()
{
LCD_ShowNum(1,1,DS1302_time[0],2);
LCD_ShowString(1,3,"-");
LCD_ShowNum(1,4,DS1302_time[1],2);
LCD_ShowString(1,6,"-");
LCD_ShowNum(1,7,DS1302_time[2],2);
LCD_ShowNum(2,1,DS1302_time[3],2);
LCD_ShowString(2,3,":");
LCD_ShowNum(2,4,DS1302_time[4],2);
LCD_ShowString(2,6,":");
LCD_ShowNum(2,7,DS1302_time[5],2);
LCD_ShowString(2,10,"week:");
LCD_ShowNum(2,15,DS1302_time[6],1);
}
void num_flash()
{
if(count==0){if(flash==0){LCD_ShowString(1,1," ");}
if(flash!=0){LCD_ShowNum(1,1,DS1302_time[count],2);}
LCD_ShowNum(2,15,DS1302_time[6],1);}
if(count==1){if(flash==0){LCD_ShowString(1,4," ");}
if(flash!=0){LCD_ShowNum(1,4,DS1302_time[count],2);}
LCD_ShowNum(1,1,DS1302_time[0],2);}
if(count==2){if(flash==0){LCD_ShowString(1,7," ");}
if(flash!=0){LCD_ShowNum(1,7,DS1302_time[count],2);}
LCD_ShowNum(1,4,DS1302_time[1],2);}
if(count==3){if(flash==0){LCD_ShowString(2,1," ");}
if(flash!=0){LCD_ShowNum(2,1,DS1302_time[count],2);}
LCD_ShowNum(1,7,DS1302_time[2],2);}
if(count==4){if(flash==0){LCD_ShowString(2,4," ");}
if(flash!=0){LCD_ShowNum(2,4,DS1302_time[count],2);}
LCD_ShowNum(2,1,DS1302_time[3],2);}
if(count==5){if(flash==0){LCD_ShowString(2,7," ");}
if(flash!=0){LCD_ShowNum(2,7,DS1302_time[count],2);}
LCD_ShowNum(2,4,DS1302_time[4],2);}
if(count==6){if(flash==0){LCD_ShowString(2,15," ");}
if(flash!=0){LCD_ShowNum(2,15,DS1302_time[count],1);}
LCD_ShowNum(2,7,DS1302_time[5],2);}
}
//定时500ms中断
void Timer0_ISR() interrupt 1
{
static int num=0;
num++;
TH0=0xfc;
TL0=0x18;
if(num==500)//每隔500ms闪烁一下
{
num=0;
flash=!flash;
}
}
void setlimit()
{
//年:2000-2099年
if(DS1302_time[0]>99){DS1302_time[0]%=100;}
if(DS1302_time[0]<0){DS1302_time[0]=99;}
//月:1-12月
if(DS1302_time[1]>12){DS1302_time[1]%=13;DS1302_time[1]++;}
if(DS1302_time[1]<1){DS1302_time[1]=12;}
//日:分闰年二月,分大小月
if(DS1302_time[0]%4==0)//闰年二月 1-29号
{
if(DS1302_time[1]==2)
{
if(DS1302_time[2]>29){DS1302_time[2]%=30;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=29;}
}
}
if(DS1302_time[0]%4!=0)//不是闰年的二月 1-28号
{
if(DS1302_time[1]==2)
{
if(DS1302_time[2]>28){DS1302_time[2]%=29;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=28;}
}
}
//大月: 1,3,5,7,8,10,12 1-31号
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]%=32;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=31;}
}
//小月:4,6,9,11 1-30号
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]%=31;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=30;}
}
//时钟 0-23
if(DS1302_time[3]>23){DS1302_time[3]%=24;}
if(DS1302_time[3]<0){DS1302_time[3]=23;}
//分钟 0-59
if(DS1302_time[4]>59){DS1302_time[4]%=60;}
if(DS1302_time[4]<0){DS1302_time[4]=59;}
//秒钟 0-59
if(DS1302_time[5]>59){DS1302_time[5]%=60;}
if(DS1302_time[5]<0){DS1302_time[5]=59;}
//星期 1-7
if(DS1302_time[6]>7){DS1302_time[6]%=7;}
if(DS1302_time[6]<1){DS1302_time[6]=7;}
}
int main()
{
unsigned char keynum=0;
unsigned char mode=0;
LCD_Init();
DS1302_Init();
settime();
Timer0_Init();
while(1)
{
keynum=Independentkey();
if(keynum!=0)
{
if(keynum==1)//选择位
{
count++;
if(count>=7)
{
count=0;
}
}
if(keynum==2)//加一
{
DS1302_time[count]++;
setlimit();//对DS1302_time[count]进行调整
settime();
}
if(keynum==3)//减一
{
DS1302_time[count]--;
setlimit();
settime();
}
if(keynum==4)//设置时间和走时显示切换
{
mode=!mode;
}
}
if(mode==0)//走时模式
{
readtime();
showtime();
}
if(mode==1)//设置时间模式
{
num_flash();
}
}
}
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
#include
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
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 Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @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;i0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(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(2,i-1)%2+'0');
}
}
#ifndef __DS1302_H__
#define __DS1302_H__
#define Year_command 0x8C
#define Day_command 0x8A
#define Month_command 0x88
#define Date_command 0x86
#define Hour_command 0x84
#define Minute_command 0x82
#define Second_command 0x80
#define Wp_command 0x8E
extern char DS1302_time[];
void DS1302_Init();
void DS1302_writebyte(unsigned char command,unsigned char byte);
unsigned char DS1302_readbyte(unsigned char command);
void settime();
void readtime();
#endif
这里要注意的是:
#include
#include"DS1302.h"
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
char DS1302_time[]={23,2,24,12,0,0,5};//2023/2/24,12:00:00,星期5
/*
*函数名: DS1302_Init()
*函数功能:初始化DS1302
*输入: 无
*输出: 无
*/
void DS1302_Init()
{
DS1302_CE=0;
DS1302_SCLK=0;
}
/*
*函数名: DS1302_writebyte(unsigned char command,unsigned char byte)
*函数功能:DS1302的单字节写
*输入: command:命令字 byte:要写入的数据
*输出: 无
*/
void DS1302_writebyte(unsigned char command,unsigned char byte)
{
unsigned char i=0;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=command&(0x01<
#ifndef __INDEPENDENTKEY_H__
#define __INDEPENDENTKEY_H__
unsigned char Independentkey();
#endif
#include
#include"Delay.h"
unsigned char Independentkey()
{
unsigned char keynum=0;
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);keynum=1;}
if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);keynum=2;}
if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);keynum=3;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);keynum=4;}
return keynum;
}
#ifndef __TIMER0_INIT_H__
#define __TIMER0_INIT_H__
void Timer0_Init();
#endif
#include
void Timer0_Init()//定时器0,工作方式1
{
TMOD=0x01;
TH0=0xfc;//1ms
TL0=0x18;
EA=1;
ET0=1;
TR0=1;
}
//定时1s中断
//void Timer0_ISR() interrupt 1
//{
// static int count=0;
// count++;
// TH0=0xfc;
// TL0=0x18;
// if(count==1000)
// {
// count=0;
//
// }
//}
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int Xms);
#endif
void Delay(unsigned int Xms) //@12.000MHz
{
unsigned char i, j;
while(Xms--)
{
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
把23年2月24日12:00:00 星期5 改成23年3月1日13:55:00 星期3。
DS1302可调时钟演示