实时时钟芯片HYM1381的使用(51单片机)
HYM1381是一款低功耗串行实时时钟芯片
两种工作方式:12小时格式/24小时格式
3根通信线:RST(复位)、SCLK(串行时钟)、IO(数据线)
数据存储格式:BCD码(4位二进制数表示一位十进制数)
1381.h:
#define RST P15 //推挽
#define SCLK P03 //推挽
#define DSIO P04 //初始化为推挽输出,发送数据时为推挽,接收数据时为悬空输入/上拉输入
#define Year 6
#define Month 4
#define Date 3
#define Hour 2
#define Minute 1
#define Seconds 0
void HYM1381Init(); //初始化函数
void DataPros(); //时间显示函数
void HYM1381_TimeSet(u8 address); //时间设定函数
//void HYM1381ReadTime();
extern uchar Time_Set[7];
extern uchar Read_TIME[7];
1381.c:
#include "HYM1381.h"
#include "sysClk_Cfg.h"
#include <INTRINS.H> //为了使用nop空指令
// HYM1381存储格式使用BCD码,使用过程中涉及十进制和BCD码之间的转换
u8 code Read_RTC_Addr[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; //读取地址
u8 code Write_RTC_Addr[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; //写入地址
// 时钟初始化2025-3-4星期二9点51分30秒,存储顺序按照秒分时日月周年,
uchar xdata TIME[7]={30,51,9,4,3,2,25}; //时间初始值---十进制
uchar Read_TIME[7]; //用来储存读到的时间信息
uchar Time_Set[7]; //用来设置时间
底层驱动,向1381写入数据和从1381读取数据:
// 向HYM1381发送命令,向地址addr写入数据dat
void HYM1381Write(uchar addr, uchar dat)
{
uchar i;
SCLK = 0; // 拉低SCLK,SCLK为低时才可拉高RST
_nop_ ();
RST = 1; // RST拉高,整个读写期间保持RST高电平
_nop_ ();
P0MDH = 0xaa; //输出模式
for(i = 0;i < 8;i++) // 从低位开始传送八位地址addr
{
DSIO = addr&0x01;
addr>>=1;
SCLK=1; //上升沿向HYM1381写数据
_nop_ ();
SCLK=0; //SCLK拉低为下一位传送做准备
_nop_ ();
}
for(i = 0;i < 8;i++) // 从低位开始传送八位数据dat
{
DSIO = dat&0x01;
dat>>=1;
SCLK = 1; // 上升沿向HYM1381写数据
_nop_ ();
SCLK = 0; // SCLK拉低为下一位传送做准备
_nop_ ();
}
// 数据传送结束,将RST拉低,为下一次传送做准备
RST = 0;
_nop_ ();
}
// 从HYM1381中读取地址addr的数据dat
uchar HYM1381Read(uchar addr)
{
uchar i;
uchar dat = 0, bi;
SCLK = 0; // 拉低SCLK,SCLK为低时才可拉高RST
_nop_ ();
RST = 1; // RST拉高,整个读写期间保持RST高电平
_nop_ ();
P0MDH = 0xaa;
for(i=0;i<8;i++) // 从低位开始传送八位地址addr
{
DSIO = addr&0x01;
addr>>=1;
SCLK = 1; // 上升沿向HYM1381写数据
_nop_ ();
SCLK = 0; // SCLK拉低为下一位传送做准备
_nop_ ();
}
_nop_ ();
SCLK = 1;
P0MDH = 0xa9;
for(i = 0;i < 8;i++) // 从低位开始读取八位数据dat
{
bi = DSIO;
dat = (dat>>1)|(bi<<7);
// SCLK = 1; // 上升沿向HYM1381写数据
SCLK = 0;
_nop_ ();
// SCLK = 0; // SCLK拉低为下一位传送做准备
SCLK = 1 ;
_nop_ ();
}
SCLK = 1;
_nop_ ();
// 数据传送结束,将RST拉低,为下一次传送做准备
RST = 0;
_nop_ ();
_nop_ ();
// HYM1381复位稳定时间
DSIO = 0;
// _nop_ ();
// DSIO = 1;
// _nop_ ();
return dat;
}
HYM1381的初始化:
// HYM1381初始化
// 1. 在对HYM1381操作之前需要关闭写保护,0x8e写0x00
// 2. 写入设置的时间
// 3. 打开写保护功能
void HYM1381Init()
{
uchar i;
HYM1381Write(0x8E, 0x00); // 关闭写保护
for(i = 0;i < sizeof(Write_RTC_Addr);i++) // 设置时间
{
HYM1381Write(Write_RTC_Addr[i],(TIME[i]/10*16+TIME[i]%10)); //将十进制的时间数据转换为BCD格式写入HYM1381
}
HYM1381Write(0x8E, 0x80); // 打开写保护
}
从HYM1381读取时间:
// 从HYM1381中读取时间信息
void HYM1381ReadTime()
{
uchar i;
for(i = 0;i < sizeof(Read_RTC_Addr);i++)
{
Read_TIME[i] = HYM1381Read(Read_RTC_Addr[i]);
}
}
读取到时间之后,就要对时间进行处理和显示了:
以年为例,读到之后转换为十进制,使用段码屏进行显示
void DataPros()
{
u8 i;
HYM1381ReadTime(); // 首先读取时间信息
for(i = 0;i < 7;i++) //将读到的BCD格式转换为十进制
{
Read_TIME[i] = Read_TIME[i]/16*10+Read_TIME[i]%16;
}
// Read_TIME[6] = Read_TIME[6]/16*10+Read_TIME[6]%16; //年--读到的BCD格式转换为十进制
Disp_Buff[15] = NumLib6_16[Read_TIME[6]%10]; //年--个位 (十进制除以10取余数得到个位)
Disp_Buff[15] = Disp_Buff[15]|0x80; //年和月之间的“-”
Disp_Buff[16] = NumLib6_16[Read_TIME[6]/10]; //年--十位 (十进制除以10取正数得到十位)
Disp_Buff[16] = Disp_Buff[16]|0x80; //月和日之间的“-”
}
接下来是用户交互的内容,可以通过按键自己调整时间:
//时间设定函数
void HYM1381_TimeSet(u8 address)
{
HYM1381Write(0x8E, 0x00); // 关闭写保护
switch(address)
{
case Year:
{
HYM1381Write(0x8c,(Time_Set[6]/10*16+Time_Set[6]%10)); //将十进制的时间数据转换为BCD格式写入HYM1381
}break;
case Month:
{
HYM1381Write(0x88,(Time_Set[4]/10*16+Time_Set[4]%10));
}break;
case Date:
{
HYM1381Write(0x86,(Time_Set[3]/10*16+Time_Set[3]%10));
}break;
case Hour:
{
HYM1381Write(0x84,(Time_Set[2]/10*16+Time_Set[2]%10));
}break;
case Minute:
{
HYM1381Write(0x82,(Time_Set[1]/10*16+Time_Set[1]%10));
}break;
case Seconds:
{
HYM1381Write(0x80,(Time_Set[0]/10*16+Time_Set[0]%10));
}break;
}
HYM1381Write(0x8E, 0x80); // 打开写保护
}
因为HYM1381的存储顺序,对数组来说,从左往右依次是:秒、分、时、日、月、周、年
以年为例,数据存在Read_TIME[6],那么
Time_Set[6] = Read_TIME[6] - 1; //每按一次置零键,年份减1
if(Time_Set[6] <= 0) Time_Set[6] = 99; //年份小于0之后回到99
HYM1381_TimeSet(Year);
特别要说明日期的调整,要根据月份确定,并且2月份要区分平年还是闰年
switch(Read_TIME[Month])
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
{
Time_Set[Date] = Read_TIME[Date] - 1;
if(Time_Set[Date] < 1) Time_Set[Date] = 31;
}
break; //一三五七八十腊,31天
case 2: //二月份判断闰年平年
{
if((Time_Set[Year] % 4 == 0 && Time_Set[Year] % 100 != 0) || Time_Set[Year] % 400 == 0)//如果是闰年
{
Time_Set[Date] = Read_TIME[Date] - 1;
if(Time_Set[Date] < 1) Time_Set[Date] = 29;
}
else //平年
{
Time_Set[Date] = Read_TIME[Date] - 1;
if(Time_Set[Date] < 1) Time_Set[Date] = 28;
}
}
break;
default: //其他月份30天
{
Time_Set[Date] = Read_TIME[Date] - 1;
if(Time_Set[Date] < 1) Time_Set[Date] = 30;
}
break;
}
HYM1381_TimeSet(Date);