DS1302
DS1302是一个低功耗、实时时钟(RTC)芯片
单字节写
1.发送数据前,需要将CE拉高,使能通信。需要注意的是,拉高CE时,需确保SCLK处于低电平
2.需要先发送命令字节,再发送数据字节,发送时都是低位优先
- 命令字节用于指定寄存器地址以及声明读/写操作,各位的含义如下
- 数据字节为发送的具体数据
3.每位数据都需要在一个时钟周期的上升沿发送
单字节读
1.接收数据前,需要将CE拉高,使能通信。需要注意的是,拉高CE时,需确保 SCLK 处于 低电平
2.需要先发送命令字节,再接收数据字节,发送和接收时都是低位优先
3.发送每位数据要在时钟信号的上升沿,读取每位数据要在时钟信号的下降沿
注意:第8个时钟周期,需要完成最后1位数据的发送和第1位数据的接收
寄存器介绍
注意:DS1302内部有一个写保护寄存器,在向DS1302写入数据前需要先关闭写保护,写完再开启写保护。
代码实现
Int_DS1302.h
#ifndef __INT_DS1302_H__
#define __INT_DS1302_H__
#include "Com_Util.h"
#include <STC89C5xRC.H>
typedef struct
{
u8 second;
u8 minute;
u8 hour;
u8 day;
u8 month;
u8 year;
u8 day_of_week;
} Struct_Date;
void Int_DS1302_Init(void);
void Int_DS1302_SetDate(Struct_Date *p_st_date);
void Int_DS1302_GetDate(Struct_Date *p_st_date);
#endif /* __INT_DS1302_H__ */
Int_DS1302.c
#include "Int_DS1302.h"
#include "intrins.h"
#define SCLK P36
#define IO P34
#define CE P35
#define SECOND 0x80
#define MINUTE 0x82
#define HOUR 0x84
#define DAY 0x86
#define MONTH 0x88
#define DAY_OF_WEEK 0x8A
#define YEAR 0x8C
#define WP 0x8E
void Int_DS1302_Init(void) {
SCLK = 0;
IO = 0;
CE = 0;
}
static void Int_DS1302_WriteByte(u8 byte) {
u8 i;
SCLK = 0;
for (i = 0; i < 8; i++) {
IO = byte & 0x01;
SCLK = 1;
SCLK = 0;
byte >>= 1;
}
}
static u8 Int_DS1302_ReadByte() {
u8 i, byte = 0;
SCLK = 0;
for (i = 0; i < 8; i++) {
byte >>= 1;
if (IO == 1) {
byte = byte | 0x80;
}
SCLK = 1;
SCLK = 0;
}
return byte;
}
static void Int_DS1302_SetRegister(u8 addr, u8 byte) {
CE = 1;
Int_DS1302_WriteByte(addr);
Int_DS1302_WriteByte(byte);
CE = 0;
}
static u8 Int_DS1302_GetRegister(u8 addr) {
u8 byte;
CE = 1;
Int_DS1302_WriteByte(addr | 1);
byte = Int_DS1302_ReadByte();
CE = 0;
SCLK = 1;
IO = 0;
IO = 1;
return byte;
}
void Int_DS1302_SetDate(Struct_Date* p_st_date) {
u8 temp;
// 关闭写保护
Int_DS1302_SetRegister(WP, 0x00);
// 设置秒
temp = (p_st_date->second % 10) | ((p_st_date->second / 10) << 4);
Int_DS1302_SetRegister(SECOND, temp);
// 设置分钟
temp = (p_st_date->minute % 10) | ((p_st_date->minute / 10) << 4);
Int_DS1302_SetRegister(MINUTE, temp);
// 设置小时
temp = (p_st_date->hour % 10) | ((p_st_date->hour / 10) << 4);
Int_DS1302_SetRegister(HOUR, temp);
// 设置日
temp = (p_st_date->day % 10) | ((p_st_date->day / 10) << 4);
Int_DS1302_SetRegister(DAY, temp);
// 设置月
temp = (p_st_date->month % 10) | ((p_st_date->month / 10) << 4);
Int_DS1302_SetRegister(MONTH, temp);
// 设置年
temp = (p_st_date->year % 10) | ((p_st_date->year / 10) << 4);
Int_DS1302_SetRegister(YEAR, temp);
// 设置星期
Int_DS1302_SetRegister(DAY_OF_WEEK, p_st_date->day_of_week % 10);
// 开启写保护
Int_DS1302_SetRegister(WP, 0x80);
}
void Int_DS1302_GetDate(Struct_Date* p_st_date) {
u8 temp;
// 获取秒
temp = Int_DS1302_GetRegister(SECOND);
p_st_date->second = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(MINUTE);
p_st_date->minute = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(HOUR);
p_st_date->hour = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(DAY);
p_st_date->day = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(MONTH);
p_st_date->month = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(YEAR);
p_st_date->year = (temp >> 4) * 10 + (temp & 0x0F);
temp = Int_DS1302_GetRegister(DAY_OF_WEEK);
p_st_date->day_of_week = (temp >> 4) * 10 + (temp & 0x0F);
}
main.c
#include "Int_DS1302.h"
#include "Int_Oled.h"
#include "STDIO.H"
code char* WEEK_NAME[] = {
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun" };
void main() {
u8 str[17];
Struct_Date st_date;
st_date.year = 23;
st_date.month = 12;
st_date.day = 29;
st_date.day_of_week = 5;
st_date.hour = 11;
st_date.minute = 32;
st_date.second = 00;
// 初始化时钟并设置时间
Int_DS1302_Init();
Int_Oled_Init();
Int_DS1302_SetDate(&st_date);
Int_Oled_Clear();
while (1) {
Int_DS1302_GetDate(&st_date);
sprintf(str, "20%02d/%02d/%02d %s",
(int)st_date.year,
(int)st_date.month,
(int)st_date.day,
WEEK_NAME[st_date.day_of_week - 1]);
Int_Oled_ShowStr(0, 0, str);
sprintf(str, "%02d:%02d:%02d",
(int)st_date.hour,
(int)st_date.minute,
(int)st_date.second);
Int_Oled_ShowStr(0, 1, str);
}
}
原理图
说明:
这里I/O口没有接上拉电阻,所以代码中需要注意以下几点