From 69f409e893cb63e5a0f1bd88e105f6b65d1284c5 Mon Sep 17 00:00:00 2001 From: sc <2401809606@qq.com> Date: Sat, 13 Dec 2025 02:00:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A1=AC=E4=BB=B6rtc?= =?UTF-8?q?=EF=BC=88ds1307=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drivers/drv_ds1307.c | 362 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 drivers/drv_ds1307.c diff --git a/drivers/drv_ds1307.c b/drivers/drv_ds1307.c new file mode 100644 index 0000000..0bbfcd6 --- /dev/null +++ b/drivers/drv_ds1307.c @@ -0,0 +1,362 @@ +/** + * @file drv_rtc_ds1307.c + * @brief DS1307 RTC driver for RT-Thread + * @author jiache (wanghuan3037@fiberhome.com) + * @version 1.1 + * @date 2025-12-13 + * + * Notes: + * - DS1307 requires 5V for reliable operation (not just 3.3V or backup battery). + * - Ensure I2C pull-up resistors are present. + * + * Kconfig usage (add to board/Kconfig): + * config BRD_RTC_DS1307 + * bool "Enable rtc ds1307" + * default n + * if BRD_RTC_DS1307 + * config DS1307_IIC_BUS + * string "Select I2C bus" + * default "i2c2" + * config BRD_RTC_DS1307_DEBUG + * bool "Enable DS1307 debug log" + * default n + * endif + */ + +#include "board.h" +#include +#include +#include + + +#define DBG_TAG "DS1307" +#ifdef BRD_RTC_DS1307_DEBUG + #define DBG_LVL DBG_LOG +#else + #define DBG_LVL DBG_INFO +#endif +#include + +/* DS1307 I2C 7-bit address */ +#define DS1307_ADDR 0x68 + +/* Driver structure */ +typedef struct drv_rtc_ds1307 +{ + struct rt_i2c_bus_device *i2c_bus; + char *iic_name; + struct tm datetime; + struct timeval tick; + rt_thread_t thd_soft_update_sec; +} drv_rtc_ds1307_t; + +/* Default I2C bus name from Kconfig or hardcoded */ +#ifndef DS1307_IIC_BUS + #define DS1307_IIC_BUS "i2c2" +#endif + +static drv_rtc_ds1307_t ds1307 = { + .iic_name = DS1307_IIC_BUS, +}; + +/* Write multiple registers starting at reg */ +static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, const rt_uint8_t *data, rt_size_t len) +{ + rt_uint8_t buf[16]; + if (len + 1 > sizeof(buf)) return -RT_EINVAL; + + buf[0] = reg; + rt_memcpy(&buf[1], data, len); + + struct rt_i2c_msg msg; + msg.addr = DS1307_ADDR; + msg.flags = RT_I2C_WR; + msg.buf = buf; + msg.len = len + 1; + + return (rt_i2c_transfer(bus, &msg, 1) == 1) ? RT_EOK : -RT_ERROR; +} + +/* Read multiple registers starting at reg */ +static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf) +{ + struct rt_i2c_msg msgs[2]; + msgs[0].addr = DS1307_ADDR; + msgs[0].flags = RT_I2C_WR; + msgs[0].buf = ® + msgs[0].len = 1; + + msgs[1].addr = DS1307_ADDR; + msgs[1].flags = RT_I2C_RD; + msgs[1].buf = buf; + msgs[1].len = len; + + return (rt_i2c_transfer(bus, msgs, 2) == 2) ? RT_EOK : -RT_ERROR; +} + +/* Read current time from DS1307 */ +static void ds1307_read_time(void) +{ + uint8_t buff[7]; + RT_ASSERT(ds1307.i2c_bus); + + if (read_regs(ds1307.i2c_bus, 0x00, 7, buff) != RT_EOK) + { + LOG_E("Failed to read DS1307 registers"); + return; + } + + // BCD to decimal + ds1307.datetime.tm_sec = (buff[0] & 0x7F); // mask CH bit + ds1307.datetime.tm_sec = (ds1307.datetime.tm_sec >> 4) * 10 + (ds1307.datetime.tm_sec & 0x0F); + + ds1307.datetime.tm_min = (buff[1] >> 4) * 10 + (buff[1] & 0x0F); + ds1307.datetime.tm_hour = (buff[2] >> 4) * 10 + (buff[2] & 0x0F); + ds1307.datetime.tm_wday = (buff[3] & 0x07) - 1; // DS1307: 1=Sun → tm_wday: 0=Sun + ds1307.datetime.tm_mday = (buff[4] >> 4) * 10 + (buff[4] & 0x0F); + ds1307.datetime.tm_mon = (buff[5] >> 4) * 10 + (buff[5] & 0x0F) - 1; // 0-based + ds1307.datetime.tm_year = (buff[6] >> 4) * 10 + (buff[6] & 0x0F) + 100; // years since 1900 + + // Optional: update tick if libc is available +#ifdef RT_USING_LIBC + ds1307.tick.tv_sec = mktime(&ds1307.datetime); + ds1307.tick.tv_usec = 0; +#endif + + LOG_I("RTC: %d-%02d-%02d %02d:%02d:%02d", + ds1307.datetime.tm_year + 1900, + ds1307.datetime.tm_mon + 1, + ds1307.datetime.tm_mday, + ds1307.datetime.tm_hour, + ds1307.datetime.tm_min, + ds1307.datetime.tm_sec); + + /* 设置日期为年月号 */ + set_date(ds1307.datetime.tm_year + 1900, ds1307.datetime.tm_mon + 1, ds1307.datetime.tm_mday); + /* 设置时间为点分秒 */ + set_time(ds1307.datetime.tm_hour, ds1307.datetime.tm_min, ds1307.datetime.tm_sec); + /* 获取时间 */ + time_t now = time(RT_NULL); + /* 打印输出时间信息 */ + rt_kprintf("DATE: %s", ctime(&now)); +} + +/* Software thread to maintain local time estimation */ +static void auto_update_current_datatime(void *parameter) +{ + uint32_t count = 0; + while (1) + { + rt_thread_delay(RT_TICK_PER_SECOND); + ds1307.datetime.tm_sec++; + if (ds1307.datetime.tm_sec >= 60) + { + ds1307.datetime.tm_sec = 0; + ds1307.datetime.tm_min++; + if (ds1307.datetime.tm_min >= 60) + { + ds1307.datetime.tm_min = 0; + ds1307.datetime.tm_hour++; + if (ds1307.datetime.tm_hour >= 24) + { + ds1307.datetime.tm_hour = 0; + // Note: day/month/year rollover not handled in soft update + } + } + } + + // Sync with hardware every 10 minutes + if (++count >= 600) // 600 seconds + { + ds1307_read_time(); + count = 0; + } + } +} + +/* Directly write struct tm to DS1307 (no time_t dependency) */ +static rt_err_t ds1307_write_tm(struct tm *tm) +{ + uint8_t buff[7]; + + // Seconds (bit7 = CH must be 0 to enable oscillator) + buff[0] = ((tm->tm_sec / 10) << 4) | (tm->tm_sec % 10); + + // Minutes + buff[1] = ((tm->tm_min / 10) << 4) | (tm->tm_min % 10); + + // Hours (24-hour mode, bit6=0) + buff[2] = ((tm->tm_hour / 10) << 4) | (tm->tm_hour % 10); + + // Day of week: DS1307 uses 1=Sun, 2=Mon ... 7=Sat + // tm_wday: 0=Sun, 1=Mon ... 6=Sat → so +1 + buff[3] = tm->tm_wday + 1; + + // Date + buff[4] = ((tm->tm_mday / 10) << 4) | (tm->tm_mday % 10); + + // Month + int month = tm->tm_mon + 1; // convert to 1-based + buff[5] = ((month / 10) << 4) | (month % 10); + + // Year (last two digits only) + int year = tm->tm_year + 1900; // full year + buff[6] = (((year % 100) / 10) << 4) | (year % 10); + + rt_err_t err = write_reg(ds1307.i2c_bus, 0x00, buff, 7); + if (err == RT_EOK) + { + ds1307.datetime = *tm; +#ifdef RT_USING_LIBC + ds1307.tick.tv_sec = mktime(tm); + ds1307.tick.tv_usec = 0; +#endif + } + return err; +} + +/* RTC ops */ +static rt_err_t ds1307_rtc_init(void) +{ + return RT_EOK; +} + +static rt_err_t ds1307_rtc_get_secs(time_t *sec) +{ + ds1307_read_time(); +#ifdef RT_USING_LIBC + *sec = ds1307.tick.tv_sec; +#else + *sec = 0; // fallback +#endif + return RT_EOK; +} + +static rt_err_t ds1307_rtc_set_secs(time_t *sec) +{ +#ifdef RT_USING_LIBC + struct tm tm_time; + localtime_r(sec, &tm_time); + return ds1307_write_tm(&tm_time); +#else + LOG_E("Set by timestamp requires RT_USING_LIBC"); + return -RT_ERROR; +#endif +} + +static rt_err_t ds1307_rtc_get_timeval(struct timeval *tv) +{ + ds1307_read_time(); + *tv = ds1307.tick; + return RT_EOK; +} + +static rt_err_t ds1307_rtc_set_timeval(struct timeval *tv) +{ +#ifdef RT_USING_LIBC + struct tm tm_time; + localtime_r(&tv->tv_sec, &tm_time); + return ds1307_write_tm(&tm_time); +#else + LOG_E("Set by timeval requires RT_USING_LIBC"); + return -RT_ERROR; +#endif +} + +static const struct rt_rtc_ops ds1307_rtc_ops = +{ + ds1307_rtc_init, + ds1307_rtc_get_secs, + ds1307_rtc_set_secs, + RT_NULL, + RT_NULL, + ds1307_rtc_get_timeval, + ds1307_rtc_set_timeval, +}; + +static rt_rtc_dev_t ds1307_rtc_dev; + +/* Initialization and registration */ +static int rt_hw_ds1307_rtc_init(void) +{ + ds1307.i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(ds1307.iic_name); + if (!ds1307.i2c_bus) + { + LOG_E("I2C bus '%s' not found", ds1307.iic_name); + return -RT_ERROR; + } + + ds1307_read_time(); + + ds1307.thd_soft_update_sec = rt_thread_create("ds1307", + auto_update_current_datatime, + RT_NULL, + 512, 10, 5); + if (ds1307.thd_soft_update_sec) + { + rt_thread_startup(ds1307.thd_soft_update_sec); + } + + rt_err_t result = rt_hw_rtc_register(&ds1307_rtc_dev, "ds1307", RT_DEVICE_FLAG_RDWR, &ds1307); + if (result != RT_EOK) + { + LOG_E("RTC register failed: %d", result); + return result; + } + + LOG_I("DS1307 RTC initialized successfully on %s", ds1307.iic_name); + return RT_EOK; +} +INIT_COMPONENT_EXPORT(rt_hw_ds1307_rtc_init); + +/* MSH Commands */ +#include + +int date_drv_set(int argc, char *argv[]) +{ + if (argc != 7) + { + rt_kprintf("Usage: ds1307_set_time YYYY MM DD HH MM SS\n"); + rt_kprintf("Example: ds1307_set_time 2025 12 25 10 30 00\n"); + return -1; + } + + struct tm tm_time = {0}; + tm_time.tm_year = atoi(argv[1]) - 1900; + tm_time.tm_mon = atoi(argv[2]) - 1; + tm_time.tm_mday = atoi(argv[3]); + tm_time.tm_hour = atoi(argv[4]); + tm_time.tm_min = atoi(argv[5]); + tm_time.tm_sec = atoi(argv[6]); + + // Basic validation + if (tm_time.tm_year < 100 || tm_time.tm_year > 200 || + tm_time.tm_mon < 0 || tm_time.tm_mon > 11 || + tm_time.tm_mday < 1 || tm_time.tm_mday > 31 || + tm_time.tm_hour < 0 || tm_time.tm_hour > 23 || + tm_time.tm_min < 0 || tm_time.tm_min > 59 || + tm_time.tm_sec < 0 || tm_time.tm_sec > 59) + { + rt_kprintf("Error: Invalid date/time range!\n"); + return -1; + } + + rt_err_t err = ds1307_write_tm(&tm_time); + if (err != RT_EOK) + { + rt_kprintf("Error: Failed to write DS1307!\n"); + return -1; + } + + rt_kprintf("Success: DS1307 time set.\n"); + return 0; +} +MSH_CMD_EXPORT(date_drv_set, Set DS1307 RTC time: YYYY MM DD HH MM SS); + +void date_drv(void) +{ + ds1307_read_time(); +} +MSH_CMD_EXPORT(date_drv, Read DS1307 RTC time); + + /* BRD_RTC_DS1307 */