ARTICLE DETAIL

资讯详情

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

CubeMX 6.18致STM32H743 ADC3温度读数异常与修复

CubeMX 6.18致STM32H743 ADC3温度读数异常与修复 先说现象。我手上好几个基于 STM32H743 的产品都在用 ADC3 读取片内温度传感器做板级过热保护一直稳定跑了两年多。上个月把项目从 STM32CubeMX 6.15 升到 6.18重新生成代码后原本正常的温度采样突然变得离谱——要么一直是固定值要么跳得毫无规律甚至出现 -40℃ 这种明显不可能的数据。检查硬件、检查线路、检查参考手册折腾了一整天最后定位到是 CubeMX 6.18 在 ADC3 内部温度传感器通道的配置和生成代码上做了改动。这篇就完整记录一下排查过程、根因分析和最终的修复方案给同样被困在这个问题里的朋友一个能直接抄作业的参考。这个问题适合所有用 STM32H7 系列做 ADC 采集的开发者尤其是那些把内部温度传感器配在 ADC3 上、并通过 CubeMX 自动生成初始化代码的项目。读完你不仅能解决版本升级后温度读数不对的问题还能顺带搞懂 H743 上 ADC3 和 ADC1/2 的内部通道映射差异以后再遇到类似“升级后功能失效”都能少走弯路。1. 问题复现CubeMX 6.18 下 ADC3 温度传感器读数异常的全过程1.1 先说结论现象、影响范围和复现条件先给结论STM32CubeMX 6.18 在生成 STM32H743 的 ADC 初始化代码时对内部温度传感器通道的映射逻辑做了变更。这个变更会导致原本配置在 ADC3 上的内部温度传感器请求生成出错误或无效的通道配置。具体表现是寄存器被写入了错误的通道编号ADC 虽然完成转换但采集到的其实是未连接的内部通道数据自然不对。这个问题的复现条件非常明确芯片是 STM32H743 或同系列带 ADC3 的型号ADC3 被配置为读取内部温度传感器使用 STM32CubeMX 6.18 及以上版本生成初始化代码项目是从 6.15 或更早版本迁移过来的需要特别说明的是这个问题不是硬件故障。芯片本身没问题温度传感器也没坏。只要把代码恢复到 6.15 生成的逻辑或者手工修正 ADC3 的通道配置温度读数立刻恢复正常。我在三个不同的板卡设计上都验证了这个问题包括自己画的底板、两家的第三方核心板现象完全一致。这说明不是某一款 PCB 的布局或参考电压导致的问题而是软件生成层面的系统性差异。1.2 我这边复现用的环境和步骤复现环境我列在下面方便你对照IDESTM32CubeIDE 1.16.1固件包STM32CubeH7 V1.11.2CubeMX 6.18 内置芯片STM32H743VIT6 / STM32H743IIT6硬件自制底板 独立板各一块ADC 配置ADC3 单通道16 位分辨率内部温度传感器采样时间保持默认温度计算公式标准 H7 公式T 25℃ - (Vsense - 1.43V) / 2.86mV/℃各芯片有对应校准点后文会细说复现步骤很简单用 CubeMX 6.15 打开旧工程确认 ADC3 温度传感器配置正常编译烧录后能读到 30℃ 左右的环境温度。用 CubeMX 6.18 打开同一个工程不修改任何 ADC 相关配置重新生成代码。编译烧录运行串口打印观察温度数据。结果数据出现固定值或剧烈跳变和 6.15 版本行为完全不同。我最初怀疑是编译器优化问题把优化等级从 -O2 改成 -O0问题依然。也怀疑过参考电压不稳定在 VREF、VDDA 上加了一堆电容没有改善。最后才想到对比两个版本生成的代码差异。1.3 现象细节数据和波形变化这里我把 6.15 和 6.18 环境下观察到的数据和现象差异整理成表格方便对照项目CubeMX 6.15CubeMX 6.18生成代码中使用的通道宏ADC_CHANNEL_TEMPSENSOR(18)ADC_CHANNEL_TEMPSENSOR 映射变更ADC3 转换结果原始值稳定在 28000~31000 区间固定 0 或 65535或无规律跳动换算温度28~35℃-40℃、100℃、突变连续采样稳定性每次采样偏差 0.5℃相邻两次采样可能差 20℃ 以上用调试器读 ADC3 数据寄存器数值平滑变化数值异常或不变注意一个关键细节在上述现象中ADC 的 EOC 标志置位正常转换完成中断也能进入DMA 采集模式也正常搬运数据。这说明 ADC 核心本身在工作问题出在输入通道选择上而不是转换流程上。当时看到这个现象我最直观的感受是6.18 把转换通道配置错了。后来对比生成的代码和寄存器配置验证了这个判断。2. 为什么升级后坏掉的深层原因2.1 H743 的 ADC3 内部通道和 ADC1/2 并不一样要理解这个 bug必须先把 H743 的内部通道映射搞清楚。这是整个问题的核心也是最容易踩坑的地方。STM32H743 的 ADC1 和 ADC2 有 20 个模拟输入通道编号 0 到 19其中有几个是内部信号通道 16VREFINT内部参考电压通道 17VBAT电池电压监测通道 18VSENSE内部温度传感器通道 19VDDA 采样部分封装而 ADC3 的通道编号映射和 ADC1/2 不一样。ADC3 的内部信号连接到通道 9VREFINT / VSENSE / VBAT 的复用入口也就是说在参考手册的 ADC 通道映射表里ADC1/2 用通道 18 读温度传感器ADC3 要用通道 9 读同一个温度传感器不能直接把 ADC1/2 的通道号套到 ADC3 上。这里有个非常重要的背景早期 STM32CubeMX 在生成 H7 系列代码时内部温度传感器的通道配置是通过一个统一的宏ADC_CHANNEL_TEMPSENSOR来处理的。这个宏在 HAL 库里被定义为 18无论你把温度传感器配置在哪个 ADC 上生成出来的代码可能都是 18。对于 ADC1/2 来说这完全正确但对于 ADC3 来说通道 18 并不是内部温度传感器它可能映射到某个外部引脚或空闲输入。在 6.15 时代CubeMX 对 ADC3 内部通道的处理实际分两种路径如果芯片头文件里没有为 ADC3 区分内部通道生成器仍可能沿用通道 18但底层 HAL 库或者用户手动补丁会把通道修正过来。这就是很多旧工程能正常工作的原因——不是 CubeMX 聪明而是 HAL 库或早期固件包有对应的兼容处理。2.2 CubeMX 6.18 在生成代码上的实际改动STM32CubeMX 6.18 捆绑的 STM32CubeH7 固件包升级后HAL 驱动对 ADC3 内部通道的支持更“完整”了。变化的核心在于6.18 之后CubeMX 对 H7 系列 ADC3 配置内部温度传感器时识别到 ADC3 应该走通道 9而不是 ADC1/2 的通道 18。从代码生成的角度看6.15 生成的MX_ADC3_Init函数里通道配置通常写的是sConfig.Channel ADC_CHANNEL_TEMPSENSOR; sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLINGTIME_16CYCLES_5;而ADC_CHANNEL_TEMPSENSOR在 HAL 头文件中被定义为 18所以最终写入寄存器的是 18。在 6.18 生成的代码里CubeMX 会生成类似于sConfig.Channel ADC_CHANNEL_9; sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLINGTIME_16CYCLES_5;如果新生成代码中的通道 9 对 ADC3 是有效且正确的那么功能应该恢复正常但问题恰恰出在这里很多从旧版本迁移上来的工程打开 CubeMX 6.18 后ADC3 的温度传感器配置界面里的通道选择项没有自动刷新或者界面显示的还是“Temperature sensor”但底层生成的代码中通道宏已经变成了别的值。更常见的情况是6.18 生成的代码中sConfig.Channel被设置为ADC_CHANNEL_9但对应的引脚配置或内部通道使能位没有正确同步导致虽然通道号正确但内部传感器通路没有被真正使能。2.3 寄存器层面发生了什么如果你用调试器去读 ADC3 的寄存器会看到关键差异。ADC 通道选择主要在 ADC 的 SQR1规则序列寄存器中配置对于 16 位过采样模式常规序列的第一个转换通道存储在 SQR1 的 SQ1 位段中。6.15 的正常工作状态下ADC3 的 SQR1.SQ1 对应的值是 9即内部温度传感器实际通道号同时 ADC3 的寄存器 CCR 或相关配置里内部通道使能位是被置位的。6.18 的错误状态下ADC3 的 SQR1.SQ1 可能是 18而 ADC3 的通道 18 在芯片内部没有连接有效信号于是转换结果要么是某个悬空输入的上电随机值要么直接是 0 或满量程。我通过以下代码确认了这个判断uint32_t sqr ADC3-SQR1; printf(ADC3-SQR1 0x%08lX\n, sqr);6.15 版本运行时的打印结果是SQR1 0x00020000附近也就是 SQ1 位段内是 96.18 版本打印出来的却是SQR1 0x00040000即 SQ1 位段内是 18。这一下就看得很明白通道号被写错了。所以问题的本质不是“温度传感器读不到”而是“ADC3 被配置去读了一个不存在的通道”。3. 解决方案三套可以落地的修法3.1 方案一在 CubeMX 6.18 里重新配置内部通道先说最推荐的做法在 CubeMX 6.18 里把 ADC3 的内部通道配置删掉重新添加一次。具体操作如下打开 6.18 工程进入 Pinout Configuration。左侧找到 ADC3进入 Channels 配置页。在内部通道配置区Internal Channels里把 Temperature Sensor 的勾选取消。点击 Apply再重新勾选 Temperature Sensor。确认下方列表里出现的通道编号已经变为 9而不是 18。重新生成代码编译烧录测试。这么做主要是为了让 CubeMX 6.18 的工程文件.ioc刷新内部通道状态。版本升级后.ioc 里的旧配置可能残留了 6.15 时代的内部通道 ID界面不刷新生成代码就会出问题。重新勾选一次CubeMX 会按 6.18 的新规则写入正确配置。我实测这个方法在约一半的工程上有效。如果你的 .ioc 文件比较干净刷新后生成的代码就是正确的如果 .ioc 里残留信息比较顽固可能还是生成错误代码需要接着用下面的手工修改方案。3.2 方案二手动修改生成的 C 代码恢复正确行为如果刷新界面后生成的代码依然不正确或者你不希望改 .ioc 文件可以直接手工修改生成的main.c或对应的 ADC 初始化文件。关键要找到MX_ADC3_Init函数然后把通道配置里错误的通道号改掉。错误代码通常长这样static void MX_ADC3_Init(void) { ADC_ChannelConfTypeDef sConfig {0}; // ... hadc3.Instance ADC3; hadc3.Init.ClockPrescaler ADC_CLOCK_SYNC_PCLK_DIV4; hadc3.Init.Resolution ADC_RESOLUTION_16B; // ... sConfig.Channel ADC_CHANNEL_18; // 或者 ADC_CHANNEL_TEMPSENSOR 被映射成了 18 sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLINGTIME_16CYCLES_5; if (HAL_ADC_ConfigChannel(hadc3, sConfig) ! HAL_OK) { Error_Handler(); } }对于 STM32H743 的 ADC3如果 CubeMX 生成的通道号不是 9需要手动改成sConfig.Channel ADC_CHANNEL_9; sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLINGTIME_32CYCLES_5;采样时间这里也值得注意。内部温度传感器输出阻抗较高H7 建议在读取温度传感器时采样时间不要太短。6.15 的默认生成可能会用 16.5 周期实际用下来偏紧我建议至少在 32.5 周期以上。修改后重新编译功能就能恢复。修改之后我还建议做一次 ADC 自校准在HAL_ADC_Start之前加调度HAL_ADCEx_Calibration_Start(hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);这一步可以消除 ADC 本身的 offset error对温度读数的绝对精度有帮助。3.3 方案三绕开 ADC3改用 ADC1/ADC2 读内部温度如果你对 ADC3 的内部通道映射实在不放心或者项目里有多个 ADC 可用最简单稳妥的方案是把温度传感器迁到 ADC1 或 ADC2 上读取。H743 的 ADC1 和 ADC2 内部温度传感器通道是标准的通道 18CubeMX 对这种配置的生成逻辑非常成熟不管哪个版本都不太会出错。而且 ADC1 和 ADC2 在引脚、DMA、中断资源的配置上通常也更灵活。迁移步骤在 CubeMX 中把 ADC3 的温度传感器功能取消只保留 ADC3 可能需要的其他外部通道。在 ADC1 或 ADC2 上启用 Temperature Sensor。重新生成代码把 ADC3 的温度采样代码迁到 ADC1/ADC2 的句柄上。修改 DMA 通道关联如果有。这么做要多花十几分钟但从长期维护的角度看降低了不少隐性成本。温度传感器是内部信号不依赖外部引脚放哪个 ADC 上读效果一样。唯一要留意的是 ADC1 和 ADC2 的采样顺序、扫描模式要单独配好不要让温度采样和其他通道的转换相互干扰。我个人在大批量产品里最后选择的就是这个方案不是怕了 ADC3而是希望固件升级、CubeMX 版本升级时少一个不可控变量。4. 排查技巧与避坑清单4.1 快速判断是配置问题还是芯片问题很多朋友遇到“温度传感器读数不对”第一反应就是怀疑硬件其实绝大多数都是配置问题。我给你一个快速判断流程几分钟就能定位用调试器读ADC3-SQR1看 SQ1 位段的值。ADC3 上读温度传感器正常应该是 9。如果读到的是 18 或其他外部通道号基本就是软件配置问题。读ADC3-CR1或ADC3-CCR检查内部通道使能位有没有被正确置位。把 ADC 输入切换到外部引脚通道比如接一个已知电压到某个 ADC3 引脚看转换结果是否和万用表读数一致。如果外部通道准、内部通道不准大概率还是内部通道配置问题。把同样的初始化代码跑在 ADC1 上如果 ADC1 温度读数正常说明芯片温度传感器本身没问题。这套排查流程不依赖特殊仪器一个调试器加一块万用表就够了。4.2 温度计算和校准相关的三个大坑通道配置修好之后温度读数依然可能不准。这里我总结三个和计算、校准相关的常见坑第一H743 的VSENSE校准三点不在数据手册的某个固定地址上。不同批次芯片25℃/110℃/130℃ 三个校准点的 ADC 值存放在芯片 OTP 区地址通常为0x1FF1E820、0x1FF1E840、0x1FF1E860附近。如果你的程序没有读取这些校准点而是直接用数据手册里的典型斜率 2.86mV/℃ 去算25℃ 附近能差出好几度。第二温度传感器读数受采样时间和 ADC 时钟影响很大。H7 的 ADC 内部温度传感器建议在 ADC 时钟较低、采样时间较长时读取否则结果偏向某一定值。我在 16.5 周期和 32.5 周期之间实测读数能差 3~5℃ 以上。第三ADC 自校准必须在每次上电后、开始转换之前执行而且不要在温度剧烈变化时校准否则 offset 校准值本身就带了误差。很多朋友把HAL_ADCEx_Calibration_Start放在初始化之后但没检查返回值校准可能根本没成功。下面给一个我在项目里实际使用的温度计算函数兼容 OTP 校准点float read_temperature_celsius(void) { uint32_t raw 0; for (int i 0; i 16; i) { raw 得到ADC采样值(); // 多次采样取平均 } raw 4; uint32_t cal30 *((uint32_t *)0x1FF1E820); // 30℃校准点 uint32_t cal130 *((uint32_t *)0x1FF1E840); // 130℃校准点 if (cal30 0xFFFFFFFF || cal130 0xFFFFFFFF) { // 校准点无效回退到典型值公式 float vsense (float)raw * 3.3f / 65535.0f; return (25.0f - (vsense - 1.43f) / 0.00286f); } return (100.0f * (float)(raw - cal30) / (float)(cal130 - cal30) 30.0f); }注意不同芯片型号的校准点地址有差异使用前一定对照参考手册的“Temperature sensor calibration values”章节确认。4.3 我的建议与长期维护经验折腾完这个问题我最大的体会是不要轻易在 CubeMX 大版本升级后直接信任生成代码。STM32CubeMX 更新频繁内部通道映射、HAL 库行为、代码生成模板都可能有隐性变化。升级后第一件事就是比较 git diff把升级前后的生成代码差异看一遍尤其是 ADC、DAC、时钟树这几块。另外对于沿用多年的成熟工程CubeMX 版本升级前最好先备份 .ioc 文件并对旧生成的代码做好版本管理。我现在的习惯是所有生成代码固定放到一个Core/Src目录不手工改如果确实要改生成代码就在注释里标明修改原因和日期避免下一个接手的人被坑。如果你遇到的现象和我描述的类似但又在 ADC1/ADC2 上那大概率不是内部通道映射的问题而是校准点读取或采样时间的问题按上面的排查思路处理就行。最后再分享一个小技巧H743 的 ADC3 虽然内部通道映射和 ADC1/2 不同但它在某些封装上没有外部引脚冲突用起来其实很自由。如果你非要继续用 ADC3 读温度传感器修好通道号之后建议在初始化代码里加一行防御性检查打印ADC3-SQR1的 SQ1 位段这样以后就算 CubeMX 再升级也能立刻发现配置有没有被改回去。踩过这次坑之后我所有 H7 项目的 ADC 初始化代码里都加了这种“配置自检”实测下来非常值得。
返回列表