文章目录
一、DS1302介绍
引脚定义与应用电路
官方电路图
内部结构框图
内部与时钟相关的寄存器
地址/命令 字节作用
5、4、3、2、1用于确定要读写的地址
时序图
二、具体操作
1.实现写入、读出功能
按照上面的寄存器与时序图一一配置,具体实现如下:
#include <REGX52.H>
// 初始化命名,方便操作
sbit DS1302_CE = P3^5;
sbit DS1302_IO = P3^4;
sbit DS1302_SCLK = P3^6;
// 读取数据
void DS1302_Init(){
DS1302_CE = 0;
DS1302_IO = 0;
DS1302_SCLK = 0;
}
// 写入数据
void DS1302_Write(unsigned char SET,Data){
unsigned char i = 0;
DS1302_CE = 1;
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = SET & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = Data & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;
}
// 读取数据,比写入数据少一个脉冲
unsigned char DS1302_Read(unsigned char SET){
unsigned char i = 0;
unsigned char Data = 0x00; // 局部变量需自己赋初值
DS1302_CE = 1;
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = SET & (0x01<<i);
DS1302_SCLK = 0;
DS1302_SCLK = 1;
}
// 读取是下降沿读取
for(i=0;i<8;i++){
DS1302_SCLK = 1;
DS1302_SCLK = 0;
if(DS1302_IO){
Data |= (0x01<<i); // 得到I/O的值
}
}
DS1302_IO = 0; // 消影
DS1302_CE = 0;
return Data;
}
2.显示数据
通过LCD1602显示,以操作秒为例,具体如下:
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
unsigned char Data;
void main(){
LCD_Init();
DS1302_Init();
LCD_ShowString(1,1,"CLK");
DS1302_Write(0x8e,0x00); // 需解除写保护(WP)
DS1302_Write(0X80,0X03); // 写入数据
while(1){
Data = DS1302_Read(0X81);
LCD_ShowNum(2,1,Data/16*10+Data%16,3); // 转换BCD码
}
}
3.BCD码
注意
时钟内部是使用BCD码来存储的
BCD码
即把十六进制与二进制的转换规则移到十进制与二进制上
三、可操作时钟案例
时钟相关内容DS1302.c
#include <REGX52.H>
#include "DS1302.h"
// 宏定义寄存器相关地址,方便使用
#define DS1302_SEC 0X80
#define DS1302_MIN 0X82
#define DS1302_HOUR 0X84
#define DS1302_DATE 0X86
#define DS1302_MONTH 0X88
#define DS1302_DAY 0X8A
#define DS1302_YEAR 0X8C
#define DS1302_WP 0X8E
// 初始化命名,方便操作
sbit DS1302_CE = P3^5;
sbit DS1302_IO = P3^4;
sbit DS1302_SCLK = P3^6;
// 设置时间数组,存储要设置的时间(年月日时分秒星期)
char DS1302_Content[] = {
20,10,27,5,20,30,6
};
// 读取数据
void DS1302_Init(){
DS1302_CE = 0;
DS1302_IO = 0;
DS1302_SCLK = 0;
}
// 写入数据
void DS1302_Write(unsigned char SET,Data){
unsigned char i = 0;
DS1302_CE = 1;
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = SET & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = Data & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;
}
// 读取数据,比写入数据少一个脉冲
unsigned char DS1302_Read(unsigned char SET){
unsigned char i = 0;
unsigned char Data = 0x00;
SET |= 0x01; // 将最低位默认为1,即读取状态
DS1302_CE = 1;
for(i=0;i<8;i++){
// 位运算结果非0即1
DS1302_IO = SET & (0x01<<i);
DS1302_SCLK = 0;
DS1302_SCLK = 1;
}
for(i=0;i<8;i++){
DS1302_SCLK = 1;
DS1302_SCLK = 0;
if(DS1302_IO){
Data |= (0x01<<i); // 得到I/O的值
}
}
DS1302_IO = 0; // 消影
DS1302_CE = 0;
return Data;
}
// 设置时间,修改时间数组
void DS1302_SetTime(void){
DS1302_Write(DS1302_WP,0x00); // 关闭写保护
// 设置年(十进制转BCD码)
DS1302_Write(DS1302_YEAR,DS1302_Content[0]/10*16+DS1302_Content[0]%10);
// 设置月(十进制转BCD码)
DS1302_Write(DS1302_MONTH,DS1302_Content[1]/10*16+DS1302_Content[1]%10);
// 设置日(十进制转BCD码)
DS1302_Write(DS1302_DATE,DS1302_Content[2]/10*16+DS1302_Content[2]%10);
// 设置时(十进制转BCD码)
DS1302_Write(DS1302_HOUR,DS1302_Content[3]/10*16+DS1302_Content[3]%10);
// 设置分(十进制转BCD码)
DS1302_Write(DS1302_MIN,DS1302_Content[4]/10*16+DS1302_Content[4]%10);
// 设置秒(十进制转BCD码)
DS1302_Write(DS1302_SEC,DS1302_Content[5]/10*16+DS1302_Content[5]%10);
// 设置星期(十进制转BCD码)
DS1302_Write(DS1302_DAY,DS1302_Content[6]/10*16+DS1302_Content[6]%10);
DS1302_Write(DS1302_WP,0x00); // 关闭写保护
}
// 确认读取时间,更新时间数组
void DS1302_ReadTime(void){
unsigned char Temp; // 用于BCD码转十进制
Temp = DS1302_Read(DS1302_YEAR);
DS1302_Content[0] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_MONTH);
DS1302_Content[1] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_DATE);
DS1302_Content[2] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_HOUR);
DS1302_Content[3] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_MIN);
DS1302_Content[4] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_SEC);
DS1302_Content[5] = Temp/16*10+Temp%16;
Temp = DS1302_Read(DS1302_DAY);
DS1302_Content[6] = Temp/16*10+Temp%16;
}
主函数内容
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Time0Init.h"
unsigned char KeyNum,Mode; // 用Mode确定是设置状态还是非设置状态
unsigned char TimeSelectNum; // 用于选择位
unsigned char FlashFlag; // 闪烁标志
void Update_Show(void){
// 更新显示内容
LCD_ShowNum(1,1,DS1302_Content[0],2);
LCD_ShowNum(1,4,DS1302_Content[1],2);
LCD_ShowNum(1,7,DS1302_Content[2],2);
LCD_ShowNum(2,1,DS1302_Content[3],2);
LCD_ShowNum(2,4,DS1302_Content[4],2);
LCD_ShowNum(2,7,DS1302_Content[5],2);
}
// 设置完成
void Time_Success(void){
DS1302_ReadTime();
LCD_ShowNum(1,1,DS1302_Content[0],2);
LCD_ShowNum(1,4,DS1302_Content[1],2);
LCD_ShowNum(1,7,DS1302_Content[2],2);
LCD_ShowNum(2,1,DS1302_Content[3],2);
LCD_ShowNum(2,4,DS1302_Content[4],2);
LCD_ShowNum(2,7,DS1302_Content[5],2);
}
// 设置时间
void Time_Set(void){
// 用于判断要操作第几位
if(KeyNum == 2){
TimeSelectNum++;
TimeSelectNum%=6; // 大于5清0
}
// 用于加法操作
if(KeyNum == 3){
DS1302_Content[TimeSelectNum]++;
// 对年判断
if(DS1302_Content[0]>99){DS1302_Content[0]=0;}
// 对月判断
if(DS1302_Content[1]>12){DS1302_Content[1]=1;}
// 对日判断
// 31天
if( DS1302_Content[1] == 1 || DS1302_Content[1] == 3 || DS1302_Content[1] == 5 ||
DS1302_Content[1] == 7 || DS1302_Content[1] == 8 || DS1302_Content[1] == 10 ||
DS1302_Content[1] == 12){
if(DS1302_Content[2]>31){DS1302_Content[2]=1;}
}
// 30天
else if(DS1302_Content[1] == 4 ||DS1302_Content[1] == 6 ||
DS1302_Content[1] == 9 || DS1302_Content[1] == 11){
if(DS1302_Content[2]>30){DS1302_Content[2]=1;}
}
// 2月
else if(DS1302_Content[1] == 2){
// 闰年
if( DS1302_Content[0]%4==0 && DS1302_Content[0]%100!=0 ||
DS1302_Content[0]%400 == 0){
if(DS1302_Content[2]>29){DS1302_Content[2]=1;}
}
else{
if(DS1302_Content[2]>28){DS1302_Content[2]=1;}
}
}
// 对时判断
if(DS1302_Content[3]>23){DS1302_Content[3]=0;}
// 对分判断
if(DS1302_Content[4]>59){DS1302_Content[4]=0;}
// 对秒判断
if(DS1302_Content[5]>59){DS1302_Content[5]=0;}
// Update_Show();
}
// 用于减法操作
if(KeyNum == 4){
DS1302_Content[TimeSelectNum]--;
// 对年判断
if(DS1302_Content[0]<0){DS1302_Content[0]=99;}
// 对月判断
if(DS1302_Content[1]<1){
DS1302_Content[1]=12;
}
// 对日判断
// 31天
if( DS1302_Content[1] == 1 || DS1302_Content[1] == 3 || DS1302_Content[1] == 5 ||
DS1302_Content[1] == 7 || DS1302_Content[1] == 8 || DS1302_Content[1] == 10 ||
DS1302_Content[1] == 12){
if(DS1302_Content[2]<1){DS1302_Content[2]=31;}
if(DS1302_Content[2]>31){DS1302_Content[2]=1;}
}
// 30天
else if(DS1302_Content[1] == 4 ||DS1302_Content[1] == 6 ||
DS1302_Content[1] == 9 || DS1302_Content[1] == 11){
if(DS1302_Content[2]<1){
DS1302_Content[2]=30;
}
if(DS1302_Content[2]>30){DS1302_Content[2]=1;}
}
// 2月
else if(DS1302_Content[1] == 2){
// 闰年
if( DS1302_Content[0]%4==0 && DS1302_Content[0]%100!=0 ||
DS1302_Content[0]%400 == 0){
if(DS1302_Content[2]<1){DS1302_Content[2]=29;}
if(DS1302_Content[2]>29){DS1302_Content[2]=1;}
}
else{
if(DS1302_Content[2]<1){DS1302_Content[2]=28;}
if(DS1302_Content[2]>28){DS1302_Content[2]=1;}
}
}
// 对时判断
if(DS1302_Content[3]<0){DS1302_Content[3]=23;}
// 对分判断
if(DS1302_Content[4]<0){DS1302_Content[4]=59;}
// 对秒判断
if(DS1302_Content[5]<0){DS1302_Content[5]=59;}
// Update_Show();
}
// 闪烁实现
if(TimeSelectNum == 0 && FlashFlag == 1){
LCD_ShowString(1,1," ");
}else{
LCD_ShowNum(1,1,DS1302_Content[0],2);
}
if(TimeSelectNum == 1 && FlashFlag == 1){
LCD_ShowString(1,4," ");
}else{
LCD_ShowNum(1,4,DS1302_Content[1],2);
}
if(TimeSelectNum == 2 && FlashFlag == 1){
LCD_ShowString(1,7," ");
}else{
LCD_ShowNum(1,7,DS1302_Content[2],2);
}
if(TimeSelectNum == 3 && FlashFlag == 1){
LCD_ShowString(2,1," ");
}else{
LCD_ShowNum(2,1,DS1302_Content[3],2);
}
if(TimeSelectNum == 4 && FlashFlag == 1){
LCD_ShowString(2,4," ");
}else{
LCD_ShowNum(2,4,DS1302_Content[4],2);
}
if(TimeSelectNum == 5 && FlashFlag == 1){
LCD_ShowString(2,7," ");
}else{
LCD_ShowNum(2,7,DS1302_Content[5],2);
}
// LCD_ShowNum(1,10,TimeSelectNum,1);
}
void main(){
LCD_Init();
DS1302_Init();
Time0_Init();
LCD_ShowString(1,1," - -");
LCD_ShowString(2,1," : :");
DS1302_SetTime();
while(1){
KeyNum = Key();
if(KeyNum == 1){
if(Mode ==0 ){Mode = 1;}
else if(Mode ==1 ){
Mode = 0;
DS1302_SetTime(); // 模式转换回来时更新时间数组
}
}
switch(Mode){
case 0: Time_Success();break;
case 1: Time_Set();break;
}
}
}
// 中断函数
void Timer0_Routine() interrupt 1
{
static unsigned int count;
count++;
// 中断后会归零,需重新赋值
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
// 每500ms改变闪烁状态
if(count>=500){
count = 0;
FlashFlag = !FlashFlag;
}
}
注意内容
- 如果keil没有破解,会有代码限制问题*** FATAL ERROR L250: CODE SIZE LIMIT IN RESTRICTED VERSION EXCEEDED,破解后重新创建项目即可。
- 在使用LCD1602时,要解除之前LED点阵屏OE引脚的连接(即连接VCC)
- 关于 keil编译时出现大量莫名其妙的 undefined identifier,这个错误是因为在C语言的旧版本中(尤其是在Keil编译器中编译8051微控制器的代码时),变量声明必须在函数或任何作用域的开头,而不能在语句之后。