/******************************************************************************** STM32F103C8T6模块 + DS3231 + 24C32)模块 + TM1637数码管模块 + OLED显示模块 《实时时钟 + 温度测量并保存,实时OLED显示温度曲线,数码管实际显示时钟》 的,实验程序…… 2019.2.3完成 by bg4uvr~ 2020.2.8: 1、加入电源管理,空闲时进入掉电模式,调试完成。 ********************************************************************************/ #include "ds3231.h" #include "myiic.h" #include "stdlib.h" //用于malloc #include "string.h" //用于memset //BCD转HEX inline uint8_t bcd2hex(uint8_t bcd_data) { return ((bcd_data/16)*10+bcd_data%16); } //HEX转BCD inline uint8_t hex2bcd(uint8_t hex_data) { return ((hex_data/10)*0x10+hex_data%10); } //计算是否是闰年(输入数值0-199,代表2000-2199年) bool isLeap(uint8_t year) { unsigned int y=year+2000; if((y%4==0 && y%100!=0) || y%400==0) return TRUE; else return FALSE; } //计算某年的某月有多少天(年份输入数值0-199,代表2000-2199年) uint8_t max_date(uint8_t year,uint8_t month) { const uint8_t month_tab[]={31,28,31,30,31,30,31,31,30,31,30,31}; return isLeap(year)&&month==2?month_tab[month-1]+1:month_tab[month-1]; } //DS3231读取函数 void DS3231_Read(uint8_t Reg_add, uint8_t byte_cnt, uint8_t *buf) { IIC_Start(); //发送I2C START信号 IIC_Send_Byte(DS3231_WRITE_ADD); //发送DS3231硬件写地址 IIC_Wait_Ack(); //等待ACK回应 IIC_Send_Byte(Reg_add); //发送寄存器地址 IIC_Wait_Ack(); //等待ACK回应 IIC_Start(); //发送I2C START信号 IIC_Send_Byte(DS3231_READ_ADD); //发送DS3231硬件读地址 IIC_Wait_Ack(); //等待ACK回应 while(--byte_cnt) //如果不是最后1字节数据 { *(buf++)=IIC_Read_Byte(1); //读取并发送ACK } *buf=IIC_Read_Byte(0); //读取并发送NACK IIC_Stop(); //发送I2C STOP信号 } //DS3231写入函数 void DS3231_Write(uint8_t Reg_add, uint8_t byte_cnt, uint8_t *buf) { IIC_Start(); //发送I2C START信号 IIC_Send_Byte(DS3231_WRITE_ADD); //发送DS3231硬件写地址 IIC_Wait_Ack(); //等待ACK回应 IIC_Send_Byte(Reg_add); //发送寄存器地址 IIC_Wait_Ack(); //等待ACK回应 while(byte_cnt--) //如果仍有未发送数据 { IIC_Send_Byte(*(buf++)); //发送之 IIC_Wait_Ack(); //等待ACK信号 } IIC_Stop(); //发送I2C STOP信号 } //立即测量温度 int8_t DS3231_Read_temp(void) { uint8_t buf[2]; //等待空闲 do { DS3231_Read(CONTROL,2,buf); } while(buf[1]&BSY); //开始测量温度 buf[0]|=CONV; DS3231_Write(CONTROL,1,buf); //等待测量完成 do { DS3231_Read(CONTROL,1,buf); } while(buf[0]&CONV); //读取温度 DS3231_Read(TEMP_H,2,buf); //转换温度格式,2字节压缩为1字节 int8_t result = (buf[0]<<2)|(buf[1]>>6); //如果是负温度,恢复负号 if(buf[0]&0x80) result |= 0x80; //如果压缩过程存在溢出,限制成为最大或最小值 if(buf[0]&0x40) { if(result < 0) result = -128; else result = 127; } return(result); } //计算星期(输入为BCD数,适用于2000-2099年) uint8_t week(uint8_t y, uint8_t m, uint8_t d) { uint16_t year=(y>>4)*10+(y&0x0f)+2000; m=(m>>4)*10+m&0x0f; d=(d>>4)*10+d&0x0f; if(m<3) { m+=12; year--; } return((d+2*m+3*(m+1)/5+year+year/4+year/400-year/100)%7+1); } //DS3231初始化程序 void DS3231_init(void) { uint8_t flag; //读取状态字节 DS3231_Read(CTL_STA,1,&flag); //如果停机标志置位,说明初次上电或者其他原因曾经停机,重新设置寄存器 if(flag&OSF) { uint8_t* ds3231 = malloc(sizeof(uint8_t)*0x10); //申请17字节RAM if(ds3231!=NULL) { memset(ds3231,0,0x10); //清零RAM //设置默认日期和时间 ds3231[YEAR]=0x20; ds3231[MONTH]=0x01; ds3231[DATE]=0x01; ds3231[WEEK]=week(ds3231[YEAR],ds3231[MONTH],ds3231[DATE]); //ds3231[HOUR]=0x14; //设置SQW脚输出1Hz方波输出,停止32KHz输出。电池供电时时钟运行,方波停止,闹钟关闭 //ds3231[CTRL]=0; //ds3231[CTRL_STA]=0; //SQW脚设置为闹钟输出,开启32KHz时钟输出(厂家默认上电设置) //ds3231[CTRL]=INTCN; //ds3231[CTRL_STA]=EN32kHz; DS3231_Write(0,0x10,ds3231);//写入DS3231 free(ds3231); } } //非掉电情况,只重新设置1Hz方波输出 else { u8 temp; DS3231_Read(CONTROL,1,&temp); temp &= ~RS2 & ~RS1 & ~INTCN; DS3231_Write(CONTROL,1,&temp); } }