1. 为什么断电后还要走时钟先把需求和坑位铺开先说说这件东西的实际用途。STM32的RTCReal-Time Clock就是一颗内置的实时时钟它能在主电源断电之后靠着VBAT引脚上外接的纽扣电池或者超级电容继续走时。这个功能在很多产品里是刚需最常见的场景就是电表、水表、考勤机、数据记录仪还有各类带时间戳的传感器节点。你想一下一个野外部署的采集设备主电源一断时间全部归零重新上电之后所有数据的时间戳全乱掉那这套系统的可用性就直接归零了。所以断电不停钟这个需求本质上不是“加个电池”这么简单而是要保证系统的“时间基准”在任何情况下都不丢失。做这个功能通常会面临几个让人头大的问题。一是VBAT电路设计不当电池在正常供电时被白白消耗二是RTC时钟源选择不对LSE外部晶振不起振或者频率偏差大导致一天能差出几十秒三是备份域寄存器在系统复位时被意外清零时间直接回退到2000年还有更隐蔽的问题比如读时间的时候把“进位”那一瞬间读到旧值导致出现过一分钟时间倒退、跳变之类的诡异现象。这些坑我在项目里基本都踩过一遍这篇就把硬件设计、代码配置、调试方法和实测心得都整理出来给大家一条能直接走通的路。如果你手上已经有了一块STM32的开发板或者正在做一个小型的数据记录类产品接下来这套东西可以直接抄作业。代码以STM32F103系列和HAL库为例但你只要理解了原理换到F4、L4、G0系列都只是改改时钟配置的问题。2. 电路层的事没做好代码写得再漂亮也是白搭很多人在VBAT这块栽跟头不是代码问题而是硬件电路设计的时候就埋了雷。先说清楚VBAT引脚的作用它专门给RTC和备份寄存器供电在VDD主电源掉电后VBAT的电压可以维持RTC继续走时同时保证备份寄存器里存的数据不丢失。注意VBAT不是给整个芯片供电的它的负载非常小在微安级别所以一颗小电池能用好几年这是设计这套机制的前提。2.1 VBAT引脚的供电切换逻辑STM32内部的电源结构是这样的正常情况下芯片由VDD供电RTC和备份域的电源来自VDD一旦VDD跌落到一定阈值以下芯片内部的电源开关会自动切换到VBAT由外部电池继续给备份域供电。这个切换是硬件自动完成的不需要代码参与但前提是VBAT引脚上必须有一个有效的备用电源。这里有个常见的错误设计有些人直接把VBAT悬空不接觉得不用RTC无所谓。结果就是RTC走时不稳定或者复位后备份寄存器数据全丢。实际上如果你不打算用RTC和备份寄存器STM32官方建议把VBAT直接接到VDD上如果你要用断电不停钟就必须接电池或者法拉电容。2.2 电池选型和放电回路保护VBAT电池最常用的是CR1220纽扣电池和超级电容。CR1220容量一般在40mAh左右工作电压3VRTC走时电流大约1~2uA算下来理论上能走两三年。实际应用中还要考虑电池自放电和温度影响保守估计一年半左右就要定期检查电压。超级电容的好处是可充电可以配合主电源做浮充但容量和漏电流要仔细算适合那种断电时间不会太长几天以内的场景。一个必须做的设计是电池防倒灌。VBAT和VDD之间有一个内部切换电路但外部如果走线不合理或者电池正极和VDD之间没有隔离正常供电时电池会被持续充电或者被电路反向消耗。常见的做法是在VBAT外部串联一个低压降二极管比如1N5819肖特基阳极端接电池阴极端接VBAT引脚防止主电源正常工作时电流倒灌进电池。不过要注意二极管本身的漏电流会影响电池寿命选型时优先选反向漏电流小的型号。2.3 锂电池和法拉电容的不同玩法如果你做的是充电型产品比如手持设备或者带USB供电的记录仪VBAT用可充电法拉电容是很好的方案。法拉电容容值大常见的是1F或更大的充满之后能维持RTC走时几天到几周。充电电路可以用二极管加限流电阻直接浮充简单粗暴但要注意法拉电容的自漏电和充电电流冲击限流电阻别省。如果使用充电锂电池比如那种小型的3.7V锂电加充电管理芯片就需要一个稳压器和合适的电压匹配因为STM32的VBAT最大允许电压是3.6V左右直接接3.7V满电的锂电池会超压。这个问题有些人会忽略结果芯片的备份域长期在超压状态下工作非常影响可靠性。3V纽扣电池需要三到五s用二极管连接、VBAT引脚对地加一个0.1uF瓷片电容去耦这个电容可以滤掉电源切换瞬间的毛刺对防止备份域数据错乱有实实在在的好处。PCB走线建议VBAT线路尽量短避免从大电流或高频信号线旁边穿过因为RTC走时是微安级的微弱电流线路上的任何干扰都可能影响时钟稳定性。3. RTC的底层原理搞不懂后面全是玄学RTC模块本身不复杂但牵扯到“时钟源”“分频”“备份寄存器”“复位域”这些概念如果不搞清楚配置时会一直处于一种半懂不懂的状态。我尽量把底层逻辑讲透把这些概念用大白话解释清楚。3.1 RTC的核心分频计数RTC的本质就是一个不断累加的计数器。STM32的RTC由两个分频器组成一个异步预分频器和一个同步预分频器常见配置是异步分频127、同步分频255两个分频器级联后把输入的32.768kHz时钟分频到1Hz也就是计数器每秒钟加1。为什么是32.768kHz因为它是2的15次方用15位分频器可以很均匀地分频误差可以做到标称值。分频之后RTC维护一组时间寄存器包括秒、分、时、日、星期、月、年还自动处理闰年和月份天数。你只需要把当前时间写入寄存器剩下的进位和日历计算全部由硬件完成。分频参数的选择影响时钟精度。32.768kHz的晶振通常是20ppm精度也就是每天约1.7秒的偏差。如果使用LSE外部晶振分频误差主要来自晶振本身的精度如果使用LSI内部RC振荡器频率受温度影响很大精度远差于外部晶振走一天偏几分钟都很正常。3.2 备份域和它的“特权机制”RTC、备份寄存器和电源控制相关的少量寄存器在STM32中被称为“备份域”。这个区域由VBAT供电在主电源掉电后依然可以保持数据和走时。备份域寄存器的访问有一个解锁机制防止程序跑飞时意外改写。你需要先在PWR寄存器中打开备份域访问权限然后才能操作RTC和备份寄存器。这个步骤漏了写操作会直接被忽略很多人调代码时发现寄存器写不进去问题多半出在这里。备份域寄存器的复位源也比较特殊。它不跟随系统复位、不跟随外部引脚复位只有以下情况才会清空备份域掉电、软件主动触发备份域复位以及芯片发生侵入检测事件。这意味着你按复位键重新启动程序RTC的时间和备份数据是保留的。这就需要特别注意你的代码里不能一启动就“重置RTC时间”否则用户只要复位一次时间就变成了你代码里写死的初始值这是非常常见的实现错误。3.3 RTC时钟源选型LSE、LSI还是HSESTM32的RTC时钟源有三类选错等于给后面的精度挖坑。LSE外部低速晶振通常是32.768kHz精度最高功耗也最低是绝大多数需要“长时间准”的项目的首选但代价是需要在PCB上放一颗晶振并且要正确配置晶振的驱动能力和负载电容。LSI内部低速RC振荡器F1系列大约40kHzF4/L4系列一般是32kHz不需要外部元件但精度很差适合对时间精度要求不高的场合。HSE经过分频后也可以做RTC时钟源因为HSE本身精度较高但有一个严重问题主电源掉电后HSE停振RTC也就没有时钟了所以HSE方案根本不支持“断电不停钟”只能用在不需要VBAT保持的场景。在实际项目中需要断电保持时间精度的直接选LSE只是需要一个计时功能、断电后重新上电时间不重要的才考虑LSI。我之前做过一个低功耗采集器主控是STM32L4为了省一颗晶振的成本和时间用LSI做RTC结果发现环境温度一变化一天能偏出几分钟最后只能老老实实改回LSE。3.4 一个重要的细节RTC校准LSE晶振本身的精度是固定的但晶振的负载电容误差、PCB走线寄生电容、芯片内部的晶振驱动电路等因素会导致最终的RTC频率和标称值有一点偏差。STM32提供了RTC校准功能可以通过调整同步分频器的参数或者使用校准寄存器在软件层面对频率偏差做补偿。校准的测量方法是用一个高精度的参考时钟比如GPS模块的PPS秒脉冲来对比RTC的走时误差然后反推校准值。对于一般产品如果每天误差在1~2秒以内用软件校准完全可以做到一个月误差不超过几秒。基础校准方法是在一个固定时间窗口内统计误差秒数计算出每日偏差再换算成校准寄存器的步进值。4. 从零配置RTC的完整过程照着做就行这一节把完整的RTC配置流程走一遍代码可以直接用每一步我都解释为什么这么写。以STM32F103C8T6最小系统板加HAL库为例IDE用Keil MDK。4.1 开启备份域访问权限这是操作RTC的第一步很多人漏掉导致后面所有写操作都不生效。__HAL_RCC_PWR_CLK_ENABLE(); __HAL_RCC_BKP_CLK_ENABLE(); // F1系列需要开启BKP时钟 HAL_PWR_EnableBkUpAccess(); // 解除备份域写保护在F1系列上PWR和BKP的时钟默认是关闭的必须先开启才能访问备份域寄存器。HAL_PWR_EnableBkUpAccess()函数的作用是把PWR寄存器里的DBP位置1这个位置1之后RTC和备份域寄存器的写操作才被允许。在F4及以上系列没有单独的BKP时钟但同样需要调用这个函数。这个步骤之后还有一个容易忽略的操作如果启用了RTC并且不希望系统复位时RTC配置被重置需要设置RTC的“无备份域复位”属性在HAL中通常通过RTC初始化结构体相关配置完成。4.2 配置RTC时钟源为LSE选择LSE作为RTC时钟源并等待LSE起振稳定。LSE起振是个慢过程一般需要几百毫秒到一两秒代码里必须等待就绪标志否则后续操作全废。RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState RCC_LSE_ON; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); }如果LSE始终无法起振大概率是硬件问题晶振没焊好、负载电容不匹配、PCB走线太长、晶振旁边有干扰源。这时要先把硬件修好不能指望代码绕过。这里补充一个排查技巧先用示波器测量晶振两个引脚正常起振时能看到明显的正弦波形。如果完全测不到波形优先检查晶振是否虚焊、负载电容是否焊反、芯片的OSC_IN/OSC_OUT引脚是否连对。有些人为了省成本用无源晶振却忘记加负载电容也会导致无法起振或起振后频偏很大。4.3 初始化RTC时间和日期RTC时钟源配置好后进入RTC初始化RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef sDate {0}; hrtc.Instance RTC; hrtc.Init.HourFormat RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv 127; hrtc.Init.SynchPrediv 255; hrtc.Init.OutPut RTC_OUTPUT_DISABLE; if (HAL_RTC_Init(hrtc) ! HAL_OK) { Error_Handler(); } sTime.Hours 23; sTime.Minutes 59; sTime.Seconds 50; sTime.DayLightSaving RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation RTC_STOREOPERATION_RESET; if (HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN) ! HAL_OK) { Error_Handler(); } sDate.WeekDay RTC_WEEKDAY_MONDAY; sDate.Month RTC_MONTH_JANUARY; sDate.Date 1; sDate.Year 24; // 注意是24不是2024 if (HAL_RTC_SetDate(hrtc, sDate, RTC_FORMAT_BIN) ! HAL_OK) { Error_Handler(); }几个值得注意的点AsynchPrediv和SynchPrediv的组合要保证分频后的频率正好是1Hz对于32.768kHz的LSE这两组值通常就是127和255。有时看到别人代码里用的是127和255也有人用127和255之外的值比如某些库或者某些系列芯片默认不同但核心公式是异步分频值加1乘以同步分频值加1要等于输入时钟频率。比如32.768kHz128 × 256 32768。年月日格式中Year的存放值是“年减去2000”所以在2024年写入24。WeekDay的数值范围是1到7周一对应1。4.4 读取时间时的防撕裂读法直接顺序读RTC的时分秒寄存器有时候会读错因为在读的过程中发生了秒进位。比如你先读到23:59:59然后下一瞬间时间变成00:00:00但你的程序可能先读秒再读小时结果组合出一个不存在的“00:59:59”。这个问题在HAL库的HAL_RTC_GetTime函数内部会做处理通过轮询RSF标志和读取影子寄存器来实现。但如果你是用寄存器操作就必须自己处理。我的建议是使用HAL的读取函数不要自己直接读寄存器。如果必须在中断或者主循环里手动读可以连续读两次时间值如果两次完全一致说明这次读取是稳定的如果两次不一致重新再读一次。这个方法简单可靠是嵌入式读取多字节连续数据时常用的一致性读取手段。4.5 从备份寄存器判断“是否需要重新设置时间”这是断电不停钟项目中代码层最关键的设计。系统上电后你不能无条件地重新设置RTC时间否则每次复位都会把时间覆盖成初始值。正确做法是使用备份寄存器作为标志#define RTC_FLAG_ADDR 0x01 // BKP寄存器编号 #define RTC_FLAG_MAGIC 0xA5A5 // 上电后判断是否已经设过时间 if (HAL_RTCEx_BKUPRead(hrtc, RTC_FLAG_ADDR) ! RTC_FLAG_MAGIC) { // 首次运行需要设置时间 HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN); HAL_RTC_SetDate(hrtc, sDate, RTC_FORMAT_BIN); HAL_RTCEx_BKUPWrite(hrtc, RTC_FLAG_ADDR, RTC_FLAG_MAGIC); } else { // 已经有时间了不要覆盖 }备份寄存器在VBAT不掉电的情况下能一直保存数据所以这个标志位可以可靠地工作。注意备份寄存器数量有限F1系列只有42个16位寄存器F4有20个32位寄存器节约使用通常用几个就够。热词里有人问“复位后备份寄存器为什么丢了”多半是VBAT电路没接电池或者代码里触发了备份域复位。备份域复位在RTC_ISR寄存器里有一个INITS位软件不能直接触发但RTC初始化过程本身在某些条件下会清备份域这也是为什么初始化代码要写在“首次设置”分支里。4.6 中断和闹钟功能RTC除了走时还有闹钟和唤醒功能。闹钟可以精确到秒级在指定时间触发中断实现定时唤醒之类功能。唤醒定时器可以配置为从几百微秒到几天不等的周期适合低功耗设备的周期唤醒。配置闹钟的代码放在HAL_RTC_SetAlarm里面主要配置闹钟日、时、分、秒和匹配方式比如只匹配时分秒、不匹配日期。使用闹钟中断要正确配置NVIC同时使能RTC闹钟中断且在中断回调函数里清除闹钟标志——这个标志不清除的话中断会反复触发CPU被拖死别问我怎么知道的。4.7 关于LSI的备选方案如果因为硬件BUG或者空间限制暂时只能用LSI代码层需要改动的有两处一是时钟源从RCC_LSE_ON改成RCC_LSI_ON二是分频值要重新算。F103的LSI典型频率是40kHz分频值就要变成40kHz / 1Hz减1但LSI本身误差太大建议配合软件校准使用。5. 调试RTC时一定会踩的坑我帮你提前踩完了这一块都是实战经验不少问题看似代码问题实际是调试思路和方式的问题。5.1 调试器复位导致的时间重置用ST-Link或者J-Link调试时每按一次复位键程序从头跑。如果你的代码里没有用备份寄存器做“首次设置”判断每次复位都会把时间重置为初始值看起来很像是“断电时间不保持”实际上问题出在代码逻辑。比较典型的例子是有些教程直接在主程序里调用HAL_RTC_SetTime完全没做判断初学者按一次复位就蒙了。解决方案就是上文的备份寄存器标志法。5.2 断电后时间不跑或者时间清零断电后时间清零几乎可以断定VBAT没接好。检查顺序是量VBAT引脚的电压是否在2.0V到3.6V之间量电池两端电压是否正常查二极管方向是否接反查VBAT的滤波电容是否虚焊。断电后时间还在走但走得慢大概率是LSE晶振频率偏了或者备用电容容量不足导致供电不稳。有一种情况特别坑VBAT接的电池电压正常但断电后时间依然清零。这时候要查一下是否是芯片复位引脚影响有些外部复位电路在电压跌落时会产生复位脉冲而备份域数据只有在“电源跌落到备份域电压阈值以下”时才会丢失如果复位电路异常导致芯片还在运行状态但主电源已经大幅波动可能出现RTC的数据被内部复位。这种情况要仔细测断电瞬间各路电压的变化曲线。5.3 时间写入后读出来不对写入时间后读出来不对先看年月的格式问题。HAL库的RTC_FORMAT_BIN表示二进制格式RTC_FORMAT_BCD表示BCD格式如果你写了BCD格式却用二进制方式去解析读出来的数据自然是一堆看似随机但很规律的数字。这个错误很隐蔽因为读出来可能是“0x23”代表23点也可能是“0x17”代表BCD的17必须统一格式。全部使用RTC_FORMAT_BIN可以避免多数混乱。另外一个时间是写入后马上读取会读到旧值因为RTC寄存器存在影子寄存器写入后有个同步延迟一般延迟几百微秒再读就正常了。HAL的RTC_GetTime中间有这个等待逻辑但如果你用寄存器操作要注意在RSF位被设置后再读取。5.4 LSE起振失败或精度不够的检测手段LSE起振失败的典型症状是HAL_RTC_Init返回错误或者在调试时看到RTC_ISR的LSERDY标志一直为0。检测手段包括用示波器测量OSC_IN/OSC_OUT引脚看有没有晶振振荡检查RCC_OscInitStruct配置里的LSEState是否为RCC_LSE_ON确认外部晶振的负载电容大小32.768kHz晶振通常要求6.8pF~12.5pF不匹配会衰减振荡幅度。精度不够的问题需要用长时间统计来发现。最简单的做法是记录一个时间点和电脑或其他标准时钟对比跑24小时后看偏差秒数然后决定是否启用RTC校准。校准参数的写入方法在STM32参考手册的RTC章节有详细说明不同系列寄存器名不同但思路一致在校准窗口内按比例增减时钟计数。5.5 Keil调试时查看时间和寄存器的技巧在Keil5的调试界面打开View菜单下的Watch窗口添加表达式hrtc.Instance-TR和hrtc.Instance-DR可以看到原始的二进制时间数据。或者直接在Watch窗口添加HAL_RTC_GetTime的函数调用但注意函数调用有延迟不适合频繁断点查看。更推荐的是在程序里加一个定时打印用串口把当前时间和备份寄存器的标志值周期性输出调试效率会高很多。另外Keil调试时如果发现程序卡在RTC初始化中先检查RTC中断开关状态和NVIC配置有时候调试器和外部中断互相影响会导致HAL_RTC_Init的等待循环超时。视情况可以在初始化前临时屏蔽相关中断确认问题后再统一开放。5.6 电流测量法验证低功耗目标如果你的项目对功耗有要求断电后整机功耗应该只包含RTC走时电流可以串一个万用表在电池回路里测一下实际电流。正常的纽扣电池供电回路电流在2uA以内都算合理如果测出来是几十微安甚至毫安级别说明VBAT电路里可能有其他器件在漏电或者二极管选型不当导致反向漏电过大。这个时候要回头检查电路不要一味优化代码。6. 扩展休眠模式下的RTC应用和产品化建议RTC除了断电保持时间还有一个非常常见的应用让MCU进入低功耗休眠模式用RTC闹钟或者唤醒定时器周期性唤醒MCU执行任务然后再次休眠。这种方式可以把待机电流做到微安级别非常适合电池供电的物联网传感器。典型的流程是初始化RTC和闹钟配置EXTI唤醒线进入STOP模式或STANDBY模式闹钟中断唤醒后执行任务然后重新设置下一次闹钟时间。STOP模式唤醒后系统时钟需要重新配置因为从STOP模式唤醒后HSE就关了。STANDBY模式唤醒后相当于复位RAM内容丢失但RTC和备份寄存器因为是由VBAT供能的所以时间信息不会丢。这两者的区别要在项目设计时提前考虑清楚。产品化阶段还有几个建议第一出厂时设置一个默认时间或者通过上位机、手机蓝牙校准时间第二考虑时间同步机制比如产品连接网络后自动校时第三记录备份电池电压在电池快没电时提前告警避免用户突然发现时间停了第四恶劣环境下备份电池最好用座子固定方便后期更换。这些都是实际项目中非常实用的小细节在开发初期考虑进去之后能省掉大量售后问题。7. 一段可以拿去直接用的完整示例代码最后给一个可以直接编译运行的完整例子基于STM32F103C8T6、HAL库使用LSE时钟包含首次设置、备份标志、读取时间、串口打印。初始化时串口输出当前时间然后周期性每秒打印一次。#include main.h RTC_HandleTypeDef hrtc; UART_HandleTypeDef huart1; #define RTC_FLAG_ADDR 0x01 #define RTC_FLAG_MAGIC 0xA5A5 static void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_RTC_Init(void); static void PrintTime(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_RTC_Init(); while (1) { PrintTime(); HAL_Delay(1000); } } static void MX_RTC_Init(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef sDate {0}; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_RCC_BKP_CLK_ENABLE(); HAL_PWR_EnableBkUpAccess(); RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState RCC_LSE_ON; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); } hrtc.Instance RTC; hrtc.Init.HourFormat RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv 127; hrtc.Init.SynchPrediv 255; hrtc.Init.OutPut RTC_OUTPUT_DISABLE; if (HAL_RTC_Init(hrtc) ! HAL_OK) { Error_Handler(); } if (HAL_RTCEx_BKUPRead(hrtc, RTC_FLAG_ADDR) ! RTC_FLAG_MAGIC) { sTime.Hours 12; sTime.Minutes 30; sTime.Seconds 0; sTime.DayLightSaving RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation RTC_STOREOPERATION_RESET; if (HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN) ! HAL_OK) { Error_Handler(); } sDate.WeekDay RTC_WEEKDAY_MONDAY; sDate.Month RTC_MONTH_JANUARY; sDate.Date 1; sDate.Year 24; if (HAL_RTC_SetDate(hrtc, sDate, RTC_FORMAT_BIN) ! HAL_OK) { Error_Handler(); } HAL_RTCEx_BKUPWrite(hrtc, RTC_FLAG_ADDR, RTC_FLAG_MAGIC); } } static void PrintTime(void) { RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef sDate {0}; char buf[64]; HAL_RTC_GetTime(hrtc, sTime, RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc, sDate, RTC_FORMAT_BIN); snprintf(buf, sizeof(buf), %04d-%02d-%02d %02d:%02d:%02d\r\n, sDate.Year 2000, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds); HAL_UART_Transmit(huart1, (uint8_t *)buf, strlen(buf), 1000); }这段代码的最核心逻辑就是背板寄存器标志判断如果第一次运行写入默认时间并标记如果已经设置过保留现有时间不做覆盖。只要VBAT供电正常无论你断电多久、复位多少次时间都会正确保留。看到的年份保存到寄存器时存的是“年减2000”读取时再减回去这是HAL库的固定规则别自己改动否则所有年份都会差2000年。串口打印部分如果缺少字符串处理支持用最原始的循环发送方式也可以不影响RTC功能本身。8. 最后再聊两句调试时的实用心得调试RTC功能时我喜欢先把串口打印弄好让RTC时间每秒输出一次观察它是否连续稳定。这样的好处是掉电重启后只要看串口的上一次时间和这一次时间是否连续就能立刻判断断电保持是否成功。这个方法比用调试器打断点高效得多特别是断电、上电这个操作场景调试器本身也会重置芯片干扰判断。我还在开发板上面飞了一颗电容到VBAT模拟法拉电容的供电场景。实测主电源拔掉后只要电容电压还能维持在2V以上RTC就一直在走。这个方法也可以用来粗略估算你的备用电源能撑多久看电压跌落的速度推算出维持时间。如果你做的是量产产品一定要在出厂前做断电老化测试。很多RTC问题是在断电切换那个瞬间暴露的比如切换瞬间电压抖动导致备份域数据错乱这种问题在常温常压下可能几十次才复现一次不专门去测根本发现不了。测试时可以在电源输入端加一个开关反复通断配合串口打印和自动比对脚本跑几百次看有没有时间跳变。RTC本身不复杂但“断电保持”这个需求考验的是对整个供电架构和复位机制的理解。硬件电路上把VBAT接好、选对电池、做好保护软件上用好备份寄存器、正确判断首次运行调试时用对方法、注意观察细节——这套组合拳做下来基本就不会有太多折腾了。
企业数字化 ERP 产品动态
相关推荐
STM32驱动SCL3400倾角传感器实战:SPI时序、DMA与工程校准 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:20:21
QT C++俄罗斯方块源代码解析:从编译运行到功能扩展 简介:这份QT C俄罗斯方块源代码面向初学者与有经验的开发者,用于学习QT框架与C游戏开发。项目以QT的QGraphicsView、QGraphicsItem绘制游戏区域与方块,用QTimer控制下落节奏,并通过信号与槽机制处理旋转、移动等事件,同… · 2026/9/28 1:20:15
毕业论文排版自检指南:目录、图表、引用一次理清 毕业论文排版自检指南:目录、图表、引用一次理清
交稿倒计时,谁懂啊!好多同学都卡在格式上反复横跳,尤其是目录、图表和引用这三座大山。内容写完了,格式却让人头秃,改来改去还是被老师打回,真… · 2026/9/28 1:20:15
Python搭建QQ聊天机器人极简教程 随着QQ粉丝群管理需求的不断增长,简单的群管工具难以满足复杂的信息响应和自动化需求。现有的自动回复机器人虽然功能强大,但其高昂的年费成为不少用户的顾虑。因此,通过搭建一个自定义机器人来实现自动回复,成为解决这一问题的有效途径。
基于此需求,本文介绍了使用go-c… · 2026/9/28 2:14:08
Python整理百度云盘文件大量重复无用文件 百度云盘容量有限,当文件数量逐渐增多,空间很容易被填满。删除重复文件可以帮助释放大量空间。通过获取云盘缓存目录并使用Python脚本来整理数据,可以高效识别重复文件并避免手动操作的繁琐。
此方法基于 sqlite3 和 pandas 进行数据处理,简单快捷。 文章目录 云盘数据整理… · 2026/9/28 2:14:07
Python实现将图片转化为具有视觉震撼效果的字符图 字符画是一种将图片转化为字符的艺术表现形式,它通过字符的密度和排列来模拟图片的色彩和形状效果。这种技术不仅在视觉上充满了创造力,还在文字处理领域展示了字符的丰富表现力。通过Python,可以将图片转换为字符画,生成具有视觉冲击力的字符艺术。
本文将通过具体步骤和… · 2026/9/28 2:13:48
Python实现将目录下的图片合并成PDF文件 在图像处理和文档管理中,经常需要将一系列图片文件合并为PDF格式,以便于传输、存档和阅读。Python凭借其丰富的第三方库,为图像处理和PDF操作提供了便捷的解决方案。
本文将详细介绍如何通过Python脚本,将目录中的所有图片合并为一个PDF文件,内容包括从基础环境配置到代码… · 2026/9/28 2:13:48
Python实现文件移动到指定文件夹 在编程过程中,经常需要对文件进行整理和管理,将不同类型的文件分类存放在指定文件夹中。Python提供了强大的文件操作模块,使得文件的移动操作变得简单高效。这篇教程将详细讲解如何使用Python实现将文件移动到指定文件夹的功能,帮助理解并掌握文件操作的基本方法和常见应用… · 2026/9/28 2:13:47
【PyQt】PyQT6制作一个Django项目启动器 在现代的桌面和Web应用开发中,Python以其简单高效的特点获得了广泛的应用。通过集成PyQt和Django框架,将桌面应用的便捷操作与Django项目的后端处理相结合,不仅能够提升用户体验,更能显著提高开发的便利性和效率。
本文将聚焦于如何构建一个基于PyQt的Django项目启动器,实… · 2026/9/28 2:13:40
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
制作网页比较方便的软件怎么选?一文搞懂避坑指南 制作网页比较方便的软件怎么选?一文搞懂避坑指南 很多老板一上来就问:做个网站多少钱?但我反问他:你的域名买了吗?服务器租了吗?他一脸懵。这就是典型的“域名服务器搞不懂”。别急,今天咱们不聊虚的,直接 一文搞懂 那些让你头秃的技术名词。… · 2026/9/28 0:00:06
婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 找婚恋网站建站公司,最怕的就是被坑高价。很多同行跟我吐槽,报价单上写得模棱两可,功能栏里全是“高级定制”、“专属UI”,结果落地全是套壳。今天不聊虚的,直接甩几个我经手的 实战案例… · 2026/9/28 0:00:19
济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 上周济南一个做建材的老板找我,脸都绿了。他的官网首页弹出了赌博广告,后台被植入了挖矿脚本。他慌得问我:“网站被黑挂马不知道怎么办?能不能直接找之前的外包公司要源码下载,看看哪里被动了手脚?… · 2026/9/28 0:00:25