summaryrefslogtreecommitdiff
path: root/bsp
diff options
context:
space:
mode:
authorrcitach <[email protected]>2026-06-30 09:25:37 +0000
committerRbb666 <[email protected]>2026-07-01 12:25:19 +0800
commite1b3a192f6a628ae48411fa3f84ad8114522b5ad (patch)
tree466471e8443013f500a3c1a699bd6593366365a7 /bsp
parenta9917f15724996ccee886c197fa707129585ac94 (diff)
Add RTC functionality
Diffstat (limited to 'bsp')
-rw-r--r--bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript3
-rw-r--r--bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c206
-rw-r--r--bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h37
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig64
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript8
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c345
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h22
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c233
-rw-r--r--bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h22
9 files changed, 939 insertions, 1 deletions
diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript
index 50008655f3..dc649c1ce2 100644
--- a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript
+++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/SConscript
@@ -41,6 +41,9 @@ if GetDepend('BSP_USING_EPWM'):
if GetDepend('BSP_USING_I2C'):
src += ['drv_i2c.c']
+if GetDepend('BSP_USING_RTC'):
+ src += ['drv_rtc.c']
+
group = DefineGroup('HAL_Drivers', src, depend = [''], CPPPATH = path)
Return('group')
diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c
new file mode 100644
index 0000000000..24f39ea38c
--- /dev/null
+++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-06-18 wenzhi346 add NS800 rtc driver
+ */
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <time.h>
+#include "drv_rtc.h"
+
+struct ns800_rtc_backend
+{
+ const char *name;
+ const struct ns800_rtc_ops *ops;
+ void *user_data;
+ rt_bool_t initialized;
+};
+
+static rt_rtc_dev_t g_rtc_dev;
+static struct ns800_rtc_backend g_rtc_backend;
+
+rt_err_t ns800_rtc_register(const char *name,
+ const struct ns800_rtc_ops *ops,
+ void *user_data)
+{
+ if ((ops == RT_NULL) || (ops->get_time == RT_NULL) || (ops->set_time == RT_NULL))
+ {
+ return -RT_EINVAL;
+ }
+
+ if (g_rtc_backend.ops != RT_NULL)
+ {
+ return -RT_EBUSY;
+ }
+
+ g_rtc_backend.name = (name != RT_NULL) ? name : "external";
+ g_rtc_backend.ops = ops;
+ g_rtc_backend.user_data = user_data;
+ g_rtc_backend.initialized = RT_FALSE;
+
+ return RT_EOK;
+}
+
+static rt_err_t ns800_rtc_init(void)
+{
+ rt_err_t ret;
+
+ if (g_rtc_backend.ops == RT_NULL)
+ {
+ return -RT_ENOSYS;
+ }
+
+ if (g_rtc_backend.initialized)
+ {
+ return RT_EOK;
+ }
+
+ ret = RT_EOK;
+ if (g_rtc_backend.ops->init != RT_NULL)
+ {
+ ret = g_rtc_backend.ops->init(g_rtc_backend.user_data);
+ }
+
+ if (ret == RT_EOK)
+ {
+ g_rtc_backend.initialized = RT_TRUE;
+ }
+
+ return ret;
+}
+
+static rt_err_t ns800_rtc_get_secs(time_t *sec)
+{
+ struct tm time_struct;
+ rt_err_t result;
+
+ if (sec == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ result = ns800_rtc_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+
+ result = g_rtc_backend.ops->get_time(g_rtc_backend.user_data, &time_struct);
+ if (result == RT_EOK)
+ {
+ *sec = mktime(&time_struct);
+ }
+
+ return result;
+}
+
+static rt_err_t ns800_rtc_set_secs(time_t *sec)
+{
+ struct tm time_struct;
+ rt_err_t result;
+
+ if (sec == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ result = ns800_rtc_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+
+ localtime_r(sec, &time_struct);
+
+ return g_rtc_backend.ops->set_time(g_rtc_backend.user_data, &time_struct);
+}
+
+static rt_err_t ns800_rtc_get_timeval(struct timeval *tv)
+{
+ time_t sec;
+ rt_err_t result;
+
+ if (tv == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ result = ns800_rtc_get_secs(&sec);
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = 0;
+
+ return RT_EOK;
+}
+
+static rt_err_t ns800_rtc_set_timeval(struct timeval *tv)
+{
+ time_t sec;
+
+ if (tv == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ sec = tv->tv_sec;
+ return ns800_rtc_set_secs(&sec);
+}
+
+static const struct rt_rtc_ops ns800_rtc_ops =
+{
+ ns800_rtc_init,
+ ns800_rtc_get_secs,
+ ns800_rtc_set_secs,
+ RT_NULL,
+ RT_NULL,
+ ns800_rtc_get_timeval,
+ ns800_rtc_set_timeval,
+};
+
+static rt_err_t ns800_rtc_try_init_device(void)
+{
+ rt_err_t result;
+
+ result = rt_device_init(&g_rtc_dev.parent);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("rtc: %s init deferred, result %d\n", g_rtc_backend.name, result);
+ return result;
+ }
+
+ rt_kprintf("rtc: %s ready\n", g_rtc_backend.name);
+ return RT_EOK;
+}
+
+int rt_hw_rtc_init(void)
+{
+ rt_err_t result;
+
+ if (g_rtc_backend.ops == RT_NULL)
+ {
+ rt_kprintf("rtc: no external backend registered\n");
+ return -1;
+ }
+
+ g_rtc_dev.ops = &ns800_rtc_ops;
+
+ result = rt_hw_rtc_register(&g_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("rtc: register failed, result %d\n", result);
+ return -1;
+ }
+
+ ns800_rtc_try_init_device();
+ return 0;
+}
+INIT_DEVICE_EXPORT(rt_hw_rtc_init);
+
diff --git a/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h
new file mode 100644
index 0000000000..8dadacb4a2
--- /dev/null
+++ b/bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ */
+
+#ifndef __DRV_RTC_H__
+#define __DRV_RTC_H__
+
+#include <rtthread.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ns800_rtc_ops
+{
+ rt_err_t (*init)(void *user_data);
+ rt_err_t (*get_time)(void *user_data, struct tm *time);
+ rt_err_t (*set_time)(void *user_data, const struct tm *time);
+};
+
+rt_err_t ns800_rtc_register(const char *name,
+ const struct ns800_rtc_ops *ops,
+ void *user_data);
+int rt_hw_rtc_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRV_RTC_H__ */
+
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig
index 2e6bad550c..94742d97cd 100644
--- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/Kconfig
@@ -184,6 +184,70 @@ menu "On-chip Peripheral Drivers"
endif
+ menuconfig BSP_USING_RTC
+ bool "Enable external RTC"
+ select RT_USING_RTC
+ default n
+ help
+ Enable the board-level RTC framework for an external RTC chip.
+
+ if BSP_USING_RTC
+ choice
+ prompt "External RTC backend"
+ default BSP_RTC_USING_DS1307
+
+ config BSP_RTC_USING_DS1307
+ bool "DS1307 over I2C"
+ select BSP_USING_I2C
+ select BSP_USING_HARD_I2C1
+ select RT_USING_I2C
+
+ config BSP_RTC_USING_SOFT
+ bool "Software RTC"
+ endchoice
+
+ if BSP_RTC_USING_DS1307
+ config BSP_RTC_DS1307_I2C_BUS_NAME
+ string "DS1307 I2C bus name"
+ default "hwi2c1"
+
+ config BSP_RTC_DS1307_I2C_ADDR
+ hex "DS1307 I2C address"
+ default 0x68
+ endif
+
+ if BSP_RTC_USING_SOFT
+ config BSP_RTC_SOFT_DEFAULT_YEAR
+ int "Software RTC default year"
+ default 2026
+
+ config BSP_RTC_SOFT_DEFAULT_MONTH
+ int "Software RTC default month"
+ range 1 12
+ default 1
+
+ config BSP_RTC_SOFT_DEFAULT_DAY
+ int "Software RTC default day"
+ range 1 31
+ default 1
+
+ config BSP_RTC_SOFT_DEFAULT_HOUR
+ int "Software RTC default hour"
+ range 0 23
+ default 0
+
+ config BSP_RTC_SOFT_DEFAULT_MIN
+ int "Software RTC default minute"
+ range 0 59
+ default 0
+
+ config BSP_RTC_SOFT_DEFAULT_SEC
+ int "Software RTC default second"
+ range 0 59
+ default 0
+ endif
+ endif
+
menuconfig BSP_USING_SPI
bool "Enable SPI"
select RT_USING_SPI
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript
index 5eeed838dc..b26046ba06 100644
--- a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/SConscript
@@ -3,7 +3,13 @@ from building import *
cwd = GetCurrentDir()
src = ['board.c']
-path = [cwd]
+path = [cwd, cwd + '/port']
+
+if GetDepend('BSP_RTC_USING_DS1307'):
+ src += ['port/ds1307.c']
+
+if GetDepend('BSP_RTC_USING_SOFT'):
+ src += ['port/soft_rtc.c']
cppdefines = ['NS800RT7P65X', 'USE_HAL_DRIVER', 'DUAL_CORE_ENABLE=0',
'SYSCLK_USE_PLL', 'SYSCLK_SOURCE_USE_HXTL', 'PLLCLK_SOURCE_USE_HXTL']
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c
new file mode 100644
index 0000000000..bbb272ef69
--- /dev/null
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-06-24 wenzhi346 first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "drv_rtc.h"
+#include "ds1307.h"
+
+#ifndef BSP_RTC_DS1307_I2C_BUS_NAME
+#define BSP_RTC_DS1307_I2C_BUS_NAME "hwi2c1"
+#endif
+
+#ifndef BSP_RTC_DS1307_I2C_ADDR
+#define BSP_RTC_DS1307_I2C_ADDR 0x68
+#endif
+
+#define DS1307_REG_SEC 0x00
+#define DS1307_REG_MIN 0x01
+#define DS1307_REG_HOUR 0x02
+#define DS1307_REG_WDAY 0x03
+#define DS1307_REG_MDAY 0x04
+#define DS1307_REG_MON 0x05
+#define DS1307_REG_YEAR 0x06
+
+#define DS1307_SEC_CH 0x80
+#define DS1307_HOUR_12H 0x40
+#define DS1307_HOUR_PM 0x20
+
+struct ds1307_device
+{
+ const char *bus_name;
+ rt_uint16_t addr;
+ struct rt_i2c_bus_device *i2c;
+ rt_bool_t clock_halt_reported;
+};
+
+static struct ds1307_device g_ds1307 =
+{
+ BSP_RTC_DS1307_I2C_BUS_NAME,
+ BSP_RTC_DS1307_I2C_ADDR,
+ RT_NULL,
+ RT_FALSE,
+};
+
+static rt_uint8_t bcd2bin(rt_uint8_t bcd)
+{
+ return ((bcd >> 4) * 10) + (bcd & 0x0F);
+}
+
+static rt_uint8_t bin2bcd(rt_uint8_t val)
+{
+ return ((val / 10) << 4) | (val % 10);
+}
+
+static rt_bool_t bcd_in_range(rt_uint8_t bcd, rt_uint8_t min, rt_uint8_t max)
+{
+ rt_uint8_t val;
+
+ if (((bcd & 0x0F) > 9) || (((bcd >> 4) & 0x0F) > 9))
+ {
+ return RT_FALSE;
+ }
+
+ val = bcd2bin(bcd);
+ return ((val >= min) && (val <= max)) ? RT_TRUE : RT_FALSE;
+}
+
+static rt_bool_t is_leap_year(int year)
+{
+ return (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))) ? RT_TRUE : RT_FALSE;
+}
+
+static rt_uint8_t month_days(int year, int mon)
+{
+ static const rt_uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ if ((mon == 1) && is_leap_year(year))
+ {
+ return 29;
+ }
+
+ return days[mon];
+}
+
+static rt_bool_t tm_is_valid(const struct tm *tm)
+{
+ int year;
+
+ if (tm == RT_NULL)
+ {
+ return RT_FALSE;
+ }
+
+ year = tm->tm_year + 1900;
+ if ((year < 2000) || (year > 2099))
+ {
+ return RT_FALSE;
+ }
+
+ if ((tm->tm_mon < 0) || (tm->tm_mon > 11))
+ {
+ return RT_FALSE;
+ }
+
+ if ((tm->tm_mday < 1) || (tm->tm_mday > month_days(year, tm->tm_mon)))
+ {
+ return RT_FALSE;
+ }
+
+ if ((tm->tm_wday < 0) || (tm->tm_wday > 6))
+ {
+ return RT_FALSE;
+ }
+
+ if ((tm->tm_hour < 0) || (tm->tm_hour > 23) ||
+ (tm->tm_min < 0) || (tm->tm_min > 59) ||
+ (tm->tm_sec < 0) || (tm->tm_sec > 59))
+ {
+ return RT_FALSE;
+ }
+
+ return RT_TRUE;
+}
+
+static rt_err_t ds1307_read_regs(struct ds1307_device *dev, rt_uint8_t reg, rt_uint8_t *buf, rt_uint8_t len)
+{
+ struct rt_i2c_msg msgs[2];
+
+ if ((dev == RT_NULL) || (dev->i2c == RT_NULL) || (buf == RT_NULL) || (len == 0))
+ {
+ return -RT_EINVAL;
+ }
+
+ msgs[0].addr = dev->addr;
+ msgs[0].flags = RT_I2C_WR;
+ msgs[0].len = 1;
+ msgs[0].buf = &reg;
+
+ msgs[1].addr = dev->addr;
+ msgs[1].flags = RT_I2C_RD;
+ msgs[1].len = len;
+ msgs[1].buf = buf;
+
+ return (rt_i2c_transfer(dev->i2c, msgs, 2) == 2) ? RT_EOK : -RT_ERROR;
+}
+
+static rt_err_t ds1307_write_regs(struct ds1307_device *dev, rt_uint8_t reg, const rt_uint8_t *buf, rt_uint8_t len)
+{
+ rt_uint8_t data[8];
+
+ if ((dev == RT_NULL) || (dev->i2c == RT_NULL) || (buf == RT_NULL) ||
+ (len == 0) || (len >= sizeof(data)))
+ {
+ return -RT_EINVAL;
+ }
+
+ data[0] = reg;
+ rt_memcpy(&data[1], buf, len);
+
+ return (rt_i2c_master_send(dev->i2c, dev->addr, 0, data, len + 1) == (len + 1))
+ ? RT_EOK : -RT_ERROR;
+}
+
+static rt_err_t ds1307_probe(struct ds1307_device *dev)
+{
+ rt_uint8_t sec;
+
+ return ds1307_read_regs(dev, DS1307_REG_SEC, &sec, 1);
+}
+
+rt_err_t ds1307_init(void)
+{
+ if (g_ds1307.i2c == RT_NULL)
+ {
+ g_ds1307.i2c = rt_i2c_bus_device_find(g_ds1307.bus_name);
+ if (g_ds1307.i2c == RT_NULL)
+ {
+ rt_kprintf("ds1307: i2c bus %s not found\n", g_ds1307.bus_name);
+ return -RT_ENOSYS;
+ }
+ }
+
+ return ds1307_probe(&g_ds1307);
+}
+
+rt_err_t ds1307_get_time(struct tm *tm)
+{
+ rt_uint8_t buf[7];
+ rt_uint8_t hour;
+ rt_uint8_t wday;
+
+ if (tm == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (ds1307_read_regs(&g_ds1307, DS1307_REG_SEC, buf, sizeof(buf)) != RT_EOK)
+ {
+ return -RT_ERROR;
+ }
+
+ if ((buf[DS1307_REG_SEC] & DS1307_SEC_CH) != 0)
+ {
+ if (!g_ds1307.clock_halt_reported)
+ {
+ rt_kprintf("ds1307: clock halted, please set time\n");
+ g_ds1307.clock_halt_reported = RT_TRUE;
+ }
+ return -RT_ERROR;
+ }
+
+ if (!bcd_in_range(buf[DS1307_REG_SEC] & 0x7F, 0, 59) ||
+ !bcd_in_range(buf[DS1307_REG_MIN] & 0x7F, 0, 59) ||
+ !bcd_in_range(buf[DS1307_REG_MDAY] & 0x3F, 1, 31) ||
+ !bcd_in_range(buf[DS1307_REG_MON] & 0x1F, 1, 12) ||
+ !bcd_in_range(buf[DS1307_REG_YEAR], 0, 99))
+ {
+ return -RT_ERROR;
+ }
+
+ if ((buf[DS1307_REG_HOUR] & DS1307_HOUR_12H) != 0)
+ {
+ hour = bcd2bin(buf[DS1307_REG_HOUR] & 0x1F);
+ if ((hour < 1) || (hour > 12))
+ {
+ return -RT_ERROR;
+ }
+
+ if ((buf[DS1307_REG_HOUR] & DS1307_HOUR_PM) != 0)
+ {
+ if (hour != 12)
+ {
+ hour += 12;
+ }
+ }
+ else if (hour == 12)
+ {
+ hour = 0;
+ }
+ }
+ else
+ {
+ if (!bcd_in_range(buf[DS1307_REG_HOUR] & 0x3F, 0, 23))
+ {
+ return -RT_ERROR;
+ }
+ hour = bcd2bin(buf[DS1307_REG_HOUR] & 0x3F);
+ }
+
+ if (!bcd_in_range(buf[DS1307_REG_WDAY] & 0x07, 1, 7))
+ {
+ return -RT_ERROR;
+ }
+ wday = bcd2bin(buf[DS1307_REG_WDAY] & 0x07);
+
+ rt_memset(tm, 0, sizeof(*tm));
+ tm->tm_sec = bcd2bin(buf[DS1307_REG_SEC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[DS1307_REG_MIN] & 0x7F);
+ tm->tm_hour = hour;
+ tm->tm_wday = wday - 1;
+ tm->tm_mday = bcd2bin(buf[DS1307_REG_MDAY] & 0x3F);
+ tm->tm_mon = bcd2bin(buf[DS1307_REG_MON] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[DS1307_REG_YEAR]) + 100;
+ tm->tm_isdst = -1;
+
+ if (!tm_is_valid(tm))
+ {
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+
+rt_err_t ds1307_set_time(const struct tm *tm)
+{
+ rt_uint8_t buf[7];
+
+ if (!tm_is_valid(tm))
+ {
+ return -RT_EINVAL;
+ }
+
+ buf[DS1307_REG_SEC] = bin2bcd(tm->tm_sec);
+ buf[DS1307_REG_MIN] = bin2bcd(tm->tm_min);
+ buf[DS1307_REG_HOUR] = bin2bcd(tm->tm_hour);
+ buf[DS1307_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
+ buf[DS1307_REG_MDAY] = bin2bcd(tm->tm_mday);
+ buf[DS1307_REG_MON] = bin2bcd(tm->tm_mon + 1);
+ buf[DS1307_REG_YEAR] = bin2bcd(tm->tm_year - 100);
+
+ g_ds1307.clock_halt_reported = RT_FALSE;
+
+ return ds1307_write_regs(&g_ds1307, DS1307_REG_SEC, buf, sizeof(buf));
+}
+
+static rt_err_t ds1307_backend_init(void *user_data)
+{
+ RT_UNUSED(user_data);
+
+ return ds1307_init();
+}
+
+static rt_err_t ds1307_backend_get_time(void *user_data, struct tm *tm)
+{
+ RT_UNUSED(user_data);
+
+ return ds1307_get_time(tm);
+}
+
+static rt_err_t ds1307_backend_set_time(void *user_data, const struct tm *tm)
+{
+ RT_UNUSED(user_data);
+
+ return ds1307_set_time(tm);
+}
+
+static const struct ns800_rtc_ops ds1307_rtc_ops =
+{
+ ds1307_backend_init,
+ ds1307_backend_get_time,
+ ds1307_backend_set_time,
+};
+
+int rt_hw_ds1307_init(void)
+{
+ rt_err_t result;
+
+ result = ns800_rtc_register("ds1307", &ds1307_rtc_ops, &g_ds1307);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("ds1307: register rtc backend failed, result %d\n", result);
+ return -1;
+ }
+
+ return 0;
+}
+INIT_BOARD_EXPORT(rt_hw_ds1307_init);
+
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h
new file mode 100644
index 0000000000..a06ce0078d
--- /dev/null
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-06-24 wenzhi346 first version
+ */
+#ifndef __DS1307_H__
+#define __DS1307_H__
+
+#include <rtthread.h>
+#include <time.h>
+
+rt_err_t ds1307_init(void);
+rt_err_t ds1307_get_time(struct tm *time);
+rt_err_t ds1307_set_time(const struct tm *time);
+int rt_hw_ds1307_init(void);
+
+#endif
+
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c
new file mode 100644
index 0000000000..a4732e483b
--- /dev/null
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-06-30 rcitach first version
+ */
+
+#include <rtthread.h>
+#include <rthw.h>
+#include <time.h>
+#include "drv_rtc.h"
+#include "soft_rtc.h"
+
+#ifndef BSP_RTC_SOFT_DEFAULT_YEAR
+#define BSP_RTC_SOFT_DEFAULT_YEAR 2026
+#endif
+
+#ifndef BSP_RTC_SOFT_DEFAULT_MONTH
+#define BSP_RTC_SOFT_DEFAULT_MONTH 1
+#endif
+
+#ifndef BSP_RTC_SOFT_DEFAULT_DAY
+#define BSP_RTC_SOFT_DEFAULT_DAY 1
+#endif
+
+#ifndef BSP_RTC_SOFT_DEFAULT_HOUR
+#define BSP_RTC_SOFT_DEFAULT_HOUR 0
+#endif
+
+#ifndef BSP_RTC_SOFT_DEFAULT_MIN
+#define BSP_RTC_SOFT_DEFAULT_MIN 0
+#endif
+
+#ifndef BSP_RTC_SOFT_DEFAULT_SEC
+#define BSP_RTC_SOFT_DEFAULT_SEC 0
+#endif
+
+struct soft_rtc_device
+{
+ time_t base_sec;
+ rt_tick_t base_tick;
+ rt_bool_t valid;
+};
+
+static struct soft_rtc_device g_soft_rtc;
+
+static rt_bool_t soft_rtc_is_leap_year(int year)
+{
+ return (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))) ? RT_TRUE : RT_FALSE;
+}
+
+static rt_uint8_t soft_rtc_month_days(int year, int mon)
+{
+ static const rt_uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ if ((mon == 1) && soft_rtc_is_leap_year(year))
+ {
+ return 29;
+ }
+
+ return days[mon];
+}
+
+static rt_bool_t soft_rtc_tm_is_valid(const struct tm *tm)
+{
+ int year;
+
+ if (tm == RT_NULL)
+ {
+ return RT_FALSE;
+ }
+
+ year = tm->tm_year + 1900;
+ if ((year < 1970) ||
+ (tm->tm_mon < 0) || (tm->tm_mon > 11) ||
+ (tm->tm_mday < 1) || (tm->tm_mday > soft_rtc_month_days(year, tm->tm_mon)) ||
+ (tm->tm_hour < 0) || (tm->tm_hour > 23) ||
+ (tm->tm_min < 0) || (tm->tm_min > 59) ||
+ (tm->tm_sec < 0) || (tm->tm_sec > 59))
+ {
+ return RT_FALSE;
+ }
+
+ return RT_TRUE;
+}
+
+static void soft_rtc_set_secs(time_t sec)
+{
+ rt_base_t level;
+
+ level = rt_hw_interrupt_disable();
+ g_soft_rtc.base_sec = sec;
+ g_soft_rtc.base_tick = rt_tick_get();
+ g_soft_rtc.valid = RT_TRUE;
+ rt_hw_interrupt_enable(level);
+}
+
+static time_t soft_rtc_get_secs(void)
+{
+ time_t base_sec;
+ rt_tick_t base_tick;
+ rt_bool_t valid;
+ rt_base_t level;
+
+ level = rt_hw_interrupt_disable();
+ base_sec = g_soft_rtc.base_sec;
+ base_tick = g_soft_rtc.base_tick;
+ valid = g_soft_rtc.valid;
+ rt_hw_interrupt_enable(level);
+
+ if (!valid)
+ {
+ return 0;
+ }
+
+ return base_sec + (time_t)(rt_tick_get_delta(base_tick) / RT_TICK_PER_SECOND);
+}
+
+rt_err_t soft_rtc_init(void)
+{
+ struct tm default_time;
+
+ if (g_soft_rtc.valid)
+ {
+ return RT_EOK;
+ }
+
+ rt_memset(&default_time, 0, sizeof(default_time));
+ default_time.tm_year = BSP_RTC_SOFT_DEFAULT_YEAR - 1900;
+ default_time.tm_mon = BSP_RTC_SOFT_DEFAULT_MONTH - 1;
+ default_time.tm_mday = BSP_RTC_SOFT_DEFAULT_DAY;
+ default_time.tm_hour = BSP_RTC_SOFT_DEFAULT_HOUR;
+ default_time.tm_min = BSP_RTC_SOFT_DEFAULT_MIN;
+ default_time.tm_sec = BSP_RTC_SOFT_DEFAULT_SEC;
+ default_time.tm_isdst = -1;
+
+ if (!soft_rtc_tm_is_valid(&default_time))
+ {
+ return -RT_EINVAL;
+ }
+
+ soft_rtc_set_secs(mktime(&default_time));
+ return RT_EOK;
+}
+
+rt_err_t soft_rtc_get_time(struct tm *tm)
+{
+ time_t sec;
+
+ if (tm == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (!g_soft_rtc.valid)
+ {
+ rt_err_t result = soft_rtc_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ }
+
+ sec = soft_rtc_get_secs();
+ localtime_r(&sec, tm);
+
+ return RT_EOK;
+}
+
+rt_err_t soft_rtc_set_time(const struct tm *tm)
+{
+ struct tm time_copy;
+ time_t sec;
+
+ if (!soft_rtc_tm_is_valid(tm))
+ {
+ return -RT_EINVAL;
+ }
+
+ time_copy = *tm;
+ time_copy.tm_isdst = -1;
+ sec = mktime(&time_copy);
+ soft_rtc_set_secs(sec);
+
+ return RT_EOK;
+}
+
+static rt_err_t soft_rtc_backend_init(void *user_data)
+{
+ RT_UNUSED(user_data);
+
+ return soft_rtc_init();
+}
+
+static rt_err_t soft_rtc_backend_get_time(void *user_data, struct tm *tm)
+{
+ RT_UNUSED(user_data);
+
+ return soft_rtc_get_time(tm);
+}
+
+static rt_err_t soft_rtc_backend_set_time(void *user_data, const struct tm *tm)
+{
+ RT_UNUSED(user_data);
+
+ return soft_rtc_set_time(tm);
+}
+
+static const struct ns800_rtc_ops soft_rtc_ops =
+{
+ soft_rtc_backend_init,
+ soft_rtc_backend_get_time,
+ soft_rtc_backend_set_time,
+};
+
+int rt_hw_soft_rtc_init(void)
+{
+ rt_err_t result;
+
+ result = ns800_rtc_register("soft_rtc", &soft_rtc_ops, &g_soft_rtc);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("soft_rtc: register rtc backend failed, result %d\n", result);
+ return -1;
+ }
+
+ return 0;
+}
+INIT_BOARD_EXPORT(rt_hw_soft_rtc_init);
+
diff --git a/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h
new file mode 100644
index 0000000000..92196db5ba
--- /dev/null
+++ b/bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-06-30 rcitach first version
+ */
+#ifndef __SOFT_RTC_H__
+#define __SOFT_RTC_H__
+
+#include <rtthread.h>
+#include <time.h>
+
+rt_err_t soft_rtc_init(void);
+rt_err_t soft_rtc_get_time(struct tm *time);
+rt_err_t soft_rtc_set_time(const struct tm *time);
+int rt_hw_soft_rtc_init(void);
+
+#endif /* __SOFT_RTC_H__ */
+