滕州营销型网站建设,产品开发流程ppt,大安网站建设,wordpress资源类主题STM32 ADC调试踩坑记#xff1a;一个printf引发的血案
前言
最近在调试STM32F429的ADC注入通道功能时#xff0c;遇到了一个诡异的问题#xff1a;注入通道转换完成后#xff0c;规则通道停止更新。
经过一番寄存器级调试#xff0c;我找到了一个printf引发的血案前言最近在调试STM32F429的ADC注入通道功能时遇到了一个诡异的问题注入通道转换完成后规则通道停止更新。经过一番寄存器级调试我找到了解决方案但最后发现——真正的凶手竟然是调试用的printf这个调试过程很有意思记录下来分享给大家。源码仓库地址https://github.com/SXSBJS-XYT/STM32/tree/ADC/ADC环境信息MCU: STM32F429IGT6开发工具: CubeMX Keil MDKHAL库版本: STM32Cube_FW_F4 V1.28.3问题描述功能需求规则通道(PA0)连续转换模式后台持续采集注入通道(PA2)软件触发高优先级打断规则通道预期行为按照STM32参考手册(RM0090)的描述11.3.9 注入通道管理 - 触发注入通过外部触发或将ADC_CR2寄存器中的SWSTART位置1来启动规则通道组转换。如果在规则通道组转换期间出现外部注入触发或者JSWSTART位置1则当前的转换会复位并且注入通道序列会切换为单次扫描模式。然后规则通道组的规则转换会从上次中断的规则转换处恢复。手册明确说注入完成后规则通道会自动恢复。规则通道: [转换][转换][暂停][转换][转换]... ↑ ↑ 注入通道: 触发 完成 打断 自动恢复实际现象规则通道: [转换][转换][停止] ↑ 注入通道: 触发 打断后再也不恢复规则通道只在初始化后更新一次触发注入后就再也不更新了。第一轮调试寄存器分析定位问题边界通过对比测试测试场景结果只启动规则通道不触发注入✓ 正常持续更新只触发注入通道✓ 正常回调触发规则注入同时使用✗ 注入后规则停止问题出在两者的交互上。寄存器调试在注入完成回调里打印寄存器voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);printf(CR20x%08lX, SR0x%08lX\r\n,hadc-Instance-CR2,hadc-Instance-SR);}输出结果CR20x00000403, SR0x00000008寄存器解析CR2 0x00000403Bit名称值含义0ADON1ADC开启 ✓1CONT1连续转换模式 ✓30SWSTART0规则通道启动位 0 ← 问题SR 0x00000008Bit名称值含义3JSTRT1注入通道已启动4STRT0规则通道未启动 ← 问题ADC没关连续模式还在但规则通道的启动位被清除了初步结论与修复当时的分析HAL库的问题手册说会自动恢复但实际没恢复。修复方案——在回调里手动重启规则通道voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);/* 修复手动重启规则通道 */hadc-Instance-CR2|ADC_CR2_SWSTART;printf(CR20x%08lX, SR0x%08lX\r\n,hadc-Instance-CR2,hadc-Instance-SR);}加上这行后规则通道确实恢复工作了。问题解决准备写博客吐槽HAL库…剧情反转真正的凶手在整理代码时我注释掉了调试用的printf顺便也注释掉了SWSTART那行修复代码voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);s_injected_readytrue;// hadc-Instance-CR2 | ADC_CR2_SWSTART; // 注释掉// printf(CR20x%08lX, SR0x%08lX\r\n,// hadc-Instance-CR2,// hadc-Instance-SR);}神奇的事情发生了——规则通道正常工作了验证测试有printf无SWSTART 规则通道停止有printf有SWSTART 规则通道工作无printf无SWSTART 规则通道工作无printf有SWSTART 规则通道工作测试场景结果有printf无SWSTART✗ 规则通道停止有printf有SWSTART✓ 规则通道工作无printf无SWSTART✓ 规则通道工作无printf有SWSTART✓ 规则通道工作真相大白printf才是罪魁祸首根因分析ADC转换时间: 约4.36μs (84采样周期 12周期 22MHz) printf执行时间: 约500μs~2ms (取决于波特率和字符串长度)时间线对比有printf时: ↓ 注入完成中断 中断回调: [读JDR][──────printf执行中(500μs)──────][返回] 规则通道: 暂停...等待...等待...超时/异常...停止 无printf时: ↓ 注入完成中断 中断回调: [读JDR][置标志][返回] ← 几μs 规则通道: 暂停 → 立即恢复 ✓printf通过串口发送数据在115200波特率下发送40个字符大约需要3.5ms而ADC硬件期望中断快速返回以恢复规则通道转换。长时间占用中断干扰了ADC硬件的自动恢复机制为什么寄存器显示SWSTART0这其实是正常的SWSTART是触发位置1后硬件自动清零SWSTART位由软件置1来启动转换转换开始后由硬件清零。在printf执行期间读取CR2当然看到的是0。这并不意味着规则通道没启动而是启动信号已经过去了。当时的分析思路错了——看到SWSTART0就以为需要重新置位实际上是printf延迟导致的错觉。为什么加上SWSTART能修复虽然printf导致了问题但在回调里加上CR2 | ADC_CR2_SWSTART确实能让规则通道恢复voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);printf(...);// 干扰硬件自动恢复hadc-Instance-CR2|ADC_CR2_SWSTART;// 手动补救}这是治标不治本的方案——用软件手段弥补了printf带来的破坏。最终正确的写法voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadcs_hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);s_injected_readytrue;s_trigger_count;/* 不要在这里printf *//* 不需要手动重启规则通道硬件会自动恢复 */}}如果确实需要调试输出用标志位在主循环里打印/* 中断回调 - 快进快出 */voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){s_injected_valueHAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1);s_debug_flagtrue;// 设置标志}/* 主循环 - 在这里打印 */while(1){if(s_debug_flag){s_debug_flagfalse;printf(Injected: %d\r\n,s_injected_value);}}总结问题根因不是HAL库的bug不是芯片的问题是调试代码(printf)在中断里执行太久干扰了ADC硬件的自动恢复机制。核心教训中断回调要快进快出不要在中断里做printf、HAL_Delay等耗时操作调试代码也会引入bug海森堡效应——观测行为本身影响了被观测对象不要急于下结论找到解决方案后要验证根因是否正确相信手册但要正确理解手册说的自动恢复是对的前提是中断正常返回中断里应该做什么允许禁止读写寄存器printf/sprintf设置标志位HAL_Delay读写全局变量复杂计算短小的赋值操作调用阻塞函数这次调试经历提醒我有时候bug不在你怀疑的地方而在你最信任的地方——比如那行看起来人畜无害的printf。如果这篇文章对你有帮助欢迎点赞收藏。有问题可以在评论区讨论。