You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
7.4 KiB
275 lines
7.4 KiB
|
1 month ago
|
/**
|
||
|
|
* @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 <rtdevice.h>
|
||
|
|
#include <rtthread.h>
|
||
|
|
#include <sys/time.h>
|
||
|
1 month ago
|
#include <drivers/rtc.h>
|
||
|
1 month ago
|
|
||
|
|
#define DBG_TAG "DS1307"
|
||
|
|
#ifdef BRD_RTC_DS1307_DEBUG
|
||
|
|
#define DBG_LVL DBG_LOG
|
||
|
|
#else
|
||
|
1 month ago
|
#define DBG_LVL DBG_INFO
|
||
|
1 month ago
|
#endif
|
||
|
|
#include <rtdbg.h>
|
||
|
|
|
||
|
|
/* 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);
|
||
|
|
|
||
|
1 month ago
|
if(ds1307.datetime.tm_year<199)//
|
||
|
|
{
|
||
|
1 month ago
|
/* 设置日期为年月号 */
|
||
|
|
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);
|
||
|
1 month ago
|
}
|
||
|
1 month ago
|
}
|
||
|
|
|
||
|
|
/* 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);
|
||
|
|
|
||
|
|
// Sync with hardware every 10 minutes
|
||
|
1 month ago
|
if (++count >= 3600) // 600 seconds
|
||
|
1 month ago
|
{
|
||
|
|
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();
|
||
|
|
|
||
|
3 weeks ago
|
ds1307.thd_soft_update_sec = rt_thread_create("ds1307",auto_update_current_datatime,RT_NULL,256, 20, 5);
|
||
|
1 month ago
|
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;
|
||
|
|
}
|
||
|
1 month ago
|
INIT_APP_EXPORT(rt_hw_ds1307_rtc_init);
|
||
|
1 month ago
|
|
||
|
|
/* MSH Commands */
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
1 month ago
|
/* BRD_RTC_DS1307 */
|