实物图
原理图
效果图
程序烧录:
DS3231时序图
【图】写数据
【图】读数据
【图】读写数据
/***************************************************************************** *文件名称:main.c *版 本:Keil uVision4 *控 制 器:STC89C52RC/12M 功能:显示时间到串口 *说 明: 1,DS3231实时时钟模块测试程序 2,1T的单片机用不了 3,晶振12M 4,串口波特率2400 编译结果: Rebuild target 'Demo_DS3231' compiling main.c... linking... *** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS SEGMENT: ?PR?_UART_SENDBYTE?MAIN Program Size: data=61.2 xdata=0 code=2059 creating hex file from "Demo_DS3231"... "Demo_DS3231" - 0 Error(s), 1 Warning(s). *****************************************************************************/ #include <reg52.h> #include <stdio.h> #include "intrins.h" //#define Effective 0x01 //#define Ineffectiveness 0x00 #define DS3231WriteAddress 0xd0 #define DS3231ReadAddress 0xd1 //#define DS3231_SecondRegister 0x00 #define DS3231_TimeFirstRegister 0x00 #define DS3231_MinuteRegister 0x01 #define DS3231_HourRegister 0x02 #define DS3231_WeekRegister 0x03 #define DS3231_DayRegister 0x04 #define DS3231_MonthRegister 0x05 #define DS3231_YearRegister 0x06 #define DS3231_InterruptRegister 0x0e #define DS3231_AlarmRegister 0x0f #define DS3231_ResetSCL() DS3231_scl = 0 #define DS3231_SetSCL() DS3231_scl = 1 #define DS3231_ResetSDA() DS3231_sda = 0 #define DS3231_SetSDA() DS3231_sda = 1 #define DS3231_ReadPinSDA() DS3231_sda #define Time0_TH0 0xec //定义计数器0计数寄存器装载的高8位值 #define Time0_TL0 0x78 //定义计数器0计数寄存器装载的低8位值 //--------------------------秒-分-时-星期-日-月-年 unsigned int SetTime[] = {12,12,12,1,1,1,15}; unsigned int CurrentT[7]; bit Flag_Collect = 0; //定义采集扫描标志变量 unsigned char SweepInterval_Collect; //定义采集扫描时间累加变量 sbit DS3231_scl = P2^1; //DS3231 clock sbit DS3231_sda = P2^0; //DS3231 data void DS3231_Delay(void) { //DS3231通信速率延时,延时5微秒 12T单片机@12M unsigned char Number = 8; while (Number--) { _nop_(); _nop_(); } } void DS3231_DelayForWrite(void){ //DS3231写字节延时,延时5毫秒 12T单片机@12M unsigned int Number = 2500; while (Number--){ _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } } void DS3231_Start(void) { //模拟DS3231通信开始信号,SCL=1期间,在SDA上产生一个下降沿 DS3231_SetSDA(); DS3231_SetSCL();DS3231_Delay(); DS3231_ResetSDA();DS3231_Delay(); } void DS3231_Stop(void) //模拟DS3231通信结束信号,SCL=1期间,在SDA上产生一个上升沿 { DS3231_ResetSDA();DS3231_Delay(); DS3231_ResetSDA();DS3231_Delay(); DS3231_SetSCL();DS3231_Delay(); DS3231_SetSCL();DS3231_Delay(); DS3231_SetSDA();DS3231_Delay(); } unsigned char DS3231_WriteByte(unsigned char SendByte){ //向DS3231设备写一字节数据及8为二进制数据,高位在前 unsigned char i=8; DS3231_ResetSCL(); for(i=0; i<8; i++) { if(SendByte&0x80) {DS3231_SetSDA();} else {DS3231_ResetSDA();} DS3231_ResetSCL();DS3231_Delay(); DS3231_SetSCL(); DS3231_Delay(); SendByte=SendByte<<1; DS3231_ResetSCL();DS3231_Delay(); } DS3231_SetSDA();DS3231_Delay(); DS3231_ResetSCL();DS3231_Delay(); DS3231_SetSCL();DS3231_Delay(); i = DS3231_ReadPinSDA();DS3231_Delay(); DS3231_ResetSCL();DS3231_Delay(); return i; } unsigned char DS3231_ReceiveByte(unsigned char Response) { //接收DS3231发送的数据 unsigned char i=8; unsigned char ReceiveByte=0; DS3231_SetSDA();DS3231_Delay(); DS3231_ResetSCL();DS3231_Delay(); for(i=0; i<8; i++){ DS3231_SetSCL();DS3231_Delay(); ReceiveByte = ReceiveByte << 1; ReceiveByte=ReceiveByte|(unsigned char)DS3231_ReadPinSDA(); DS3231_ResetSCL();DS3231_Delay(); } if(Response) DS3231_SetSDA(); else DS3231_ResetSDA(); DS3231_Delay(); DS3231_SetSCL();DS3231_Delay(); DS3231_ResetSCL();DS3231_Delay(); DS3231_SetSDA();DS3231_Delay(); return ReceiveByte; } unsigned char DS3231_ReadOneByteFromE2PROM(unsigned char DataAddress) {//读指定地址E2PROM中的数据 unsigned char ReadData; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DataAddress); //写入状态寄存器地址 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231ReadAddress); //写入芯片IIC读地址 ReadData = DS3231_ReceiveByte(0x01); DS3231_Stop(); //DS3231芯片IIC通信停止信号 return ReadData; } void DS3231_Initialization(){ //初始化时钟芯片DS3231,先选择要写入的寄存器,在写入需要设置的指令 DS3231_Start(); //IIC通信起始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_HourRegister); //选择时寄存器为写入地址 DS3231_WriteByte(0x00); //写入指令,时钟范围为0-23,即24小时制式 DS3231_Stop(); DS3231_Start(); //IIC通信起始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_InterruptRegister);//选择中断寄存器为写入地址 DS3231_WriteByte(0x04); //中断寄存器初始化,关闭方波信号,关闭闹钟中断 DS3231_WriteByte(0x00); //状态寄存器初始化,失效32KHz信号输出,不匹配闹钟 DS3231_Stop(); } void DS3231_SetTime(unsigned int *Pointer){ //向DS3231写入设置时间信息 unsigned char Number = 0x00; unsigned char TransitionData = 0x00; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_TimeFirstRegister); //写入时间寄存器首地址 for(Number=0; Number<7; Number++) { TransitionData = *Pointer++; DS3231_WriteByte((TransitionData/10)*16+TransitionData%10); //向DS3231写入设置时间信息 } DS3231_Stop(); //DS3231芯片IIC通信停止信号 DS3231_DelayForWrite(); } void DS3231_ReadTime(unsigned int *Pointer){ //从DS3231中读出当前时间 unsigned char Number = 7; unsigned char Time = 0x00; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_AlarmRegister); //写入闹钟寄存器首地址 DS3231_WriteByte(0x00); //关闭闹钟中断标志位 DS3231_Stop(); //DS3231芯片IIC通信停止信号 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_TimeFirstRegister); //写入时间寄存器首地址 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231ReadAddress); //写入芯片IIC读地址 for(Number=0; Number<6; Number++) { Time = DS3231_ReceiveByte(0x00); *Pointer++ = (Time/16*10+Time%16); } Time = DS3231_ReceiveByte(0x01); *Pointer++ = (Time/16*10+Time%16); DS3231_Stop(); //DS3231芯片IIC通信停止信号 } void Time0_Initialization(void){//5ms@12M TMOD=0x21; //T0,工作方式1 TH0=Time0_TH0; //装载定时器0寄存器高8位值 TL0=Time0_TL0; //装载定时器0寄存器低8位值 TR0=1; //开启T0定时器 ET0=1; //允许T0定时器中断 EA=1; //开启总中断允许 } void UART_Initialization(void){ //2400@12M SCON = 0x50; //选择串口工作方式1,打开接收允许 TMOD = 0x21; //定时器1工作在方式2,定时器0工作在方式1 TH1 = 0xe6; //实现波特率2400(系统时钟12MHZ) TCON = 0x40; //定时器1开始计数 PCON = 0x80; //波特率倍频 RI = 0x00; //清接收标志 TI = 0x01; //清发送标志 TR1 = 0x01; //启动定时器T1 } void UART_SendByte(unsigned char SendByte){ SBUF = SendByte; //输出字符 while(!TI); //判断是否发完 TI=0; //清TI } void main(){ //定时采集DS3231时钟芯片时间信息,通过RS232串口打印 unsigned char State_Timing; unsigned int Value_Contrast; UART_Initialization(); State_Timing = DS3231_ReadOneByteFromE2PROM(DS3231_AlarmRegister);//判断是否校时 if(State_Timing & 0x80){ //判断芯片是否有掉电 DS3231_Initialization(); //初始化时间时钟芯片DS3231 DS3231_SetTime(SetTime); } DS3231_ReadTime(CurrentT); //掉电检测操作,电源短路或者供电系统电量彻底消耗掉才有效 每次上电读取一次当前时间信息 if(CurrentT[6] == 0x00){ //年份信息为0x00,芯片时间需要初始化 DS3231_Initialization(); //初始化时间时钟芯片DS3231 DS3231_SetTime(SetTime); } Time0_Initialization(); //定时器初始化 while(1){ while(Flag_Collect){ // 中断控制 EA = 0; //关闭总中断 DS3231_ReadTime(CurrentT); //读取当前时间信息 if(Value_Contrast != CurrentT[0]){ Value_Contrast = CurrentT[0]; printf("Week=%d\r\n",CurrentT[3]); printf("Data=20%d/%d/%d\r\n",CurrentT[6],CurrentT[5],CurrentT[4]); printf("Time=%d:%d:%d\r\n",CurrentT[2],CurrentT[1],CurrentT[0]); printf("\r\n"); } Flag_Collect = 0; EA = 1; } } } void Time0_Handle(void) interrupt 1 using 3 { TR0 = 0; //关闭T0定时器 TH0 = Time0_TH0; //重新装载定时器0寄存器高8位值 TL0 = Time0_TL0; //重新装载定时器0寄存器低8位值 TR0 = 1; //开启T0定时器 if(SweepInterval_Collect >= 0x3c){ //大约0.3s采集一次时钟信息 Flag_Collect = 1; //采集扫描标志变量置位 SweepInterval_Collect = 0; //采集扫描时间变量清零 } else {SweepInterval_Collect++;} //采集扫描时间累加 }
Ver2.0更新
/***************************************************************************** *文件名称:main.c *版 本:Keil uVision4 *单片机:STC89C52RC/12M 功能:显示时间到串口 版本:2.0 更新说明: 简化代码 *说 明: 1,DS3231实时时钟模块测试程序 2,1T的单片机用不了 3,晶振12M 4,串口波特率2400 编译结果: Rebuild target 'Demo_DS3231' compiling main.c... compiling DS3231.c... linking... Program Size: data=61.2 xdata=0 code=2039 creating hex file from "Demo_DS3231"... "Demo_DS3231" - 0 Error(s), 0 Warning(s). 关于0F状态寄存器 OSF: 振荡器停止标志。该位为逻辑1表示振荡器现在停止工作,或者曾经停止工作,可用于判定计时数据的有效性。 无论何时振荡器停止工作,该位均置为逻辑1。该位保持为逻辑1,直到写入逻辑0清除。以下情况能够造成OSF置位: 1) 初次上电。 2) VCC与VBAT上的电压都不足以支持振荡器工作。 3) 在电池备份模式下,EOSC位关闭。 4) 影响振荡器的外部因素(即噪声、泄漏等)。 BIT 1 A2F:闹钟2标志。闹钟2标志位为逻辑1时表示时间与闹钟2寄存器匹配。如果A2IE位为逻辑1,并且INTCN位设定为逻辑1, 则触发INT/SQW引脚。写入逻辑0时A2F位清零。该位仅能写入逻辑0,试图写入逻辑1的操作不改变原逻辑值。 BIT 0 A1F:闹钟1标志。闹钟1标志位为逻辑1时表示时间与闹钟1寄存器匹配。如果A1IE位为逻辑1,并且INTCN位设定为逻辑1, 则触发INT/SQW引脚。写入逻辑0时A1F位清零。该位仅能写入逻辑0,试图写入逻辑1的操作不改变原逻辑值。 *****************************************************************************/ #include <reg52.h> #include <stdio.h> #include "intrins.h" #define DS3231WriteAddress 0xd0 #define DS3231ReadAddress 0xd1 #define DS3231_Second 0x00 #define DS3231_TimeFirst 0x00 #define DS3231_Minute 0x01 #define DS3231_Hour 0x02 #define DS3231_Week 0x03 #define DS3231_Day 0x04 #define DS3231_Month 0x05 #define DS3231_Year 0x06 #define DS3231_Interrupt 0x0e #define DS3231_Status 0x0f //--------------------------秒-分-时-星期-日-月-年 unsigned int SetTime[] = {12,12,12,1,1,1,15}; unsigned int CurrentT[7]; bit Flag_Collect = 0; //定义采集扫描标志变量 unsigned char SweepInterval_Collect; //定义采集扫描时间累加变量 sbit DS3231_scl = P2^1; //DS3231 clock sbit DS3231_sda = P2^0; //DS3231 data void DS3231_Delay(void) { //DS3231通信速率延时,延时5微秒 12T单片机@12M unsigned char Number = 8; while (Number--){ _nop_(); _nop_(); } } void DS3231_DelayForWrite(void){ //DS3231写字节延时,延时5毫秒 12T单片机@12M unsigned int Number = 2500; while (Number--){ _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } } void DS3231_Start(void) { //模拟DS3231通信开始信号,SCL=1期间,在SDA上产生一个下降沿 DS3231_sda = 1; DS3231_scl = 1;DS3231_Delay(); DS3231_sda = 0;DS3231_Delay(); } void DS3231_Stop(void) {//模拟DS3231通信结束信号,SCL=1期间,在SDA上产生一个上升沿 DS3231_sda = 0;DS3231_Delay(); DS3231_scl = 1;DS3231_Delay(); DS3231_sda = 1;DS3231_Delay(); } unsigned char DS3231_WriteByte(unsigned char SendByte){ //向DS3231设备写一字节数据及8为二进制数据,高位在前 unsigned char i=8; DS3231_scl = 0; for(i=0; i<8; i++) { if(SendByte&0x80){DS3231_sda = 1;} else {DS3231_sda = 0;} DS3231_scl = 0;DS3231_Delay(); DS3231_scl = 1; DS3231_Delay(); SendByte=SendByte<<1; DS3231_scl = 0;DS3231_Delay(); } DS3231_sda = 1;DS3231_Delay(); DS3231_scl = 0;DS3231_Delay(); DS3231_scl = 1;DS3231_Delay(); i = DS3231_sda;DS3231_Delay(); DS3231_scl = 0;DS3231_Delay(); return i; } unsigned char DS3231_ReceiveByte(unsigned char Response) { //接收DS3231发送的数据 unsigned char i=8; unsigned char ReceiveByte=0; DS3231_sda = 1;DS3231_Delay(); DS3231_scl = 0;DS3231_Delay(); for(i=0; i<8; i++){ DS3231_scl = 1;DS3231_Delay(); ReceiveByte = ReceiveByte << 1; ReceiveByte=ReceiveByte|(unsigned char)DS3231_sda; DS3231_scl = 0;DS3231_Delay(); } if(Response) DS3231_sda = 1; else DS3231_sda = 0; DS3231_Delay(); DS3231_scl = 1;DS3231_Delay(); DS3231_scl = 0;DS3231_Delay(); DS3231_sda = 1;DS3231_Delay(); return ReceiveByte; } unsigned char DS3231_ReadOneByteFromE2PROM(unsigned char DataAddress) {//读指定地址E2PROM中的数据 unsigned char ReadData; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DataAddress); //写入状态寄存器地址 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231ReadAddress); //写入芯片IIC读地址 ReadData = DS3231_ReceiveByte(0x01); DS3231_Stop(); //DS3231芯片IIC通信停止信号 return ReadData; } void DS3231_Initialization(){ //初始化时钟芯片DS3231,先选择要写入的寄存器,在写入需要设置的指令 DS3231_Start(); //IIC通信起始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_Hour); //选择时寄存器为写入地址 DS3231_WriteByte(0x00); //写入指令,时钟范围为0-23,即24小时制式 DS3231_Stop(); DS3231_Start(); //IIC通信起始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_Interrupt); //选择中断寄存器为写入地址 DS3231_WriteByte(0x04); //中断寄存器初始化,关闭方波信号,关闭闹钟中断 DS3231_WriteByte(0x00); //状态寄存器初始化,失效32KHz信号输出,不匹配闹钟 DS3231_Stop(); } void DS3231_SetTime(unsigned int *Pointer){ //向DS3231写入设置时间信息 unsigned char Number = 0x00; unsigned char TransitionData = 0x00; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_TimeFirst); //写入时间寄存器首地址 for(Number=0; Number<7; Number++) { TransitionData = *Pointer++; DS3231_WriteByte((TransitionData/10)*16+TransitionData%10); //向DS3231写入设置时间信息 } DS3231_Stop(); //DS3231芯片IIC通信停止信号 DS3231_DelayForWrite(); } void DS3231_ReadTime(unsigned int *Pointer){ //从DS3231中读出当前时间 unsigned char Number = 7; unsigned char Time = 0x00; DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_Status); //写入状态寄存器首地址 DS3231_WriteByte(0x00); //关闭闹钟中断标志位 DS3231_Stop(); //DS3231芯片IIC通信停止信号 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231WriteAddress); //写入芯片IIC写地址 DS3231_WriteByte(DS3231_TimeFirst); //写入时间寄存器首地址 DS3231_Start(); //DS3231芯片IIC通信开始信号 DS3231_WriteByte(DS3231ReadAddress); //写入芯片IIC读地址 for(Number=0; Number<6; Number++) { Time = DS3231_ReceiveByte(0x00); *Pointer++ = (Time/16*10+Time%16); } Time = DS3231_ReceiveByte(0x01); *Pointer++ = (Time/16*10+Time%16); DS3231_Stop(); //DS3231芯片IIC通信停止信号 } void Time0_Initialization(void){ //5毫秒@12.000MHz TMOD=0x21; //T0,工作方式1 TH0=0xec; //装载定时器0寄存器高8位值 TL0=0x78; //装载定时器0寄存器低8位值 TR0=1; //开启T0定时器 ET0=1; //允许T0定时器中断 EA=1; //开启总中断允许 } void UART_Initialization(void){ //2400@12M SCON = 0x50; //选择串口工作方式1,打开接收允许 TMOD = 0x21; //定时器1工作在方式2,定时器0工作在方式1 TH1 = 0xe6; //实现波特率2400(系统时钟12MHZ) TCON = 0x40; //定时器1开始计数 PCON = 0x80; //波特率倍频 RI = 0x00; //清接收标志 TI = 0x01; //清发送标志 TR1 = 0x01; //启动定时器T1 } void main(){ //定时采集DS3231时钟芯片时间信息,通过RS232串口打印 unsigned char State_Timing; unsigned int Value_Contrast; UART_Initialization(); State_Timing = DS3231_ReadOneByteFromE2PROM(DS3231_Status);//判断是否振荡器停止工作 if(State_Timing & 0x80){ //无论何时振荡器停止工作,第七位均置为逻辑1。 DS3231_Initialization(); //初始化时间时钟芯片DS3231 DS3231_SetTime(SetTime); } DS3231_ReadTime(CurrentT); //掉电检测操作,电源短路或者供电系统电量彻底消耗掉才有效 每次上电读取一次当前时间信息 if(CurrentT[6] == 0x00){ //年份信息为0x00,芯片时间需要初始化 DS3231_Initialization(); //初始化时间时钟芯片DS3231 DS3231_SetTime(SetTime); } Time0_Initialization(); //定时器初始化 while(1){ while(Flag_Collect){ // 中断控制 EA = 0; //关闭总中断 DS3231_ReadTime(CurrentT); //读取当前时间信息 if(Value_Contrast != CurrentT[0]){ Value_Contrast = CurrentT[0]; printf("Week=%d\r\n",CurrentT[3]); printf("Data=20%d/%d/%d\r\n",CurrentT[6],CurrentT[5],CurrentT[4]); printf("Time=%d:%d:%d\r\n",CurrentT[2],CurrentT[1],CurrentT[0]); printf("\r\n"); } Flag_Collect = 0; EA = 1; } } } void Time0_Handle(void) interrupt 1 using 3 { //5毫秒@12.000MHz TR0 = 0; //关闭T0定时器 TH0 = 0xec; //重新装载定时器0寄存器高8位值 TL0 = 0x78; //重新装载定时器0寄存器低8位值 TR0 = 1; //开启T0定时器 SweepInterval_Collect++; if(SweepInterval_Collect >= 0x3c){ //大约0.3s采集一次时钟信息 Flag_Collect = 1; //采集扫描标志变量置位 SweepInterval_Collect = 0; //采集扫描时间变量清零 } }