/** * @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 #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; } /* 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); } 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 */