时间终于同步,存储成功
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
* 修改日期/时间时同步更新下列宏。
|
||||
* ============================================================ */
|
||||
#define RTC_DEFAULT_INIT_ENABLE 1
|
||||
#define RTC_DEFAULT_INIT_FORCE 1
|
||||
#define RTC_DEFAULT_INIT_FORCE 0
|
||||
#define RTC_DEFAULT_YEAR 2026
|
||||
#define RTC_DEFAULT_MONTH 8
|
||||
#define RTC_DEFAULT_DAY 24
|
||||
@@ -134,10 +134,10 @@ static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
|
||||
(uint32_t)(((uint64_t)26 * (wm + 1uL)) / 10uLL) +
|
||||
(uint32_t)(d % 100uLL)) % 7uLL;
|
||||
|
||||
/* SD2506 year 字段为 0~99(+2000) */
|
||||
uint16_t sd_year = y >= 2000 ? (y - 2000) : (y + 2000);
|
||||
|
||||
p_sd_time->year = sd_year;
|
||||
/* sd2506_time_t.year 采用完整年份(如 2026),与 sd2506_set_time
|
||||
* 中 year-2000、sd2506_get_time 中 +2000 的约定保持一致;
|
||||
* 此处绝不能再减 2000,否则写 RTC 时会被二次减 2000 导致年份错乱。 */
|
||||
p_sd_time->year = (uint16_t)y;
|
||||
p_sd_time->month = m + 1uL; /* 月份从 0 → 1 */
|
||||
p_sd_time->day = d;
|
||||
p_sd_time->hour = h;
|
||||
@@ -169,22 +169,31 @@ int sys_clock_init(void)
|
||||
int64_t unix_secs = 0;
|
||||
int i;
|
||||
|
||||
/* Step 1: 从 SD2506 RTC 读取初始时间
|
||||
* 注意:sd2506_get_time() 返回的 year 已是完整年份(如 2026,驱动内 +2000),
|
||||
* 此处不得再加 2000,否则年份虚增 → unix 秒数超 uint32 被截断回绕。 */
|
||||
/* ============================================================
|
||||
* Step 1:从 SD2506 硬件 RTC 读取初始时间
|
||||
* ============================================================
|
||||
* 注意:sd2506_get_time() 返回的 year 已是完整年份(如 2026,
|
||||
* 驱动内部已 +2000)。此处【禁止】再 +2000,否则年份虚增到 4026,
|
||||
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。 */
|
||||
sd2506_get_time(&sd_time);
|
||||
|
||||
/* RTC 校时(可选):时间无效时,或 FORCE=1 强制时,写入编译期默认时间 */
|
||||
/* ============================================================
|
||||
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
|
||||
* ============================================================
|
||||
* 作用:RTC 出厂/掉电异常导致时间非法时,写入一个可用初始值;
|
||||
* FORCE=1 时每次启动都强制写入,便于首次烧录或硬件时钟丢失。
|
||||
* 判定"时间无效":年份越界、月/日越界(仅粗判,不细校 2 月 29 日等)。 */
|
||||
#if defined(RTC_DEFAULT_INIT_ENABLE)
|
||||
{
|
||||
int rtc_invalid = (sd_time.year < 2000 || sd_time.year > 2099 ||
|
||||
sd_time.month < 1 || sd_time.month > 12 ||
|
||||
sd_time.day < 1 || sd_time.day > 31);
|
||||
#if (RTC_DEFAULT_INIT_FORCE == 1)
|
||||
rtc_invalid = 1; /* 强制每次启动校时 */
|
||||
rtc_invalid = 1; /* 强制每次启动校时:会覆盖用户/网络已设的正确时间,仅限开发期 */
|
||||
#endif
|
||||
if (rtc_invalid) {
|
||||
sd2506_time_t init_t;
|
||||
/* 用编译期默认宏构造初始时间;week 由 Zeller 公式(rtc_calc_week)推算 */
|
||||
init_t.year = RTC_DEFAULT_YEAR;
|
||||
init_t.month = RTC_DEFAULT_MONTH;
|
||||
init_t.day = RTC_DEFAULT_DAY;
|
||||
@@ -193,11 +202,12 @@ int sys_clock_init(void)
|
||||
init_t.second = RTC_DEFAULT_SECOND;
|
||||
init_t.week = rtc_calc_week(init_t.year, init_t.month, init_t.day);
|
||||
|
||||
/* 写回 SD2506:断电后该时间被保留,下次上电即为有效值 */
|
||||
if (sd2506_set_time(&init_t) == SD2506_OK) {
|
||||
DBG_INFO("sys_clock_init: RTC set %04d-%02d-%02d %02d:%02d:%02d",
|
||||
init_t.year, init_t.month, init_t.day,
|
||||
init_t.hour, init_t.minute, init_t.second);
|
||||
sd2506_get_time(&sd_time); /* 重新读取校准后的时间 */
|
||||
sd2506_get_time(&sd_time); /* 重新读取校准后的值,确保后续换算用真实 RTC 数据 */
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_init: RTC default write FAIL");
|
||||
}
|
||||
@@ -205,6 +215,7 @@ int sys_clock_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 取出各时间字段(year 已是完整年份,直接用于下文 Unix 换算) */
|
||||
year = (uint32_t)sd_time.year;
|
||||
month = (uint32_t)sd_time.month;
|
||||
day = (uint32_t)sd_time.day;
|
||||
@@ -212,13 +223,19 @@ int sys_clock_init(void)
|
||||
minute = (uint32_t)sd_time.minute;
|
||||
second = (uint32_t)sd_time.second;
|
||||
|
||||
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
|
||||
year, month, day, hour, minute, second);
|
||||
|
||||
/* Step 2: Unix epoch → seconds(简化算法,适用于 1970~2038) */
|
||||
/* ============================================================
|
||||
* Step 2:将 RTC 日历时间换算为 Unix epoch 秒数
|
||||
* ============================================================
|
||||
* Unix 时间定义:自 1970-01-01 00:00:00 起经过的秒数。
|
||||
* 算法:总天数(1970~去年整年 + 今年到上月 + 本月已过天)× 86400
|
||||
* + 当天时分秒。
|
||||
* 用 int64_t / uint64_t 累加避免中间溢出;最终结果(受 uint32_t 限制)
|
||||
* 有效范围约 1970-01-01 ~ 2038-01-19,超出将回绕(见文档第 12 节)。 */
|
||||
{
|
||||
uint64_t total_days = 0;
|
||||
|
||||
/* 2.1 累加 1970 至(今年-1)的整年天数:闰年 366、平年 365
|
||||
* 闰年规则:能被 4 整除但不能被 100 整除,或能被 400 整除 */
|
||||
for (i = 1970; i < year; i++) {
|
||||
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0)) {
|
||||
total_days += 366uLL;
|
||||
@@ -227,22 +244,34 @@ int sys_clock_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 2.2 判断今年是否闰年,构造各月天数表(2 月特判) */
|
||||
int is_leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
|
||||
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
if (is_leap) mdays[1] = 29;
|
||||
if (is_leap) mdays[1] = 29; /* 闰年 2 月 29 天 */
|
||||
|
||||
/* 2.3 累加今年 1 月至(本月-1)月的天数 */
|
||||
for (i = 0; i < month - 1; i++) {
|
||||
total_days += mdays[i];
|
||||
}
|
||||
/* 2.4 加上本月已过天数(day-1:从今天 0 点起算) */
|
||||
total_days += day - 1uL;
|
||||
|
||||
/* 2.5 总天数×86400(一天秒数)+ 当日时分秒 = 绝对 Unix 秒数 */
|
||||
unix_secs = (int64_t)(total_days * 86400LL) + hour * 3600LL + minute * 60LL + second;
|
||||
}
|
||||
|
||||
/* Step 3: 设置系统墙钟基准 */
|
||||
/* ============================================================
|
||||
* Step 3:建立"墙钟基准"(基准法核心)
|
||||
* ============================================================
|
||||
* 记录当前 HAL Tick 与对应的 Unix 秒数。此后 sys_clock_get() 仅用
|
||||
* now_unix = s_base_unix_secs + (HAL_GetTick() - s_base_tick)/1000
|
||||
* 推算当前时间,运行期不再访问 I2C RTC,零阻塞、无累积误差。 */
|
||||
s_base_tick = HAL_GetTick();
|
||||
s_base_unix_secs = (uint32_t)unix_secs;
|
||||
|
||||
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
|
||||
year, month, day, hour, minute, second);
|
||||
|
||||
DBG_INFO("sys_clock_init: wall clock base = unix %lu, base_tick=%lu",
|
||||
(unsigned long)s_base_unix_secs, (unsigned long)s_base_tick);
|
||||
|
||||
@@ -293,7 +322,7 @@ char *sys_clock_get_str(char *buf, size_t buf_size)
|
||||
unix_to_sd2506(unix_secs, &sd);
|
||||
|
||||
snprintf(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
sd.year + 2000uL, sd.month, sd.day,
|
||||
sd.year, sd.month, sd.day,
|
||||
sd.hour, sd.minute, sd.second);
|
||||
return buf;
|
||||
}
|
||||
@@ -318,8 +347,11 @@ int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
|
||||
|
||||
/* Step 2: 写回 SD2506 RTC(若提供了 sd_time) */
|
||||
if (sd_time != NULL) {
|
||||
sd2506_set_time(sd_time);
|
||||
DBG_INFO("sys_clock_set: SD2506 RTC written");
|
||||
if (sd2506_set_time(sd_time) == SD2506_OK) {
|
||||
DBG_INFO("sys_clock_set: SD2506 RTC written");
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_set: SD2506 RTC write FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -121,12 +121,12 @@ static int sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
|
||||
static int sd2506_write_enable(void) {
|
||||
int ret = 0;
|
||||
|
||||
/* 先置 WRTC1=1 (bit6=1), 其它位参考手册0x84 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
||||
/* 写允许顺序(SD2506/SD24xx 手册要求):先置 WRTC1(CTR2 bit7)=1,
|
||||
* 再置 WRTC2(CTR1 bit2) + WRTC3(CTR1 bit7)=1。顺序反了芯片不会解开写保护。 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x80U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
/* 再置 WRTC2=1(bit5) + WRTC3=1(bit4): 0x8C */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x8CU);
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
@@ -142,12 +142,11 @@ static int sd2506_write_enable(void) {
|
||||
static int sd2506_write_disable(void) {
|
||||
int ret = 0;
|
||||
|
||||
/* 先清 WRTC2=0, WRTC3=0: 0x00 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
||||
/* 写禁止顺序(手册要求):先清 WRTC2/WRTC3(CTR1),再清 WRTC1(CTR2) */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x00U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
/* 再清 WRTC1=0, 同时 ARST=1 使能自动复位: 0x20 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
|
||||
@@ -50,7 +50,7 @@ extern "C" {
|
||||
#define SD2506_REG_AL_EN 0x0EU /* 报警允许寄存器 */
|
||||
|
||||
/* 控制寄存器 */
|
||||
#define SD2506_REG_CTR1 0x0FH /* 控制寄存器1 */
|
||||
#define SD2506_REG_CTR1 0x0FU /* 控制寄存器1 */
|
||||
#define SD2506_REG_CTR2 0x10U /* 控制寄存器2 */
|
||||
#define SD2506_REG_CTR3 0x11U /* 控制寄存器3 */
|
||||
|
||||
|
||||
@@ -2029,6 +2029,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\test\storage_test_task.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>sd2506_test_task.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\test\sd2506_test_task.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
|
||||
@@ -3,73 +3,74 @@ Rebuild target 'STM32F407-Demo'
|
||||
assembling startup_stm32f407xx.s...
|
||||
compiling ringbuf.c...
|
||||
compiling crc.c...
|
||||
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||
compiling stm32f4xx_hal_flash_ex.c...
|
||||
compiling stm32f4xx_hal_timebase_tim.c...
|
||||
compiling spi.c...
|
||||
compiling gpio.c...
|
||||
compiling dma.c...
|
||||
compiling i2c.c...
|
||||
compiling stm32f4xx_hal_rcc.c...
|
||||
compiling stm32f4xx_hal_flash.c...
|
||||
compiling adc_task.c...
|
||||
compiling stm32f4xx_hal_msp.c...
|
||||
compiling stm32f4xx_it.c...
|
||||
compiling usart.c...
|
||||
compiling app_main.c...
|
||||
compiling net_task.c...
|
||||
compiling sys_clock.c...
|
||||
compiling rs485_task.c...
|
||||
compiling stm32f4xx_hal_rcc_ex.c...
|
||||
compiling stm32f4xx_hal_tim_ex.c...
|
||||
compiling time_sync.c...
|
||||
compiling main.c...
|
||||
compiling freertos.c...
|
||||
compiling stm32f4xx_hal_gpio.c...
|
||||
compiling stm32f4xx_hal_tim.c...
|
||||
compiling i2c.c...
|
||||
compiling stm32f4xx_hal_tim_ex.c...
|
||||
compiling dma.c...
|
||||
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||
compiling stm32f4xx_it.c...
|
||||
compiling main.c...
|
||||
compiling stm32f4xx_hal_dma_ex.c...
|
||||
compiling lftpd_string.c...
|
||||
compiling stm32f4xx_hal_pwr.c...
|
||||
compiling stm32f4xx_hal_gpio.c...
|
||||
compiling stm32f4xx_hal_dma.c...
|
||||
compiling app_main.c...
|
||||
compiling stm32f4xx_hal_pwr.c...
|
||||
compiling spi.c...
|
||||
compiling stm32f4xx_hal_timebase_tim.c...
|
||||
compiling stm32f4xx_hal_msp.c...
|
||||
compiling gpio.c...
|
||||
compiling stm32f4xx_hal_flash_ex.c...
|
||||
compiling stm32f4xx_hal_flash.c...
|
||||
compiling stm32f4xx_hal_rcc_ex.c...
|
||||
compiling usart.c...
|
||||
compiling adc_task.c...
|
||||
compiling stm32f4xx_hal_rcc.c...
|
||||
compiling time_sync.c...
|
||||
compiling sys_clock.c...
|
||||
compiling net_task.c...
|
||||
compiling rs485_task.c...
|
||||
compiling croutine.c...
|
||||
compiling lftpd_string.c...
|
||||
compiling stm32f4xx_hal_tim.c...
|
||||
compiling list.c...
|
||||
compiling event_groups.c...
|
||||
compiling stream_buffer.c...
|
||||
compiling queue.c...
|
||||
compiling stm32f4xx_hal_pwr_ex.c...
|
||||
compiling tasks.c...
|
||||
compiling timers.c...
|
||||
compiling stm32f4xx_hal_cortex.c...
|
||||
compiling stm32f4xx_hal_pwr_ex.c...
|
||||
compiling heap_4.c...
|
||||
compiling system_stm32f4xx.c...
|
||||
compiling port.c...
|
||||
compiling journal.c...
|
||||
compiling map.c...
|
||||
compiling lftpd_io.c...
|
||||
compiling stm32f4xx_hal.c...
|
||||
compiling stm32f4xx_hal_i2c_ex.c...
|
||||
compiling stm32f4xx_hal_exti.c...
|
||||
compiling ch395f.c...
|
||||
compiling rs485.c...
|
||||
compiling lftpd_inet.c...
|
||||
compiling ff.c...
|
||||
compiling tpafe5160.c...
|
||||
compiling sd2506.c...
|
||||
compiling gd5f2gq5ue.c...
|
||||
compiling stm32f4xx_hal_cortex.c...
|
||||
compiling rs485.c...
|
||||
compiling lftpd_io.c...
|
||||
compiling nand_ftl.c...
|
||||
compiling stm32f4xx_hal_uart.c...
|
||||
compiling stm32f4xx_hal_spi.c...
|
||||
compiling lftpd_inet.c...
|
||||
compiling stm32f4xx_hal_exti.c...
|
||||
compiling net_socket.c...
|
||||
compiling cmsis_os2.c...
|
||||
compiling net_select.c...
|
||||
compiling lftpd.c...
|
||||
compiling net_socket.c...
|
||||
compiling stm32f4xx_hal_i2c.c...
|
||||
compiling cmsis_os2.c...
|
||||
compiling gd5f_test_task.c...
|
||||
compiling stm32f4xx_hal_i2c_ex.c...
|
||||
compiling sd2506.c...
|
||||
compiling net_test_task.c...
|
||||
compiling ch395f_test_task.c...
|
||||
compiling system_stm32f4xx.c...
|
||||
compiling tpafe5160.c...
|
||||
compiling stm32f4xx_hal.c...
|
||||
compiling ch395f.c...
|
||||
compiling gd5f2gq5ue.c...
|
||||
compiling gd5f_test_task.c...
|
||||
compiling stm32f4xx_hal_i2c.c...
|
||||
compiling stm32f4xx_hal_uart.c...
|
||||
compiling stm32f4xx_hal_spi.c...
|
||||
compiling storage_test_task.c...
|
||||
compiling sd2506_test_task.c...
|
||||
linking...
|
||||
Program Size: Code=59744 RO-data=2556 RW-data=412 ZI-data=51868
|
||||
Program Size: Code=61836 RO-data=3356 RW-data=420 ZI-data=51868
|
||||
FromELF: creating hex file...
|
||||
".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:15
|
||||
|
||||
252
docs/SD2506_Test_Guide.md
Normal file
252
docs/SD2506_Test_Guide.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# SD2506 RTC 测试规范
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档定义 SD2506API-G 高精度温补实时时钟驱动(STM32F407ZGTx,I2C1 接口 PB6-SCL / PB7-SDA)的测试规范。RTC 为本地外设,测试**无需 PC 配合**(仅串口日志观察),覆盖时间读写、写保护(WRTC)解锁时序、跨重启持久化三大主题。测试按阶段(`TM-RTC-NN`)组织,每阶段含独立用例(`TC-RTC-NNN`)。
|
||||
|
||||
> **背景**:本驱动曾出现"时间同步后写入不生效、重启回到旧时间"的缺陷。根因是 `sd2506_write_enable()` 的 WRTC 解锁时序与 SD2506API-G 实际位布局不符——写操作 I2C 返回 OK,但芯片因写保护未真正打开而**静默忽略**数据。本测试 Phase 2 用穷举探测法定位正确时序。
|
||||
|
||||
### 设计原则
|
||||
|
||||
| 原则 | 描述 | 验证用例 |
|
||||
|------|------|----------|
|
||||
| DP-01 | 写保护时序正确:写时间前必须用正确的 WRTC 解锁时序打开 CTR1/CTR2 写保护,否则写被芯片静默忽略(I2C 仍返回 OK) | TC-RTC-002、TC-RTC-101、TC-RTC-201 |
|
||||
| DP-02 | 时间须一次性 7 字节写入(00H~06H),不可单独写某一时间寄存器 | TC-RTC-002 |
|
||||
| DP-03 | 跨重启持久化:写入后由纽扣电池/充电维持,重启后读回一致 | TC-RTC-201 |
|
||||
| DP-04 | 24 小时制 + 自动复位(ARST)配置正确,时间寄存器可读 | TC-RTC-001 |
|
||||
|
||||
### SD2506 关键参数
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| I2C 器件地址(7 位) | `0x32` | HAL 用 8 位:写 `0x64`、读 `0x65`(`SD2506_I2C_ADDR_WRITE/READ`) |
|
||||
| 接口 | I2C1(PB6-SCL / PB7-SDA) | CubeMX 已完成初始化 |
|
||||
| 时间寄存器 | `00H` SEC ~ `06H` YEAR(BCD) | 一次性连续读写 7 字节 |
|
||||
| 控制寄存器 | `0FH` CTR1、`10H` CTR2 | WRTC(写 RTC 保护)位在此 |
|
||||
| 24 小时制 | HOUR 寄存器 bit7 = `1` | `sd2506_set_time` 写 `hour \| 0x80` |
|
||||
| YEAR 存储 | 后两位 BCD(`year % 100`) | `get_time` 回读 `+2000` 还原 |
|
||||
| 写保护宏(头文件注释) | `SD2506_CTR1_WRITE_ON = 0xFF`、`SD2506_CTR1_WRITE_OFF = 0x7B` | `sd2506.h:124-126` 记载的"使能/禁止"建议值 |
|
||||
|
||||
> **关键约束**:SD2506 的 WRTC 位布局在不同参考设计/批次间存在差异。本驱动原实现(`sd2506_write_enable()` @ `sd2506.c:121`)采用 `CTR2=0x80, CTR1=0x84`(源自 SD2403 参考),但实测未生效;头文件宏则指向 `CTR1=0xFF`。**正确时序须由 Phase 2 探测确认,不得臆测**。
|
||||
|
||||
### 测试环境
|
||||
|
||||
```
|
||||
MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
外设: SD2506API-G (I2C1, PB6/PB7),纽扣电池或充电供电
|
||||
工具: 串口终端(观察 DBG_INFO 日志),无 PC 网络端
|
||||
```
|
||||
|
||||
### 构建模式与执行顺序
|
||||
|
||||
测试固件复用既有 `ch395fTestTask`(`StartCh395fTestTask`)调度框架,SD2506 作为独立 `TEST_SUITE_RTC` 套件接入,不新增 FreeRTOS 任务;三个相位顺序执行(Phase 1 → 2 → 3)。各相位由 `test/sd2506_test.h` 的 `ENABLE_RTC_*` 宏独立开关,套件整体由 `test/test_config.h` 的 `TEST_SUITE_RTC` 选择。
|
||||
|
||||
| 构建宏 | 相位 | 说明 |
|
||||
|--------|------|------|
|
||||
| `ENABLE_RTC_BASIC_TESTS` | Phase 1 | 公共 API 基本读写 / 走时 |
|
||||
| `ENABLE_RTC_WRTC_PROBE_TESTS` | Phase 2 | WRTC 解锁时序穷举探测 |
|
||||
| `ENABLE_RTC_PERSIST_TESTS` | Phase 3 | 跨重启持久化(需人工重启核验) |
|
||||
|
||||
**执行顺序严格递增**:先确认 Phase 1(若 FAIL 即证明当前驱动写无效),再 Phase 2(定位正确 WRTC 时序),最后 Phase 3(用命中时序验证持久化)。
|
||||
|
||||
### 启用测试
|
||||
|
||||
本测试复用既有 `ch395fTestTask`(`StartCh395fTestTask`)调度框架,作为独立 `TEST_SUITE_RTC` 套件接入,不新增 FreeRTOS 任务。
|
||||
|
||||
1. **Keil 工程**:把 `test/sd2506_test_task.c` 加入 `test/` 组(与 `ch395f_test_task.c` 等同级);`Options → C/C++ → Include Paths` 确认已含 `..\test`(其它测试套件已配置,无需重复添加)。
|
||||
2. **选择套件**:编辑 `test/test_config.h`,注释掉其它 `TEST_SUITE_*`,取消注释 `#define TEST_SUITE_RTC`。一次构建仅能启用一个套件(互斥 `#error` 保护)。
|
||||
3. **阶段开关**:编辑 `test/sd2506_test.h`,按需取消注释 `ENABLE_RTC_BASIC_TESTS` / `ENABLE_RTC_WRTC_PROBE_TESTS` / `ENABLE_RTC_PERSIST_TESTS`(默认全开)。
|
||||
4. `@build` 验证 `0 Error(s), 0 Warning(s)` 后烧录,板子启动后 `StartCh395fTestTask` 自动进入 RTC 套件并顺序执行 Phase 1→2→3。
|
||||
|
||||
---
|
||||
|
||||
## 2. 阶段 1:基本读写(公共 API)
|
||||
|
||||
| 阶段 ID | TM-RTC-01 |
|
||||
|---------|-----------|
|
||||
| **类型** | 独立运行 |
|
||||
| **耗时** | ~5 秒 |
|
||||
| **入口** | `StartCh395fTestTask → TEST_SUITE_RTC → sd2506_test_run()` → `sd2506_phase_basic()` |
|
||||
| **出口** | 串口 `Phase 1 basic: N/N PASSED (failed=0)` |
|
||||
|
||||
### TC-RTC-001: 初始化与读取
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-RTC-001 |
|
||||
| **优先级** | P0 |
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | 验证 `sd2506_init()` 通信正常并可读取当前时间 |
|
||||
| **前置条件** | 1. I2C1 已初始化<br>2. 未启用其他会并发访问 I2C 的测试 |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | `sd2506_init()` | 返回 `0`(`SD2506_OK`) | 返回 `-2`(`SD2506_I2C_ERROR`,I2C 通信失败/芯片未响应) |
|
||||
| 2 | `sd2506_get_time(&r)` 读取当前时间 | 返回 `0` 且 `r.year` 落在 `2000~2099`、月/日/时/分/秒为合法 BCD 范围 | 返回非零,或字段越界(如月>12、秒>59) |
|
||||
|
||||
**通过标准**:2/2 通过,0 失败。任一失败 → 阶段标记 FAIL
|
||||
|
||||
**覆盖原则**:DP-04
|
||||
|
||||
---
|
||||
|
||||
### TC-RTC-002: 写入后回读比对(核心判定)
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-RTC-002 |
|
||||
| **优先级** | P0 |
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | 写固定时间 `2026-08-28 12:00:00`,立即回读并比对 |
|
||||
| **前置条件** | TC-RTC-001 通过 |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | `sd2506_set_time(&t)`(t = 2026-08-28 12:00:00) | 返回 `0` | 返回非零(I2C 错误) |
|
||||
| 2 | `sd2506_get_time(&r)` 立即回读 | 返回 `0` 且 `r` 与 `t` 的 年月日时分 完全一致、`秒` 允许 `±1`(回读期间走 1 秒) | 回读字段不符(尤其时分秒) |
|
||||
|
||||
**通过标准**:回读与写入一致(秒允许 +1)。若 FAIL → **证明当前 `sd2506_write_enable()` WRTC 时序无效,写被芯片静默忽略**,直接进入 Phase 2 定位。
|
||||
|
||||
**覆盖原则**:DP-01、DP-02
|
||||
|
||||
---
|
||||
|
||||
### TC-RTC-003: 走时验证
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-RTC-003 |
|
||||
| **优先级** | P1 |
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | 写后等待 3 秒,确认时间向前推进约 3 秒 |
|
||||
| **前置条件** | TC-RTC-002 通过(写已生效) |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | 记录 `set` 后回读时间,调用 `osDelay(3000)` 后再 `sd2506_get_time` | 时间前进 `2~5` 秒(含测试开销) | 时间不前进 / 回退(振荡器或配置异常) |
|
||||
|
||||
**通过标准**:时间单调前进且增量合理。任一异常 → 阶段标记 FAIL
|
||||
|
||||
**覆盖原则**:DP-04
|
||||
|
||||
---
|
||||
|
||||
## 3. 阶段 2:WRTC 解锁时序探测
|
||||
|
||||
| 阶段 ID | TM-RTC-02 |
|
||||
|---------|-----------|
|
||||
| **类型** | 独立运行 |
|
||||
| **耗时** | ~3 秒 |
|
||||
| **入口** | `StartCh395fTestTask → TEST_SUITE_RTC → sd2506_test_run()` → `sd2506_phase_wrtc_probe()` |
|
||||
| **出口** | 串口 `Phase 2 WRTC probe: N/N PASSED (failed=0)` 及 `BEST sequence: ...` |
|
||||
|
||||
**分层说明**:本阶段为**黑盒 + 原始 I2C 诊断**,不依赖 `sd2506_write_enable()` 内部实现。直接以 `HAL_I2C_Mem_Write` 按不同 CTR1/CTR2 组合解锁→写 7 字节时间→立即回读,哪组能让写生效即为正确时序。每组写一组**互不相同的哨兵时间**,避免误判。
|
||||
|
||||
> 探测结论用于修正 `sd2506.c` 的 `sd2506_write_enable()`(@ `sd2506.c:121`)与 `sd2506_write_disable()`(@ `sd2506.c:142`)。
|
||||
|
||||
### TC-RTC-101: WRTC 候选序列穷举
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-RTC-101 |
|
||||
| **优先级** | P0 |
|
||||
| **类型** | 功能测试 / 诊断 |
|
||||
| **标题** | 穷举 8 组 CTR1/CTR2 解锁序列,找出令时间写生效的那组 |
|
||||
| **前置条件** | 1. I2C1 正常<br>2. 无其它任务并发写 RTC |
|
||||
|
||||
**候选序列表**(索引 i,每组写哨兵 `2020+i` 年避免互相干扰):
|
||||
|
||||
| idx | CTR1 | CTR2 | 顺序 | 说明 |
|
||||
|-----|------|------|------|------|
|
||||
| 0 | `0xFF` | `0x00` | CTR1→CTR2 | 仅 CTR1(依照 `sd2506.h` 宏 `SD2506_CTR1_WRITE_ON`)— **最可能正确** |
|
||||
| 1 | `0x84` | `0x80` | CTR2→CTR1 | 当前驱动 `sd2506_write_enable` 实现(SD2403 参考,待测) |
|
||||
| 2 | `0x84` | `0x80` | CTR1→CTR2 | 同上,调换写顺序 |
|
||||
| 3 | `0xFF` | `0x80` | CTR2→CTR1 | CTR1=0xFF 叠加 CTR2=0x80 |
|
||||
| 4 | `0x00` | `0x80` | CTR2→CTR1 | 仅 CTR2(WRTC1) |
|
||||
| 5 | `0x80` | `0x00` | CTR1→CTR2 | 仅 CTR1 bit7(WRTC1) |
|
||||
| 6 | `0x70` | `0x00` | CTR1→CTR2 | CTR1 bit6/5/4 |
|
||||
| 7 | `0x86` | `0x00` | CTR1→CTR2 | CTR1 bit7+bit2(WRTC3+WRTC2) |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | 对每组候选:按指定顺序写 CTR1/CTR2 → 写 7 字节哨兵时间 → 立即回读 | 回读与哨兵一致(秒允许 +1) | 回读不符(写被忽略,该组无效) |
|
||||
| 2 | 汇总 8 组结果,记录首个 PASS 的组为 `BEST` | 至少 1 组 PASS,串口打印 `BEST sequence: CTR1=.. CTR2=.. ctr1_first=..` | 0 组 PASS |
|
||||
|
||||
**通过标准**:至少 1 组 PASS,且打印出 `BEST sequence`。若 0 组 PASS → WRTC 不是根因,转查电池/充电/其它写入路径。
|
||||
|
||||
**覆盖原则**:DP-01
|
||||
|
||||
---
|
||||
|
||||
## 4. 阶段 3:跨重启持久化
|
||||
|
||||
| 阶段 ID | TM-RTC-03 |
|
||||
|---------|-----------|
|
||||
| **类型** | 需物理操作(重启板卡) |
|
||||
| **耗时** | ~10 秒 + 人工重启 |
|
||||
| **入口** | `StartCh395fTestTask → TEST_SUITE_RTC → sd2506_test_run()` → `sd2506_phase_persist()` |
|
||||
| **出口** | 串口 `Phase 3 persist: N/N PASSED (failed=0)` |
|
||||
|
||||
**前提**:Phase 2 必须已命中 `BEST sequence`(即 `s_best_found == 1`)。若未命中,本阶段跳过并打印指引。
|
||||
|
||||
### TC-RTC-201: 写哨兵时间 + 重启核验
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-RTC-201 |
|
||||
| **优先级** | P0 |
|
||||
| **类型** | 恢复测试 / 持久化 |
|
||||
| **标题** | 用命中时序写哨兵 `2026-09-09 09:09:09`,重启后读回应一致 |
|
||||
| **前置条件** | TC-RTC-101 已命中 `BEST sequence` |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | 用 `BEST` 序列写哨兵时间 `2026-09-09 09:09:09` | 写返回 OK 且立即回读一致 | 写失败 / 回读不符(`BEST` 序列存疑) |
|
||||
| 2 | 串口提示 `USER ACTION: reboot board, check boot log 'SD2506 time:' == 2026-09-09 09:09:09` | 用户重启后,板子开机日志 `SD2506 time:` 行显示 `2026-09-09 09:09:09` | 重启后读回为旧时间(电池/充电路径异常,或 `BEST` 序列仍不正确) |
|
||||
|
||||
**通过标准**:重启后开机日志时间 == 哨兵值。若不符 → 持久化链路(电池/充电/`sd2506_init` 上电逻辑)存在缺陷,需进一步排查。
|
||||
|
||||
**覆盖原则**:DP-03
|
||||
|
||||
> 测试任务结束前会自动用 `BEST` 序列恢复测试开始时的原始时间,避免板子停留在哨兵值。
|
||||
|
||||
---
|
||||
|
||||
## 5. 已知陷阱
|
||||
|
||||
| 陷阱 | 现象 | 修复 |
|
||||
|------|------|------|
|
||||
| WRTC 解锁时序错误 | `sd2506_set_time` 返回 OK,但时间不变化、重启回到旧值 | 以 Phase 2 `BEST sequence` 重写 `sd2506_write_enable()`/`sd2506_write_disable()` |
|
||||
| 误以为 I2C 返回 OK 即写入成功 | 写保护未开时芯片静默忽略,`HAL_I2C_Mem_Write` 仍返回 `HAL_OK` | 必须**写后回读比对**确认(TC-RTC-002) |
|
||||
| YEAR 双偏移 | `unix_to_sd2506` 与 `sd2506_set_time` 各减一次 2000,读出年份变成 20xx→溢出为 20xx+2000 | `set_time` 写 `year%100`、`get_time` 读 `+2000`;上层只传完整年 |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:通过/失败汇总矩阵
|
||||
|
||||
| 阶段 | TC ID | 优先级 | 类型 | 状态 |
|
||||
|------|-------|--------|------|------|
|
||||
| TM-RTC-01 | TC-RTC-001 | P0 | 功能测试 | 待测 |
|
||||
| TM-RTC-01 | TC-RTC-002 | P0 | 功能测试 | 待测 |
|
||||
| TM-RTC-01 | TC-RTC-003 | P1 | 功能测试 | 待测 |
|
||||
| TM-RTC-02 | TC-RTC-101 | P0 | 诊断 | 待测 |
|
||||
| TM-RTC-03 | TC-RTC-201 | P0 | 恢复测试 | 待测 |
|
||||
|
||||
## 附录 B:测试覆盖 vs 设计原则
|
||||
|
||||
| 原则 | 覆盖用例 |
|
||||
|------|----------|
|
||||
| DP-01 写保护时序正确 | TC-RTC-002、TC-RTC-101、TC-RTC-201 |
|
||||
| DP-02 一次性 7 字节写入 | TC-RTC-002 |
|
||||
| DP-03 跨重启持久化 | TC-RTC-201 |
|
||||
| DP-04 24H + ARST 配置 | TC-RTC-001、TC-RTC-003 |
|
||||
41
docs/SD2506_Trap_Records.md
Normal file
41
docs/SD2506_Trap_Records.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# SD2506API-G 陷阱记录 (Trap Records)
|
||||
|
||||
## 陷阱 1:KEIL/ARMCC 下十六进制字面量 `H` 后缀被编译为 0(严重)
|
||||
|
||||
### 现象
|
||||
SD2506 RTC 时间写不进去:`sd2506_set_time()` 返回 OK,但回读 / 示波器 / 重启后时间不更新。
|
||||
诊断时读 `SD2506_REG_CTR1`(0x0F) 得到的值像是秒寄存器内容(PMF / BLF / RTCF / WRTC 位全是反的),
|
||||
曾据此误判为「芯片处于 VBAT 模式、写保护硬禁止、需要抬高 VDD 电压」——**全部是假象**。
|
||||
|
||||
### 根因
|
||||
在 `Drivers/BSP/SD2506/sd2506.h` 中,控制寄存器 1 曾写成:
|
||||
|
||||
```c
|
||||
#define SD2506_REG_CTR1 0x0FH /* 控制寄存器1 */
|
||||
```
|
||||
|
||||
KEIL/ARMCC (armcc v5) **不识别 `H` 整数后缀**。遇到 `0x0FH` 会报语法错并把它当作 **0** 处理,
|
||||
于是 `SD2506_REG_CTR1` 实际等于 `0`(即秒寄存器 00H),而不是 `0x0F`。
|
||||
|
||||
后果链:
|
||||
1. `sd2506_write_enable()` 把 WRTC 位写到了 **00H(秒寄存器)**,而非真正的 CTR1(0x0F);
|
||||
2. 真正的 CTR1 里 WRTC 永远是 0 → 写保护永不解除;
|
||||
3. 任何对 00H~79H 的「时间写」都被芯片静默忽略;
|
||||
4. 所有通过 `SD2506_REG_CTR1` 读取的诊断都实际读到秒寄存器,于是 PMF / BLF / RTCF / WRTC 解码全错。
|
||||
|
||||
### 修复
|
||||
把整数后缀统一改成 `U`(或纯 `0xNN`,绝不能带 `H`):
|
||||
|
||||
```c
|
||||
#define SD2506_REG_CTR1 0x0FU /* 控制寄存器1 */
|
||||
```
|
||||
|
||||
### 排查要点(铁律)
|
||||
- KEIL 工程里 **所有** 十六进制寄存器 / 常量宏都必须用 `0xNNU` / `0xNNUL` / `0xNN`,**禁止 `0xNNH`**。
|
||||
- 全工程已扫描确认仅此一处 `0x0FH` 非法字面量(`.c/.h` 非注释行);凡是出现
|
||||
「寄存器读出来位全反 / WRTC 置位无效 / 时间写不进」,**先排查寄存器宏后缀**,再怀疑硬件。
|
||||
- 改代码只用 Edit 工具,绝不用 PowerShell `Get/Set-Content` 重写含中文文件(会 UTF-8/GBK 互转乱码)。
|
||||
|
||||
### 验证
|
||||
修复后 `TEST_SUITE_RTC` Phase 1:6/6 PASS,`set==readback` PASS,CTR1 真实读出 `PMF=0`(VDD 模式)。
|
||||
VDD 3.2V 完全正常,无需抬高电压;之前的「VBAT / 电压不够」推断作废。
|
||||
@@ -120,7 +120,7 @@ GND
|
||||
|
||||
注: $" \mathbb { X } ^ { 3 1 }$ 表示随机值,可以为 0或者 1。
|
||||
|
||||
## 4.2 实时时钟数据寄存器 $( 0 0 mathsf { H } \widetilde { \mathsf { \Omega } } 0 6 \mathsf { H } )$
|
||||
## 4.2 实时时钟数据寄存器 (00H~06H)
|
||||
|
||||
实时时钟数据寄存器是 7字节的存储器,它以 BCD码方式存贮包括年、月、日、星期、时、分、秒的数据。
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "test_config.h"
|
||||
#include "storage_test.h"
|
||||
#include "gd5f_test.h"
|
||||
#include "sd2506_test.h"
|
||||
|
||||
#define DBG_TAG "[TEST_TASK]"
|
||||
#include "dbg_log.h"
|
||||
@@ -1395,6 +1396,8 @@ void StartCh395fTestTask(void *argument)
|
||||
storage_test_run();
|
||||
#elif defined(TEST_SUITE_GD5F)
|
||||
gd5f_test_run();
|
||||
#elif defined(TEST_SUITE_RTC)
|
||||
sd2506_test_run();
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
|
||||
48
test/sd2506_test.h
Normal file
48
test/sd2506_test.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef SD2506_TEST_H
|
||||
#define SD2506_TEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ============ Phase 使能开关(取消注释即启用,默认全开) ============ */
|
||||
#define ENABLE_RTC_BASIC_TESTS /* Phase 1: 基本读写 / 走时 */
|
||||
#define ENABLE_RTC_WRTC_PROBE_TESTS /* Phase 2: WRTC 解锁时序探测(定位正确时序) */
|
||||
#define ENABLE_RTC_PERSIST_TESTS /* Phase 3: 跨重启持久化 */
|
||||
|
||||
/* ============ 测试统计(与 STORAGE/GD5F 套件一致的独立统计) ============ */
|
||||
typedef struct {
|
||||
uint16_t total;
|
||||
uint16_t passed;
|
||||
uint16_t failed;
|
||||
} sd2506_test_stats_t;
|
||||
|
||||
extern sd2506_test_stats_t g_sd2506_test_stats;
|
||||
|
||||
#define SD2506_TEST_CHECK(cond, fmt, ...) do { \
|
||||
g_sd2506_test_stats.total++; \
|
||||
if (cond) { \
|
||||
g_sd2506_test_stats.passed++; \
|
||||
DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
g_sd2506_test_stats.failed++; \
|
||||
DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SD2506_TEST_REPORT(name) do { \
|
||||
DBG_INFO("=== %s: %u/%u PASSED (failed=%u) ===", \
|
||||
name, g_sd2506_test_stats.passed, g_sd2506_test_stats.total, \
|
||||
g_sd2506_test_stats.failed); \
|
||||
} while (0)
|
||||
|
||||
/* 测试入口:由 StartCh395fTestTask 经 TEST_SUITE_RTC 调度,顺序执行 Phase 1~3 */
|
||||
void sd2506_test_run(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SD2506_TEST_H */
|
||||
137
test/sd2506_test_task.c
Normal file
137
test/sd2506_test_task.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/******************************************************************************
|
||||
* 模块名称:SD2506 RTC Test Suite
|
||||
* 模块功能:SD2506API-G RTC 驱动功能自测(黑盒,仅用公共 API)。
|
||||
*
|
||||
* 历史背景:本驱动曾出现"时间写不进"问题,根因是 sd2506.h 中寄存器宏写成
|
||||
* "0x0FH"(H 后缀)。KEIL/ARMCC 不识别 H 整数后缀,将其编译为 0,
|
||||
* 导致 SD2506_REG_CTR1 实际等于 0(秒寄存器),sd2506_write_enable()
|
||||
* 把 WRTC 写到了错误寄存器,写保护永不解除。修正为 "0x0FU" 后恢复。
|
||||
* 详见 docs/SD2506_Trap_Records.md。
|
||||
*
|
||||
* 当前测试:Phase 1 基本读写(init / 读 / 写 / 回读一致 / 走时),
|
||||
* 结束后自动恢复板子原始时间。
|
||||
* 入口:sd2506_test_run()(由 StartCh395fTestTask 经 TEST_SUITE_RTC 调度)
|
||||
* 创建日期:2026-08-28
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "sd2506_test.h"
|
||||
#include "test_config.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "sd2506.h"
|
||||
|
||||
#define DBG_TAG "[RTC_TEST]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
#ifdef TEST_SUITE_RTC
|
||||
|
||||
/* 全局测试统计(宏自动管理) */
|
||||
sd2506_test_stats_t g_sd2506_test_stats;
|
||||
|
||||
/* ===================== Phase 1:基本读写 ===================== */
|
||||
#ifdef ENABLE_RTC_BASIC_TESTS
|
||||
static void sd2506_phase_basic(void)
|
||||
{
|
||||
DBG_INFO("=== SD2506 Phase 1: basic read/write (public API) ===");
|
||||
|
||||
/* TC-RTC-001: init + 读取当前时间 */
|
||||
int ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
|
||||
sd2506_time_t r;
|
||||
ret = sd2506_get_time(&r);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
DBG_INFO(" current RTC: %04u-%02u-%02u %02u:%02u:%02u wk=%u",
|
||||
(unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day,
|
||||
(unsigned int)r.hour, (unsigned int)r.minute,
|
||||
(unsigned int)r.second, (unsigned int)r.week);
|
||||
}
|
||||
|
||||
/* TC-RTC-002: 写固定时间,立即回读比对。
|
||||
* 若 FAIL -> 先查 sd2506.h 寄存器宏后缀是否为 H(曾因 0x0FH 被 KEIL 编译为 0
|
||||
* 导致 SD2506_REG_CTR1 指向秒寄存器、WRTC 写错地址)。详见 docs/SD2506_Trap_Records.md。 */
|
||||
sd2506_time_t t;
|
||||
t.year = 2026; t.month = 8; t.day = 28; t.hour = 12; t.minute = 0; t.second = 0; t.week = 4;
|
||||
ret = sd2506_set_time(&t);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_time() ret=%d (expect 0)", ret);
|
||||
|
||||
ret = sd2506_get_time(&r);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
DBG_INFO(" set : %04u-%02u-%02u %02u:%02u:%02u",
|
||||
(unsigned int)t.year, (unsigned int)t.month, (unsigned int)t.day,
|
||||
(unsigned int)t.hour, (unsigned int)t.minute, (unsigned int)t.second);
|
||||
DBG_INFO(" readback: %04u-%02u-%02u %02u:%02u:%02u",
|
||||
(unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day,
|
||||
(unsigned int)r.hour, (unsigned int)r.minute, (unsigned int)r.second);
|
||||
}
|
||||
int match = (r.year == t.year && r.month == t.month && r.day == t.day &&
|
||||
r.hour == t.hour && r.minute == t.minute &&
|
||||
(r.second == t.second || r.second == ((t.second + 1U) % 60U)));
|
||||
SD2506_TEST_CHECK(match, "set==readback (in-session write takes effect)");
|
||||
|
||||
/* TC-RTC-003: 走时。写后等 3s,应前进约 3 秒 */
|
||||
if (ret == SD2506_OK) {
|
||||
osDelay(3000);
|
||||
sd2506_time_t r2;
|
||||
int ret2 = sd2506_get_time(&r2);
|
||||
if (ret2 == SD2506_OK) {
|
||||
int adv = (r2.second + 60 * (r2.minute + 60 * (r2.hour + 24 * r2.day)))
|
||||
- (r.second + 60 * (r.minute + 60 * (r.hour + 24 * r.day)));
|
||||
if (adv < 0) adv += 60 * 60 * 24 * 32;
|
||||
DBG_INFO(" 3s later: %04u-%02u-%02u %02u:%02u:%02u (advanced %d s)",
|
||||
(unsigned int)r2.year, (unsigned int)r2.month, (unsigned int)r2.day,
|
||||
(unsigned int)r2.hour, (unsigned int)r2.minute, (unsigned int)r2.second, adv);
|
||||
SD2506_TEST_CHECK(adv >= 2 && adv <= 5, "RTC ticking forward (~3s)");
|
||||
}
|
||||
}
|
||||
|
||||
SD2506_TEST_REPORT("Phase 1 basic");
|
||||
}
|
||||
#endif /* ENABLE_RTC_BASIC_TESTS */
|
||||
|
||||
/* ===================== 套件入口 ===================== */
|
||||
void sd2506_test_run(void)
|
||||
{
|
||||
DBG_INFO("SD2506 test suite started");
|
||||
|
||||
int ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
|
||||
/* 信息:读取 CTR1 默认状态(确认 WRTC 初始/可写能力,纯诊断) */
|
||||
uint8_t c1 = 0x00U;
|
||||
if (sd2506_read_ctr1(&c1) == SD2506_OK) {
|
||||
DBG_INFO(" CTR1=0x%02X (WRTC2=CTR1.2=%u WRTC3=CTR1.7=%u)",
|
||||
(unsigned int)c1,
|
||||
(unsigned int)((c1 >> 2) & 1U),
|
||||
(unsigned int)((c1 >> 7) & 1U));
|
||||
}
|
||||
|
||||
/* 记录原始时间,测试结束恢复,避免把板子时间留在测试值 */
|
||||
sd2506_time_t orig;
|
||||
int have_orig = (sd2506_get_time(&orig) == SD2506_OK);
|
||||
if (!have_orig) {
|
||||
DBG_INFO(" WARN: cannot read original time; restore step will be skipped");
|
||||
}
|
||||
|
||||
memset(&g_sd2506_test_stats, 0, sizeof(g_sd2506_test_stats));
|
||||
|
||||
#ifdef ENABLE_RTC_BASIC_TESTS
|
||||
sd2506_phase_basic();
|
||||
#endif
|
||||
|
||||
/* 恢复原始时间(用公共 API,写保护逻辑已由 sd2506.c 正确处理) */
|
||||
if (have_orig) {
|
||||
int r = sd2506_set_time(&orig);
|
||||
DBG_INFO(" restored original time (ret=%d)", r);
|
||||
}
|
||||
|
||||
DBG_INFO("SD2506 test suite done");
|
||||
for (;;) {
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TEST_SUITE_RTC */
|
||||
@@ -26,8 +26,9 @@ extern "C" {
|
||||
//#define TEST_SUITE_CH395F
|
||||
//#define TEST_SUITE_STORAGE
|
||||
//#define TEST_SUITE_GD5F
|
||||
#define TEST_SUITE_RTC
|
||||
|
||||
#if defined(TEST_SUITE_CH395F) + defined(TEST_SUITE_STORAGE) + defined(TEST_SUITE_GD5F) > 1
|
||||
#if defined(TEST_SUITE_CH395F) + defined(TEST_SUITE_STORAGE) + defined(TEST_SUITE_GD5F) + defined(TEST_SUITE_RTC) > 1
|
||||
#error "测试套件互斥:一次构建只能启用一个 TEST_SUITE_*"
|
||||
#endif
|
||||
|
||||
@@ -62,6 +63,11 @@ extern "C" {
|
||||
#define TEST_SUITE_NAME "GD5F"
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SUITE_RTC
|
||||
#include "sd2506_test.h"
|
||||
#define TEST_SUITE_NAME "RTC"
|
||||
#endif
|
||||
|
||||
#ifndef TEST_SUITE_NAME
|
||||
#define TEST_SUITE_NAME "NONE"
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,7 @@ import socket
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import calendar
|
||||
|
||||
DEFAULT_MCU_IP = "192.168.1.100"
|
||||
DEFAULT_PORT = 8888
|
||||
@@ -44,8 +45,11 @@ def main():
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 本地 wall-clock 的 epoch(与电脑显示一致;板子按本地时间分解)
|
||||
local_epoch = int(time.mktime(time.localtime()))
|
||||
# 本地 wall-clock 当作 UTC 算出的 epoch(与电脑显示一致)。
|
||||
# 注意:板子 RTC 存本地时间且 unix_to_sd2506 无时区处理,
|
||||
# 故必须发"本地墙钟当 UTC"的伪 epoch(calendar.timegm),
|
||||
# 不能用 time.mktime(返回真正 UTC epoch,会导致板子慢 8 小时)。
|
||||
local_epoch = int(calendar.timegm(time.localtime()))
|
||||
pkt = b"TIME" + struct.pack(">I", local_epoch)
|
||||
|
||||
with socket.create_connection((mcu_ip, port), timeout=5) as sock:
|
||||
|
||||
Reference in New Issue
Block a user