ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32F103C8T6备份寄存器掉电丢失与ADC采样精度问题排查与修复

STM32F103C8T6备份寄存器掉电丢失与ADC采样精度问题排查与修复 如果你用过 stm32f103c8t6 这颗芯片做项目大概对它已经很熟了——但我敢说下面这两个坑你多半也踩过一是备份寄存器明明写进去了断电重启之后全部清零二是 ADC 采样出来的数值跳得让人怀疑人生。前阵子我做一个小型温控记录仪这两件事在同一天爆发硬是把我折腾到凌晨两点。今天就把完整排查过程、原理拆解和最终方案一次性写清楚给遇到同样问题的朋友做个参考。这篇文章适合正在用 stm32f103c8t6 最小系统板做项目的开发者尤其是需要掉电保存参数、又对 ADC 采集精度有要求的场景。我会从备份寄存器的供电架构讲起再深入 ADC 采样精度的硬件与软件调优最后给一份可直接抄作业的避坑清单。1. 问题全景一块蓝板上的两个疑难杂症1.1 备份寄存器掉电未保存是什么现场先说现象。我的温控记录仪里用备份寄存器BKP保存温度校准系数逻辑非常简单上电时读一次没有有效值就进校准模式校完写进去下次开机直接用。结果实测发现只要把板子的 USB 供电拔掉再插回来之前写入的校准系数就全丢了设备回到出厂状态必须重新校准。第一次我还以为是代码写错了查了一遍读写逻辑没问题。后来我又做了个验证程序运行中写入一个特征值 0x5A5A断电前读出来是对的断电重启再读就变成了 0。这基本坐实了不是软件没写进去而是掉电过程中备份寄存器本身没保住数据。紧接着我又发现ADC 采样值也在“捣乱”——同样的供电、同样的传感器采出来的温度值上下能跳十几二十个 LSB看起来完全没法用。1.2 ADC 采样不准的三种典型表现ADC 的问题我最初以为是随机噪声后来仔细统计发现其实是三类问题叠加在一起第一类是结果跳变同一输入电压连续采样数值范围很大属于典型的噪声干扰或采样时间不足第二类是结果稳定但整体偏大或偏小例如给 1.000V 输入实测读数换算出来是 1.035V这通常是参考电压不准或增益误差第三类是信号变化时响应迟钝输入电压已经变了ADC 读数要很久才跟上这种基本可以断定是前端阻抗太高、采样保持电容没充足电。这三类问题在 stm32f103c8t6 上都有可能同时出现尤其当你用的是蓝色最小系统板时板子默认的电源布局和引脚设计本身就给这两个坑埋下了雷。下面分开说。2. 备份寄存器为什么掉电就丢从硬件到代码的层层排查2.1 备份域的供电架构和 C8T6 的引脚真相要理解备份寄存器掉电丢失必须先搞明白它在芯片里属于哪个“域”。STM32F103 内部有一个独立的备份域Backup Domain包含备份寄存器、RTC 和 LSE 振荡器相关配置。这个备份域有两条供电路径当 VDD 正常时备份域由 VDD 供电一旦 VDD 掉电芯片内部会自动切换改由 VBAT 引脚供电。换句话说只要 VBAT 引脚上始终有电就算主电源 VDD 断了备份寄存器的内容也能保持住。问题就出在这里市面上最常见的蓝色 stm32f103c8t6 最小系统板绝大多数都把 VBAT 引脚直接连到了板载 3.3V 电源网络。也就是说板子本身没有给 VBAT 配独立的备用电源你拔掉整个板子的供电VBAT 自然也没电了备份寄存器的内容当然保不住。如果你用的是自己的板子还要注意 LQFP48 封装里 VBAT 引脚的走线不要在 VBAT 上并大容量的滤波电容否则在 VDD 掉电瞬间电容上存储的电荷反而会通过 VBAT 往回倒灌干扰正常的电源切换逻辑。VBAT 脚建议直接接纽扣电池正极电池负极接地再并联一个 100nF 左右的去耦电容即可。2.2 代码配置访问使能、时钟使能一个都不能少排除硬件供电之后代码层面最容易被忽略的是备份域访问使能。STM32F103 复位后备份域默认处于写保护状态这是芯片为了防止程序跑飞时误写备份寄存器而设计的。如果你只是调用了 BKP_WriteBackupRegister而没有先打开访问权限写入操作实际上不会生效但读操作又很可能正常这就很容易造成“明明写了、断电后却没有”的错觉。标准操作是先使能 PWR 和 BKP 的时钟再调用 PWR_BackupAccessCmd(ENABLE) 打开备份域写保护最后才能读写备份寄存器。可以参考下面这段初始化代码RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); // 写入和读取示例 BKP_WriteBackupRegister(BKP_DR1, 0x5A5A); uint16_t val BKP_ReadBackupRegister(BKP_DR1);这里还需要注意一个细节stm32f103c8t6 属于中等容量型号备份数据寄存器只有 BKP_DR1 到 BKP_DR10一共 10 个 16 位寄存器总共 20 字节。你在网上看到的高容量型号有 42 个备份寄存器那是指 F103ZET6 等大容量芯片C8T6 上没有这么多。所以设计数据结构时一定要确认容量没有超。2.3 容易被忽略的备份域复位源还有一种情况让人特别困惑明明 VBAT 接了电池代码也正确但备份寄存器还是会偶尔被清零。这时候要往备份域复位的其他触发源上查。第一个是软件复位备份域。RCC_BDCR 寄存器里的 BDRST 位可以控制备份域复位如果程序里不小心调用了 RCC_BackupResetCmd(ENABLE) 再去 DISABLE整个备份域就会被重置。有些 RTC 初始化代码为了省事会在配置 RTC 时顺手复位备份域如果你的 BKP 数据也是在同一段代码里写的就会刚写完就被清掉。第二个是 TAMPER 侵入检测功能。STM32 的 PC13 引脚默认复用功能就是 TAMPER-RTC如果开启了 RTC 侵入检测PC13 上的指定边沿会触发侵入事件硬件会自动清除全部备份寄存器。很多开发板把 PC13 引出来给用户当普通按键用这就埋下了隐患——按下按键的瞬间备份寄存器可能就被清空了。所以在使用 BKP 之前请务必检查 RTC 侵入检测是否使能以及 PC13 是否被配置成了 TAMPER 功能。如果 PC13 要当普通 IO 用一定要把复用功能改掉或者在 RTC 配置里关掉侵入检测。2.4 实测排查流程和硬件改造方案我自己当时的排查顺序是先拿万用表量 VBAT 引脚对地电压断电瞬间观察电压曲线结果发现 VBAT 和 VDD 一起掉到 0问题当场定位——板载 VBAT 接到了 3.3V。然后我再检查代码里的访问使能确认没问题之后就开始做硬件改造。改造方案很简单把蓝色板子上 VBAT 和 3.3V 之间的走线切断飞线出来接一个 CR1220 纽扣电池座电池座正极接 VBAT负极接 GND。如果你用的是自己设计的板子原理图上就应该把 VBAT 单独引到一个电池座或超级电容上不要在 PCB 内部把 VBAT 和 VDD 直接短接。改造完成后我又做了断电测试正常运行写 BKP拔掉 USB 电源等 10 秒再重新上电读取数据还在。实测 VBAT 在待机状态下的电流只有几微安一节 CR1220 电池理论上可以用好几年。如果不想加电池另一个可行方案是使用掉电检测PVD。配置 PVD 中断当 VDD 跌到设定阈值以下时系统还有最后几个毫秒的供电时间可以在这段时间里把关键参数紧急写入内部 Flash 或外部 EEPROM。这个方法适合对成本和体积敏感的产品但要注意写入时间窗口很短Flash 页擦除可能来不及更稳妥的搭配是用 W25Q64 这类外部 SPI Flash 做临时存储或者直接用 I2C EEPROM。下面把几种掉电保存方案的优劣势列个表方案断电保持能力写入寿命容量关键注意事项备份寄存器 VBAT 电池依赖电池无限次C8T6 仅 20 字节VBAT 必须独立供电防 TAMPER 误清内部 Flash不需要外部供电约 1 万次擦写较大擦写慢频繁写入会磨损外部 EEPROM (I2C)不需要外部供电约 100 万次从几 K 到几 M成本稍高写入有页大小限制外部 SPI Flash (W25Q64)不需要外部供电约 10 万次8M bit注意擦写均衡掉电写入需防写坏3. ADC 采样精度12 位 ADC 的误差从哪来3.1 F103 的 ADC 工作原理与关键参数STM32F103 内置的 ADC 是 12 位逐次逼近型SAR结构采样过程可以简单理解为采样阶段内部开关闭合外部信号给一个很小的采样保持电容充电保持阶段开关断开ADC 内部通过逐次逼近比较器从高位到低位逐位确定最终数字量。这个过程决定了两个直接影响精度的参数采样时间和转换时间。转换时间公式为 Tconv 采样时间 12.5 个 ADC 时钟周期。ADC 时钟来自 APB2 总线最高 72MHz但 ADC 本身最高只能跑 14MHz所以必须用预分频器分频。如果 PCLK2 是 72MHzADC 预分频选择 6ADC 时钟就是 12MHz选择 8 则是 9MHz。很多人图省事直接选 2 分频ADC 时钟跑到 36MHz 超限了出来的数据完全不正常。采样时间通过 SMPR1/SMPR2 寄存器配置可选 1.5、7.5、13.5、28.5、41.5、55.5、71.5、239.5 个周期。需要特别提醒的是采样时间不是越小越好。采样时间越短留给采样电容充电的时间就越短如果外部信号源阻抗较高充电不完全转换结果就会偏小或跳动。这也是 ADC“不准”最常见的原因之一。3.2 C8T6 的参考电压陷阱很多新手没有意识到一个问题STM32F103C8T6 的 LQFP48 封装里没有独立的 VREF 引脚。参考电压在芯片内部直接连到了 VDDA。这意味着你的 ADC 参考电压完全取决于 VDDA 的干净程度。如果 VDDA 上还有来自 DC-DC 或 LDO 的高频纹波ADC 结果就会跟着纹波动。市面上蓝色最小系统板的 VDDA 一般直接通过一个磁珠连接到 3.3V这个做法不能说错但磁珠对高频噪声的抑制有限。实测很多蓝板在空载时 VDDA 纹波已经达到几十毫伏换算成 12 位 ADC 就是好几个 LSB 的跳动。如果做精密采集建议你自己设计板子时把 VDDA 单独供电或者用低噪声 LDO 加 π 型滤波给模拟电源。改善方法很直接在 VDDA 对地加一个 10μF 钽电容和一个 100nF 陶瓷电容电容尽量靠近 VDDA 引脚如果条件允许在 VDDA 和 VDD 之间串一个 10Ω 电阻或磁珠做隔离再讲究一点可以使用外部基准源芯片比如 REF3030给 VDDA 供电但你需要确认封装上有独立的 VREF 引脚可接C8T6 本身不行所以 VDDA 即 VREF。3.3 采样时间与信号源阻抗的匹配计算ADC 采样保持电容充电过程可以简化成一个 RC 电路。STM32F103 的采样电容约 3.5~4pF内部串联电阻约 1kΩ。12 位分辨率要求充电误差小于 1/4096粗略估算需要 8.3 个时间常数。举个例子如果信号源来自一个 10kΩ 的分压电阻网络总的源阻抗就是 10kΩ再加上内部 1kΩ时间常数约为 (10kΩ 1kΩ) × 4pF 44ns。8.3 个时间常数大约是 365ns。在 14MHz ADC 时钟下一个周期是约 71.4ns所以采样时间至少需要约 5.1 个时钟周期选择 7.5 周期才稳妥。如果你的源阻抗是 100kΩ时间常数就变成 400ns 以上需要 55.5 甚至 71.5 周期才够。这就是为什么同一个 ADC 配置接不同传感器时精度截然不同。使用高阻传感器时建议在 ADC 输入引脚前面加一个电压跟随器运放把源阻抗降到几百欧以下或者直接把采样时间设成 239.5 周期以获得最大裕量。这里提供一段简单的初始化代码ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC 时钟 72MHz / 6 12MHz ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); // 通道0采样时间 55.5 周期适合稍高阻抗信号源 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_Cmd(ADC1, ENABLE);3.4 多通道切换、DMA 与串扰问题如果你和热搜词里一样用 ADC 多通道加 DMA 做采样还要注意通道间的串扰问题。ADC 内部只有一个采样保持电容切换通道后如果采样时间不够上一个通道的残余电荷会影响当前通道的结果。多通道扫描时尤其明显往往表现为某个通道读数偏高另一个偏低而且值和扫描顺序有关。应对办法有两个一是把每个通道的采样时间都配置得足够长二是在 DMA 搬运时做软件通道隔离比如先丢弃第一次转换结果或者连续采样 N 次只取后 N-1 次有效值。另外外部信号调理电路里每个通道都建议加一阶 RC 低通滤波电阻加在信号源和 ADC 引脚之间电容并到地。这个电容本身可以额外存储电荷能在采样瞬间提供一部分充电电流等于变相降低了源阻抗的影响。RC 截止频率要根据信号带宽选择。比如温控信号带宽只要几赫兹那 RC 可以选 100kΩ 100nF截止频率约 16Hz滤除噪声效果显著如果是音频或振动信号带宽高RC 就要相应减小否则信号会被衰减。3.5 软件滤波与软件校准硬件做到位之后软件滤波和校准能进一步把精度从“能用”提到“好用”。滤波算法里最简单的是一阶低通y(n) y(n-1) k×[x(n) - y(n-1)]k 取 0.1~0.3 效果不错如果怕尖峰干扰可以用滑动中值滤波想要兼顾实时性和平滑度推荐“去极值滑动平均”比如连续采 8 次去掉最大值和最小值剩下 6 次取平均。下面给出一个实用的参考函数uint16_t ADC_Filter(uint16_t *buf, uint8_t len) { uint16_t min 0xFFFF, max 0; uint32_t sum 0; for (uint8_t i 0; i len; i) { if (buf[i] min) min buf[i]; if (buf[i] max) max buf[i]; sum buf[i]; } return (uint16_t)((sum - min - max) / (len - 2)); }校准方面单点校准只能消除偏移误差两点校准可以同时消除偏移和增益误差三点及以上则能修正部分非线性。校准公式是 y (x - offset) × gain两点校准时用高精度万用表测量两个已知电压例如 0.5V 和 3.0V记录 ADC 读数 x1、x2代入真值 y1、y2就能解出 offset 和 gain。校准完成后把这两个参数存到备份寄存器或 Flash 里以后每次采样后做一次线性修正。4. 实操记录温控记录仪项目的翻车与修复4.1 项目背景和初始方案我的温控记录仪需求不复杂用 NTC 热敏电阻加固定电阻分压接入 ADC 采样换算成温度用户可以通过按键校准温度偏差校准参数掉电不能丢。所以最初方案就是备份寄存器存校准参数、ADC 采温度、Flash 存历史记录。第一版代码烧进去桌面测试时一切正常。但装进壳子、插拔几次电源之后客户反馈“校准过又失效了”我拿回来一测果然备份寄存器全清零。同时把 NTC 分压电压用示波器一看波纹明显ADC 原始码值在 1000 附近上下跳了七八个码。4.2 备份问题的修复VBAT 飞线加纽扣电池我先从最简单的地方下手根据第二章的方法确认 VBAT 接线。蓝色板子上VBAT 旁边有一个过孔用刀片小心切断连接到 3.3V 的短线然后从 VBAT 焊盘引出一根漆包线接到 DIY 的 CR1220 电池座上。焊接时注意烙铁温度不要太高C8T6 的引脚间距很小相邻引脚连锡就是灾难。装好电池后用万用表量 VBAT 对地电压3.0V 左右再测整板待机电流VBAT 支路只有约 3μA。随后重新烧录程序写入特征值断电再上电数据还在。重复了二十几次开关机没有一次丢失。到这里备份寄存器问题算是彻底修复。4.3 ADC 精度修复硬件滤波加软件三重处理ADC 部分我分了三步。第一步改硬件在 VDDA 和 3.3V 之间加了一个 10Ω 电阻并在 VDDA 对地加上 10μF 钽电容和 100nF 陶瓷电容NTC 分压输出到 ADC 引脚之间串了 1kΩ 电阻对地接 100nF 电容构成低通滤波。第二步改软件配置采样时间从默认的 1.5 周期改成 55.5 周期ADC 时钟用 12MHz。第三步加软件滤波连续采样 8 次去掉最大值和最小值取平均。这三步做完后的实测对比非常明显测试条件原始 ADC 跳变范围优化后跳变范围输入电压约 1.0V±18 LSB±2 LSB输入电压约 2.5V±25 LSB±3 LSB传感器通道NTC 分压±12 LSB±1~2 LSB随后我又用精密万用表做了两点校准分别在 0.500V 和 3.000V 下记录 ADC 读数解出 offset 和 gain写进备份寄存器。校准之后换算出的温度误差从原来的 ±1.5℃ 缩小到 ±0.2℃ 以内完全满足项目需求。5. 常见问题速查表与避坑清单5.1 备份寄存器故障速查表现象可能原因排查和解决方法断电重启后 BKP 数据丢失VBAT 没有独立供电或直接接 VDD万用表量 VBAT接纽扣电池或超级电容写 BKP 后读出来正常断电后丢写入时未使能备份域访问加入 RCC 和 PWR_BackupAccessCmd 使能代码运行中 BKP 数据突然清零TAMPER 侵入检测触发检查 PC13 功能和 RTC 侵入检测配置禁用或改 GPIO程序重启但不掉电BKP 丢失初始化代码误复位备份域检查 RCC_BackupResetCmd 是否被调用BKP 写入内容溢出或覆盖C8T6 只有 10 个 16 位寄存器确认 BKP_DRn 下标不超 BKP_DR105.2 ADC 采样异常速查表现象可能原因排查和解决方法采样值跳变大VDDA 纹波大、采样时间不足改善 VDDA 滤波增加采样周期读数稳定但整体偏低信号源阻抗太高保持电容未充满加运放跟随器或采样时间设最大读数整体偏高或偏低参考电压不准确用基准源校准或校准 VDDA做软件校准多通道扫描相互串扰通道切换后采样时间不足每个通道都配置长采样时间丢弃首次数值ADC 数值完全为 0 或 4095ADC 时钟超 14MHz 或通道未初始化检查 RCC_ADCCLKConfig 分频和 GPIO 配置5.3 几条不写进手册的实战心得备份寄存器最大的优势不是容量大而是写入次数无限制、速度极快。像校准参数这种需要频繁修改、又怕丢失的数据放 BKP 比放 Flash 省心得多因为 Flash 有擦写寿命限制频繁擦写很容易把扇区写坏。但 BKP 的天生短板也很明显必须有 VBAT 供电一旦电池没电数据一样保不住。所以在产品设计时我通常把“不经常变化但很重要”的参数放备份寄存器把“历史记录类”的大量数据放 Flash 或 W25Q64各司其职。还有一点经验是关于调试器的。很多人在 MDK/IAR 里点“Reset and Run”时发现 BKP 数据没了以为复位会清备份域。实际上内核复位和备份域复位是两回事NRST 复位并不会清 BKP。出现这种情况的原因往往是调试器在下载程序时触发了芯片的整片擦除或者你代码启动阶段重新配置了备份域排查时往这个方向想会快很多。另外PVD 掉电检测配合备份寄存器是一个性价比很高的组合方案。配置 PVD 中断后VDD 跌落瞬间就能感知把当前状态存进 BKP因为 BKP 写入不需要等待擦除几个微秒内就能落盘比紧急写 Flash 靠谱得多。这个方案在不需要长期断电保持、只要求“瞬间掉电数据不丢”的场合非常实用。最后想说一句这类问题排查起来千万不要只盯代码。stm32f103c8t6 的坑往往一半在 PCB 和供电设计上用万用表和示波器先确认硬件再回头审视代码顺序反了会浪费大把时间。希望这份记录能帮你少熬几个夜。
返回列表