在本系统里使用了一个RTC模块DS1307,并且通过STM32来控制,显示标准时间,日历、计时等功能,这个芯片使用i2c接口,同多对对应寄存器的读取就能获取当前时间,当然这个时间是需要我们先设置一下的,具体内容感兴趣可以参考芯片手册,我这里值展示我们能用到的相关内容。
#define DS1307_I2C_ADDR 0x68
#define DS1307_REG_SECOND 0x00
#define DS1307_REG_MINUTE 0x01
#define DS1307_REG_HOUR 0x02
#define DS1307_REG_DOW 0x03
#define DS1307_REG_DATE 0x04
#define DS1307_REG_MONTH 0x05
#define DS1307_REG_YEAR 0x06
#define DS1307_REG_CONTROL 0x07
#define DS1307_REG_UTC_HR 0x08
#define DS1307_REG_UTC_MIN 0x09
#define DS1307_REG_CENT 0x10
#define DS1307_REG_RAM 0x11
#define DS1307_TIMEOUT 1000
我把比较关键的函数接口写在下面,供参考,我们首先从硬件层面来说是初始化操作,启动计时
/**
* @brief 初始化 DS1307 模块。设置时钟停止位为 0,开始计时。
* @param hi2c 用户提供的 I2C 句柄指针。
*/
void DS1307_Init(I2C_HandleTypeDef *hi2c) {
_ds1307_ui2c = hi2c; // 将传入的 I2C 句柄保存到全局变量
DS1307_SetClockHalt(0); // 设置时钟停止位为 0,启动计时
}
/**
* @brief 设置时钟停止位。
* @param halt 时钟停止位,0 或 1。0 用于开始计时,1 用于停止计时。
*/
void DS1307_SetClockHalt(uint8_t halt) {
uint8_t ch = (halt ? 1 << 7 : 0); // 根据传入的 halt 值决定设置时钟停止位
// 设置 DS1307 的秒钟寄存器(SECOND),将停止位修改为所需值
DS1307_SetRegByte(DS1307_REG_SECOND, ch | (DS1307_GetRegByte(DS1307_REG_SECOND) & 0x7f));
}
/**
* @brief 获取当前时钟停止位。
* @return 时钟停止位,0 或 1。
*/
uint8_t DS1307_GetClockHalt(void) {
// 获取秒钟寄存器中的停止位(第 7 位)
return (DS1307_GetRegByte(DS1307_REG_SECOND) & 0x80) >> 7;
}
/**
* @brief 设置指定 DS1307 寄存器的字节值。
* @param regAddr 寄存器地址。
* @param val 要设置的值,范围是 0 到 255。
*/
void DS1307_SetRegByte(uint8_t regAddr, uint8_t val) {
uint8_t bytes[2] = { regAddr, val }; // 创建一个包含寄存器地址和数据的字节数组
// 通过 I2C 总线向 DS1307 发送数据
HAL_I2C_Master_Transmit(_ds1307_ui2c, DS1307_I2C_ADDR << 1, bytes, 2, DS1307_TIMEOUT);
}
/**
* @brief 获取指定 DS1307 寄存器的字节值。
* @param regAddr 寄存器地址。
* @return 寄存器中存储的值,范围是 0 到 255。
*/
uint8_t DS1307_GetRegByte(uint8_t regAddr) {
uint8_t val;
// 通过 I2C 总线发送寄存器地址,并接收寄存器中的数据
HAL_I2C_Master_Transmit(_ds1307_ui2c, DS1307_I2C_ADDR << 1, ®Addr, 1, DS1307_TIMEOUT);
HAL_I2C_Master_Receive(_ds1307_ui2c, DS1307_I2C_ADDR << 1, &val, 1, DS1307_TIMEOUT);
return val;
}
DS1307_SetRegByte和DS1307_GetRegByte是封装的两个函数接口,下面是获取当前时间的一些函数,我们通过调用即可获取
/**
* @brief 获取当前星期几。
* @return 距离上一个星期天的天数,范围是 0 到 6。
*/
uint8_t DS1307_GetDayOfWeek(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_DOW)); // 获取星期几的值并解码
}
/**
* @brief 获取当前日期(月份中的第几天)。
* @return 日期,范围是 1 到 31。
*/
uint8_t DS1307_GetDate(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_DATE)); // 获取日期并解码
}
/**
* @brief 获取当前月份。
* @return 月份,范围是 1 到 12。
*/
uint8_t DS1307_GetMonth(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_MONTH)); // 获取月份并解码
}
/**
* @brief 获取当前年份。
* @return 年份,范围是 2000 到 2099。
*/
uint16_t DS1307_GetYear(void) {
uint16_t cen = DS1307_GetRegByte(DS1307_REG_CENT) * 100; // 获取世纪部分
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_YEAR)) + cen; // 获取年份并解码,合并世纪
}
/**
* @brief 获取当前小时(24 小时制)。
* @return 小时,范围是 0 到 23。
*/
uint8_t DS1307_GetHour(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_HOUR) & 0x3f); // 获取小时并解码,去除 AM/PM 标志位
}
/**
* @brief 获取当前分钟。
* @return 分钟,范围是 0 到 59。
*/
uint8_t DS1307_GetMinute(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_MINUTE)); // 获取分钟并解码
}
/**
* @brief 获取当前秒钟(不包括时钟停止位)。
* @return 秒钟,范围是 0 到 59。
*/
uint8_t DS1307_GetSecond(void) {
return DS1307_DecodeBCD(DS1307_GetRegByte(DS1307_REG_SECOND) & 0x7f); // 获取秒钟并解码,去除停止位
}
下面是一些设置函数,通过使用这些函数后我们可以对当前时间进行设置
/**
* @brief 设置当前星期几。
* @param dayOfWeek 距离上一个星期天的天数,范围是 0 到 6。
*/
void DS1307_SetDayOfWeek(uint8_t dayOfWeek) {
DS1307_SetRegByte(DS1307_REG_DOW, DS1307_EncodeBCD(dayOfWeek)); // 设置星期几,并编码为 BCD 格式
}
/**
* @brief 设置当前日期(月份中的第几天)。
* @param date 日期,范围是 1 到 31。
*/
void DS1307_SetDate(uint8_t date) {
DS1307_SetRegByte(DS1307_REG_DATE, DS1307_EncodeBCD(date)); // 设置日期,并编码为 BCD 格式
}
/**
* @brief 设置当前月份。
* @param month 月份,范围是 1 到 12。
*/
void DS1307_SetMonth(uint8_t month) {
DS1307_SetRegByte(DS1307_REG_MONTH, DS1307_EncodeBCD(month)); // 设置月份,并编码为 BCD 格式
}
/**
* @brief 设置当前年份。
* @param year 年份,范围是 2000 到 2099。
*/
void DS1307_SetYear(uint16_t year) {
// 设置世纪部分(年份的前两位),存储在 CENT 寄存器中
DS1307_SetRegByte(DS1307_REG_CENT, year / 100);
// 设置年份的后两位(百位以下),存储在 YEAR 寄存器中
DS1307_SetRegByte(DS1307_REG_YEAR, DS1307_EncodeBCD(year % 100));
}
/**
* @brief 设置当前小时(24 小时制)。
* @param hour_24mode 小时(24 小时制),范围是 0 到 23。
*/
void DS1307_SetHour(uint8_t hour_24mode) {
// 将小时(0-23)编码为二进制编码十进制(BCD)格式,并设置到 HOUR 寄存器中
DS1307_SetRegByte(DS1307_REG_HOUR, DS1307_EncodeBCD(hour_24mode & 0x3f));
// 注意:使用与操作(& 0x3f)确保时钟数据的高位(AM/PM标志)为零,始终使用 24 小时制
}
/**
* @brief 设置当前分钟。
* @param minute 分钟,范围是 0 到 59。
*/
void DS1307_SetMinute(uint8_t minute) {
// 将分钟编码为 BCD 格式并存储到 MINUTE 寄存器中
DS1307_SetRegByte(DS1307_REG_MINUTE, DS1307_EncodeBCD(minute));
}
/**
* @brief 设置当前秒钟。
* @param second 秒钟,范围是 0 到 59。
*/
void DS1307_SetSecond(uint8_t second) {
uint8_t ch = DS1307_GetClockHalt(); // 获取时钟停止位(如果是 1,则时钟停止)
// 将秒钟编码为 BCD 格式,并设置到 SECOND 寄存器中,同时保留时钟停止位的状态
DS1307_SetRegByte(DS1307_REG_SECOND, DS1307_EncodeBCD(second | ch));
}
/**
* @brief 设置 UTC 偏移。
* @note UTC 偏移不会自动更新。
* @param hr UTC 小时偏移,范围是 -12 到 12。
* @param min UTC 分钟偏移,范围是 0 到 59。
*/
void DS1307_SetTimeZone(int8_t hr, uint8_t min) {
// 设置 UTC 小时偏移,存储在 UTC_HR 寄存器中
DS1307_SetRegByte(DS1307_REG_UTC_HR, hr);
// 设置 UTC 分钟偏移,存储在 UTC_MIN 寄存器中
DS1307_SetRegByte(DS1307_REG_UTC_MIN, min);
}
至此DS1307的驱动就算完成了