From e1b3a192f6a628ae48411fa3f84ad8114522b5ad Mon Sep 17 00:00:00 2001 From: rcitach Date: Tue, 30 Jun 2026 09:25:37 +0000 Subject: [PATCH] Add RTC functionality --- .../libraries/HAL_Drivers/drivers/SConscript | 3 + .../libraries/HAL_Drivers/drivers/drv_rtc.c | 206 +++++++++++ .../libraries/HAL_Drivers/drivers/drv_rtc.h | 37 ++ .../ns800/ns800rt7p65-nssinepad/board/Kconfig | 64 ++++ .../ns800rt7p65-nssinepad/board/SConscript | 8 +- .../ns800rt7p65-nssinepad/board/port/ds1307.c | 345 ++++++++++++++++++ .../ns800rt7p65-nssinepad/board/port/ds1307.h | 22 ++ .../board/port/soft_rtc.c | 233 ++++++++++++ .../board/port/soft_rtc.h | 22 ++ 9 files changed, 939 insertions(+), 1 deletion(-) create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.c create mode 100644 bsp/novosns/ns800/libraries/HAL_Drivers/drivers/drv_rtc.h create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.c create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/ds1307.h create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.c create mode 100644 bsp/novosns/ns800/ns800rt7p65-nssinepad/board/port/soft_rtc.h 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 +#include +#include +#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 +#include + +#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 +#include +#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 = ® + + 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 +#include + +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 +#include +#include +#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 +#include + +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__ */ +