STM32CubeMX + HAL库:基于ADC内部通道信号源的芯片温度监测实现
本文介绍了基于STM32CubeMX+KeilMDK+VSCode的协同开发模式,采用HAL库实现ADC温度监测和多通道数据采集。通过图形化配置完成外设初始化,利用DMA双缓冲机制实现无阻塞数据传输,最终通过UART输出温度与电压数据。文章详细阐述了从时钟配置、ADC参数设置到代码实现的完整流程,重点解决了温度测量异常等常见问题,为STM32开发提供了实用参考方案。该方案特别适合作为HAL库应用、
本文采用图形化配置+专业编译+轻量编辑的协同开发模式:通过 STM32CubeMX 完成外设配置与基础代码生成,利用 Keil MDK 进行工程构建、编译调试及烧录验证,最终在 VSCode 中完成核心业务逻辑开发。这种分工明确、优势互补的工具链组合已成为当前STM32工程开发的黄金实践方案。
在编程实现上,本文选用HAL库作为开发框架。相较于传统寄存器操作与标准库开发,HAL库凭借其硬件抽象层设计、跨系列兼容性及STM32官方长期维护等优势,已成为工业现场的主流选择。

实验部分重点实现两大功能:
- 片上温度监测:通过ADC读取内部温度传感器通道信号,经线性转换公式计算实时芯片温度
- 多通道数据采集:同步扫描外部引脚电压信号,采用DMA双缓冲机制实现无阻塞传输,最终通过UART将温度值(℃)与电压值(V)格式化输出至终端。
项目完整演示了从硬件配置到算法实现的全流程开发,特别适合作为:
- STM32多工具链协同开发入门案例
- HAL库高级应用实践参考
- ADC+DMA+UART组合外设开发范本
1. 图形化配置
1.1 SYS配置
作者选择的是JLink V8下载调试器,所以选择serial wire调试,如果用STlink 同样也是这个选项。
时钟源选择系统时钟systick

1.2 RCC 配置
使用外部晶振,市面上几乎所以STM32的板子都有外部8M晶振

按如下图形配置时钟源,固定不变的,虽然可以有N种配置方案,但几乎所有软件代码都是基于这套方案配置的,高速时钟72MHz 低速时钟36MHz
注意:因为此时还没配置ADC 所以下面ADC时钟应该是灰色无法配置的(作者截图是已经配置好ADC的样子,所以ADC不是灰色的)

1.3 USART1配置
为了方便看打印结果,配置一个串口输出结果。
注:硬件要使用串口连接电脑,并打开串口调试工具才能看到

1.4 GPIO配置
为了显示系统是否在运行,配置一个LED灯进行翻转指示芯片是否在运行while程序
1.5 ADC1配置
采样周期尽量选最大,这样才有充分的时间进行温度采集和转换,同时我还配置了通道1同时进行电压测试。
因为此时有不止一个通道,所以要开启扫描模式,多通道顺序扫描,挨个转换
Scan conversion Mode: enabled
打开循环模式,关掉间断模式,通道组内转换完一轮后继续转换下一轮,让ADC不断进行信号采集转换,不要停

为了减少CPU的负担,采用了DMA传输转换数据
1.6 NVIC配置
取消DMA中断,因为DMA中断是默认打开的,因为ADC转换采集频率相对较高,为了不必要的高频中断,这里进行取消

1.6 project配置

最后点击右上角 generate code 生成代码 并打开keil 软件
2. 工程设置
2.1 target配置
为了实现串口打印重定向功能,引入一个包

2.2 debug配置
我用的Jlink 所以这里选择这个选项,如果用的stlink 就选择对应选项即可

下载速率搞到最大,选择JTAG端口
避免每次下载程序后还要按复位键才执行最新代码,一定要选那个红色选项
配置完后,关闭keil 这些配置就自动保存了,并生成对应配置文件,此时再使用VScode打开即可
注:一定要关掉keil再打开VScode,如果不关掉keil,那些配置就不会生成
3. 代码编写
3.1 USSRT1打印重定向
只需要写下如图几行代码即可实现串口打印重定向,重定向的意思就是,串口输出打印内容不再使用复杂的函数,只需要用 printf() 加上要打印的内容即可,非常方便调试,这也是除了仿真以外最重要的一种调试手段了。
/* USER CODE BEGIN 1 */
int fputc(int ch, FILE * file){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch, 1, 1000);
return ch;
}
/* USER CODE END 1 */
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
注:include代码是写在usart.h文件中的,看截图所示,如果刚打开工程,只有usart.c文件,没有usart.h文件,只需要编译一次工程,即可看到

3.2 参数定义
在main函数中进行编码,先定义温度计算相关的参数,和dma的变量
/* USER CODE BEGIN PD */
// 温度传感器参数(参考 STM32F103 手册)
#define V25 1.43f // 25°C 时的电压(典型值)
#define AVG_SLOPE 0.0043f // 平均斜率(4.3 mV/°C)
#define VREF 3.3f // ADC 参考电压(默认 3.3V)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint16_t adc_values[2] = {0};
/* USER CODE END PV */

3.3 开启ADC
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_values,2);
/* USER CODE END 2 */
第一行代码是校验ADC,第二行是开启ADC

3.4 计算温度并循环打印
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("Hello World!\n"); // 打印调试信息
// 切换LED状态
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
// 读取 ADC 值(DMA 自动更新 adc_values)
uint16_t adc_voltage = adc_values[0]; // 假设第一个通道是电压
uint16_t adc_temp = adc_values[1]; // 假设第二个通道是温度
// 计算电压(假设第一个通道测量外部电压)
float voltage = (adc_voltage * VREF) / 4095.0f;
// 计算温度(第二个通道是温度传感器)
float v_sense = (adc_temp * VREF) / 4095.0f;
float temperature = (V25 - v_sense) / AVG_SLOPE + 25.0f;
// 打印结果
printf("Voltage Channel: %u, Voltage: %.3f V \r\t\n", adc_voltage, voltage);
printf("Temp Channel: %u, V_sense: %.3f V, Temperature: %.2f C \r\t\n",
adc_temp, v_sense, temperature);
HAL_Delay(2000); // 2秒延迟(若需更高实时性,改用定时器中断)
}
分别从DMA中取出两个通道的值,然后一个转换成电压,一个转换成温度,这个公式就是固定的。

4. 编译下载验证
因为内部集成的温度传感器精度不高,误差在1.5度左右,所以每秒钟测量的值在这个区间抖动,用手去摸芯片,能明显看到温度上升。
ADC1通道1对应的引脚是PA1,可以用杜邦线把这个引脚连接0V和3.3V之间的任意电压,都可以测量出来,注意不要超过3.3V,否则烧芯片。

注意:
在做这个实验的时候,作者困扰了好久,测量出来的温度是一个负数,打印出来的DMA值一直都是4095不变,而对比通道1却能够正常测量电压。
原因是硬件方便,参考电压VREF没有接地和接电源导致的,一定要注意这两个引脚分别接上正负极,一般的板子会空出来,需要用短路帽或者杜邦线进行连接。

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)