☰
【STM32C5教程】(8)ADC 模拟量数据采集实验
2026/10/17 2:43:29 网站建设 项目流程

欢迎关注 youcans@xidian【嵌入式软件AI编程】专栏
【STM32C5教程】(4)GPIO 外部中断实验
【STM32C5教程】(5)定时器周期中断实验
【STM32C5教程】(6)定时器 PWM 输出实验
【STM32C5教程】(7)TIM1 互补 PWM 输出实验
【STM32C5教程】(8)ADC 模拟量数据采集实验


1. 项目简介

1.1 实验目标

本实验在 NUCLEO-C542RC 开发板上,使用 STM32C542RCT6 的 ADC1 通道 1 采集 PA1 引脚输入的模拟电压,采用软件触发与转换完成中断方式,每 100 ms 启动一次 ADC 采集,读取 12 位 ADC 原始值并换算为输入电压。

功能需求:

  • 使用 ADC1 通道 1 采集 PA1 引脚输入的模拟电压
  • 采用软件触发方式启动转换
  • 采用转换完成中断方式读取结果
  • 每 100 ms 启动一次 ADC 采集
  • 读取 12 位 ADC 原始值并换算为输入电压(mV)

开发板和 MCU 型号:NUCLEO-C542RC 开发板,搭载 STM32C542RCT6 MCU。


1.2 实验条件

硬件:

  • NUCLEO-C542RC 开发板
  • 支持数据传输的 USB Type-C 线(用于连接 ST-LINK 接口)
  • 外部电位器(两端接 3.3 V 与 GND,中间滑动端接 PA1)

软件:

  • STM32CubeMX2(用于引脚、时钟、ADC 配置与代码生成)
  • VS Code + STM32Cube 插件(用于编译、烧录、调试)

2. 硬件说明

2.1 NUCLEO-C542RC 开发板

NUCLEO-C542RC 是 ST 基于 STM32C542RCT6 推出的 Nucleo-64 开发板。主要特性:

  • MCU:STM32C542RCT6,Arm Cortex-M33 内核,最高主频 144 MHz,256 KB Flash,64 KB RAM
  • 调试器:板载 STLINK-V3EC,支持大容量存储、虚拟串口和调试端口三种功能
  • 用户 LED:LD1,连接 PA5
  • 用户按键:B1,连接 PC13
  • 扩展接口:Arduino Uno V3 和 ST morpho 连接器

本实验使用 PA1 作为 ADC 输入,通过外部电位器提供模拟信号源。


2.2 引脚说明

引脚功能说明
PA1ADC1_IN1ADC1 通道 1,模拟输入

PA1(ADC1_IN1):配置为模拟输入模式。ADC1 通道 1 对应 PA1 引脚,不是 PA2(ADC1_IN2)。决定采集哪个引脚的是引脚连接关系——电位器接到 PA1,就必须配置为 ADC1_IN1。


2.3 电位器接线

电位器引脚连接到
一端3.3 V
另一端GND
中间滑动端PA1

调节电位器时,PA1 引脚电压在 0 ~ 3.3 V 范围内线性变化。


3. STM32CubeMX2 工程配置

3.1 新建工程

本实验采用 MCU 方式创建工程,即以芯片型号 STM32C542RC 为起点,而非开发板方式。

  1. 进入 MCU 选择界面
    启动 STM32CubeMX2,在首页点击 MCU,进入芯片型号选择列表。

  2. 搜索并选中芯片
    在 MCU name 输入框中输入 STM32C542RC(或完整型号 STM32C542RCT6),从筛选出的列表中选择对应型号。

  1. 填写项目信息
    输入项目名称 ADC_IT 和项目保存路径。项目名称避免使用特殊字符,路径建议使用纯英文目录。

3.2 基础配置

工程创建后,软件自动进入 Pinout 视图。

时钟配置:本工程系统时钟为48 MHz(HSI ÷ 3),ADC 内核时钟为144 MHz(PSIS 时钟),分频系数为 1。


3.3 ADC1 配置

(1)启用 ADC1

在左侧外设列表中找到Analog→ADC1,点击展开。

(2)配置 ADC 参数

在 ADC1 的Parameter Settings中设置:

参数值说明
Resolution12 bit12 位分辨率
Sampling ModeNormal普通采样模式
Kernel Clock SourcePSISADC 内核时钟源

(3)配置通道

在Channels区域中,将ADC1_IN1的Use Channel设置为启用。

参数值说明
ChannelADC1_IN1对应 PA1 引脚
Sampling Time25 Cycles采样时间
Single/DifferentialSingle Ended单端输入

设置完成后,Pinout 视图中PA1自动分配为ADC1_IN1(模拟模式)。

(4)配置 Regular 组

切换到Groups Conversions标签页,配置 Regular 组:

参数值说明
Trigger TypeSoftware start软件触发
Sequencer Length1序列长度 1
ContinuousSingle单次转换
OverrunOverwritten覆盖模式

将IN1加入正则转换序列。

(5)使能中断

在NVIC Settings中,勾选ADC1 global interrupt使能转换完成中断。


3.4 项目配置

  1. 点击Project Settings,进入工程设置页面。
  2. 在IDE Format下拉选项中选择CMake。
  3. 在CMake Toolchain下拉选项中选择GCC。
  4. 确认配置无误后,点击Generate Code。CubeMX2 会执行配置检查,如果没有错误,开始在指定路径下生成 CMake 工程文件。
  5. 生成完成后,项目路径下会出现标准 CMake 工程结构,包含Core/、Drivers/、CMakeLists.txt等。


4. 编程实现

4.1 实现思路

采用“ADC 初始化 + 软件触发 + 转换完成中断 + 100 ms 非阻塞计时”的方案:

mx_system_init() // CubeMX2 生成:时钟、GPIO、ADC1、NVIC 初始化 │ ▼ HAL_ADC_Start() // 激活 ADC(上电、使能,进入 ACTIVE 状态) │ ▼ HAL_ADC_Calibrate() // ADC 校准(要求处于 ACTIVE 状态) │ ▼ 主循环:每 100 ms 调用一次 HAL_ADC_REG_StartConv_IT() │ ▼ 转换完成,触发 ADC1 中断 │ ▼ HAL_ADC_REG_UnitaryConvCpltCallback() │ ▼ 读取原始值 → 换算电压 → 存入全局变量

为什么用中断而不是轮询?轮询方式需要 CPU 持续等待转换完成,中断方式下 CPU 可以在等待期间执行其他任务,本实验的主循环只需做 100 ms 计时,转换和读取由硬件和中断自动完成。

ADC 操作顺序:HAL_ADC_Start()→HAL_ADC_Calibrate()→HAL_ADC_REG_StartConv_IT()。HAL_ADC_Calibrate()内部要求 ADC 已处于 ACTIVE 状态,因此顺序不可颠倒。

各步骤使用的 HAL 函数:

步骤HAL 函数作用
激活 ADCHAL_ADC_Start()使 ADC 进入 ACTIVE 状态
校准HAL_ADC_Calibrate()ADC 校准
启动转换HAL_ADC_REG_StartConv_IT()软件触发启动单次转换 + EOC 中断
读取原始值HAL_ADC_REG_ReadConversionData()读取 12 位转换结果
电压换算HAL_ADC_CALC_DATA_TO_VOLTAGE()原始值换算为电压(mV)

4.2 关键代码

在 VS Code 中打开生成的 CMake 工程,找到Core/Src/main.c,添加以下代码。

(1)文件头部添加头文件与宏定义

#include"main.h"#include"mx_adc1.h"/* Private define ------------------------------------------------------------*/#defineADC_SAMPLE_PERIOD_MS100U/* 采样周期:每 100ms 启动一次转换 */#defineADC_REF_VOLTAGE_MV3300U/* 模拟参考电压 Vref+ = 3.3V (mV) */#defineADC_RESOLUTIONHAL_ADC_RESOLUTION_12_BIT/* 分辨率 12 位 */

(2)全局变量与句柄

/* Private variables ---------------------------------------------------------*/statichal_adc_handle_t*s_adc1_handle;/* ADC1 句柄(来自 mx_adc1_gethandle)*//* 供调试器监视窗口查看的全局变量 */volatileuint32_tg_adc_raw_value=0U;/* ADC 原始值 0 ~ 4095 */volatileuint32_tg_adc_voltage_mv=0U;/* 换算电压 (mV) 0 ~ 3300 *//* Private functions prototype -----------------------------------------------*/staticvoidadc_start_conversion(void);

(3)主函数

intmain(void){if(mx_system_init()!=SYSTEM_OK){return(-1);}else{/* 获取 ADC1 句柄 */s_adc1_handle=mx_adc1_gethandle();if(s_adc1_handle==NULL){return(-1);}/* ADC 操作流程: 1. HAL_ADC_Start() -- 激活 ADC(上电、使能,进入 ACTIVE 状态) 2. HAL_ADC_Calibrate() -- ADC 校准(要求处于 ACTIVE 状态) 3. HAL_ADC_REG_StartConv_IT() -- 软件触发启动单次转换 + EOC 中断 */if(HAL_ADC_Start(s_adc1_handle)!=HAL_OK){return(-1);}if(HAL_ADC_Calibrate(s_adc1_handle)!=HAL_OK){return(-1);}while(1){/* 非阻塞计时:每 100ms 软件触发启动一次 ADC 单次转换 */adc_start_conversion();}}}

(4)100 ms 非阻塞触发函数

/** * @brief 每 100ms 软件触发启动一次 ADC 单次转换,转换完成后由中断回调处理。 * @note 单次转换 EOC 后 HAL 自动将 regular 组状态恢复为 IDLE, * 故可反复调用 HAL_ADC_REG_StartConv_IT() 而不会返回 HAL_BUSY。 */staticvoidadc_start_conversion(void){staticuint32_tlast_tick=0U;if((HAL_GetTick()-last_tick)>=ADC_SAMPLE_PERIOD_MS){last_tick=HAL_GetTick();(void)HAL_ADC_REG_StartConv_IT(s_adc1_handle);}}

(5)转换完成回调函数

/** * @brief ADC 常规组单次转换完成回调(重载 HAL 弱函数)。 * 读取 12 位 ADC 原始值并换算为输入电压 (mV)。 * @param hadc ADC 句柄指针 */voidHAL_ADC_REG_UnitaryConvCpltCallback(hal_adc_handle_t*hadc){(void)hadc;/* 读取 ADC 原始值(12 位单次转换结果,0 ~ 4095) */g_adc_raw_value=(uint32_t)HAL_ADC_REG_ReadConversionData(s_adc1_handle);/* 电压换算:mV = raw * Vref / 满刻度(4095) (ST 官方宏,第三个参数须为 HAL_ADC_RESOLUTION_12_BIT 枚举值,单位 mV) */g_adc_voltage_mv=(uint32_t)HAL_ADC_CALC_DATA_TO_VOLTAGE(ADC_REF_VOLTAGE_MV,g_adc_raw_value,ADC_RESOLUTION);}

代码说明:

  • mx_adc1_gethandle() 返回 CubeMX2 生成的 ADC1 句柄。
  • HAL_ADC_Start() 使 ADC 进入 ACTIVE 状态,必须先调用。
  • HAL_ADC_Calibrate() 校准 ADC,要求 ADC 已处于 ACTIVE 状态,所以必须在 HAL_ADC_Start() 之后调用。
  • HAL_ADC_REG_StartConv_IT() 以软件触发方式启动一次单次转换,并使能转换完成中断。
  • HAL_ADC_REG_UnitaryConvCpltCallback() 是 HAL2 中的转换完成回调函数,用户重写这个同名函数,中断发生时 HAL 自动调用它。
  • HAL_ADC_REG_ReadConversionData() 读取 12 位转换结果。
  • HAL_ADC_CALC_DATA_TO_VOLTAGE() 是 ST 官方宏,将原始值换算为电压(mV)。第三个参数必须传 HAL_ADC_RESOLUTION_12_BIT 枚举值。
  • g_adc_raw_value 和 g_adc_voltage_mv 声明为 volatile,供调试器监视窗口查看。

5. 编译烧录与运行测试

5.1 编译与烧录

  1. 打开工程:用 VS Code 打开 CubeMX2 生成的 CMake 工程文件夹(如 ADC_IT_cmake)。
  2. 编译:点击 VS Code 底部状态栏的Build按钮,或使用命令面板执行 CMake 构建。编译完成后,终端应输出退出代码为 0,无报错提示。
  3. 连接开发板:用支持数据传输的 USB Type-C 线连接 NUCLEO-C542RC 的 ST-LINK USB 口(CN1)。
  4. 确认设备识别:打开设备管理器,在“端口 (COM 和 LPT)”下应能看到STLink COMx。
  5. 烧录:点击 VS Code 中的运行按钮,将固件下载到开发板。


5.2 运行与测试

烧录完成后,程序自动开始运行。使用调试器监视窗口查看 g_adc_raw_value 和 g_adc_voltage_mv 两个全局变量的值。

测试方法:用断点读取稳定值

  1. 在 HAL_ADC_REG_UnitaryConvCpltCallback() 回调内最后一条赋值语句(换算 g_adc_voltage_mv 那一行)设断点。停住时正是本次转换结果刚写入两个变量的瞬间,数值稳定不再变化。
  2. 程序每 100 ms 停在断点一次(停在 ADC 中断上下文,不影响查看全局变量)。
  3. 分别在 PA1 接 GND、接 3.3 V、接电位器中间挡位三种状态下,记录变量 g_adc_raw_value 和 g_adc_voltage_mv 的值。

  • 测试 1:PA1 接 GND
    通过电位器调到头使 PA1 电压为 0 V,观察监视窗口:

    • g_adc_raw_value 接近 0
    • g_adc_voltage_mv 接近 0 mV
  • 测试 2:PA1 接 3.3 V
    通过电位器调到头使 PA1 电压为 3.3 V,观察监视窗口:

    • g_adc_raw_value 接近 4095
    • g_adc_voltage_mv 接近 3300 mV
  • 测试 3:PA1 接电位器中间挡位
    调节电位器到中间位置,观察监视窗口:

    • g_adc_raw_value 在 0 ~ 4095 之间
    • g_adc_voltage_mv 与原始值按公式 电压 = raw × 3300 / 4095 对应

测试结果:

PA1 连接原始值 g_adc_raw_value电压 g_adc_voltage_mv是否正常
→ GND10 mV✅
→ 3.3 V40913296 mV✅
→ 电位器中间24631968 mV✅

测试结果总结:
☑ 编译通过,无错误
☑ 烧录成功
☑ ADC1 通道 1 正确采集 PA1 电压
☑ 软件触发 + 转换完成中断方式工作正常
☑ 每 100 ms 启动一次采集
☑ 12 位原始值读取与电压换算正确
功能正常。


6. 小结

本实验通过 ADC1 通道 1 采集 PA1 引脚输入的模拟电压,实现了软件触发、转换完成中断、12 位原始值读取与电压换算,掌握了 STM32C5 系列 ADC 的配置与使用方法。

核心逻辑可以概括为:“ADC 激活 → 校准 → 每 100 ms 软件触发单次转换 → 中断中读取原始值 → 换算电压”。

有两点需要注意:

  • ADC 操作顺序不能颠倒。
    HAL_ADC_Start() 先使 ADC 进入 ACTIVE 状态,然后才能调用 HAL_ADC_Calibrate() 校准,最后才能用 HAL_ADC_REG_StartConv_IT() 启动转换。

  • HAL2 的回调函数名与经典 HAL 不同。
    转换完成回调是 HAL_ADC_REG_UnitaryConvCpltCallback(),启动函数是 HAL_ADC_REG_StartConv_IT(),与经典 HAL 的 HAL_ADC_ConvCpltCallback() 和 HAL_ADC_Start_IT() 不同,使用时需注意区分。


版权声明:

欢迎关注 youcans@xidian【嵌入式软件AI编程】专栏
转发必须注明原文链接:
【STM32C5教程】(8)ADC 模拟量数据采集实验
Copyright by ai4mcu@qq.com 2026
Crated:2026-10

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询