核心问题:当看门狗复位发生时,RTC(实时时钟)和PMU TIMER(电源管理单元定时器)的硬件TICK会被重置为零。本文将重点放在RTC上。 相关技术依赖:
根据Reference Manual的描述: 看门狗的计数器是一个20位的递减计数器,其时钟源为RC32K,并且已经分频至128Hz(即看门狗的时钟周期为128Hz,大约等于7.8毫秒)。一旦启用看门狗,计数值降至0xa时,将触发看门狗中断函数。此时,代码的中断向量处理函数WDT_IRQHandler将被调用。随后,看门狗计数器继续递减,直至值为0,此时系统将执行复位。因此,从WDT_IRQHandler执行到系统复位,大约有78毫秒的时间可用于处理紧急事务。鉴于RC32K时钟的精度,建议将处理任务的时间限制在最短范围内,例如20至30毫秒内,以确保安全。 1 初始化看门狗并启用中断 // 已经在wdt_bkup.c/h中提供 void wdt_irq_enable(void) { // Enable WDT cpu en int REGW1(&OM_PMU->WDT_STATUS, PMU_WDT_STATUS_WDT_INT_CPU_EN_MASK); // Enable WDT NVIC int NVIC_ClearPendingIRQ(WDT_IRQn); NVIC_SetPriority(WDT_IRQn, RTE_WDT_IRQ_PRIORITY); NVIC_EnableIRQ(WDT_IRQn); } main.c ... int main(void) { ... drv_wdt_init(5000); wdt_irq_enable(); // 打开看门狗中断 ... ... } 2 利用看门狗实现快速复位,初始化值不宜过小,理论上不应低于86毫秒,以确保能够产生中断 #include "om_driver.h" #include "wdt_bkup.h" // 调用看门狗 void app_reset(void) { // 时间必须大于78.125ms,内部按照ms*128/1000进行取整,必须满足这个值计算出来大于0x0a // 比如要设置计数值位0x0b,那么反推计算(11*1000/128)=85.9375,向上取整为86 // 这里,故意让计数值比原理论值大于5, 那么(15*1000/128)=117.85≈118 drv_wdt_init(118); wdt_irq_enable(); // 必须重新调用 while(1); } 3 保留RAM区域 在看门狗复位时,RAM的前后各8KB区域将保持不变,而其他区域的内容在复位后会丢失。RAM后部的内容为系统保留。建议在RAM的起始前8KB区域中定义一段空间用于存储数据。 以Keil为例,通过修改link_flash.sct文件来实现。 4 修改链接脚本 以下为关键修改部分: 5 定义变量 // 魔术字用来标记当前区域保存的内容是否是手动保存 __attribute((section(".bss.NOINIT"))) volatile uint64_t backup_magic_word; // 定义RTC备份的变量值 __attribute((section(".bss.NOINIT"))) volatile uint32_t rtc_time_backup; // 定义其他值 __attribute((section(".bss.NOINIT"))) volatile uint32_t measure_start_backup; 6 在看门狗中断中保存变量 7 在main.c的早期阶段获取备份变量并清除魔术字 8 在初始化RTC时应用备份值 uint32_t rtc_val_init = 0; if (notinit_data_valid()) { rtc_val_init = rtc_time_backup; } no_init_clear_magic(); // 初始化RTC #include <time.h> om_error_t err = drv_rtc_init(OM_RTC); ... rtc_tm_t tm; time_t time_sec = rtc_val_init; memcpy(&tm, localtime(&time_sec), sizeof(rtc_tm_t)); drv_rtc_timer_set(OM_RTC, &tm); |
