【动手学STM32G4】(1)STM32G431之导入和创建项目
【动手学STM32G4】(2)STM32G431之外部中断
【动手学STM32G4】(13)STM32G431之 TIM+ADC
【动手学STM32G4】(13)STM32G431之 TIM+ADC
-
- [1. 项目简介](#1. 项目简介)
-
- [1.1 定时触发采样的重要性](#1.1 定时触发采样的重要性)
- [1.2 定时器触发ADC的原理](#1.2 定时器触发ADC的原理)
- [2. CubeMX工程配置](#2. CubeMX工程配置)
-
- [2.1 CubeMX 工程配置](#2.1 CubeMX 工程配置)
- [2.2 ADC 配置](#2.2 ADC 配置)
- [2.3 TIM 配置](#2.3 TIM 配置)
- [2.4 工程配置](#2.4 工程配置)
- [3. CubeIDE 编程与调试](#3. CubeIDE 编程与调试)
-
- [2.1 CubeIDE编程](#2.1 CubeIDE编程)
- [2.2 编译与调试](#2.2 编译与调试)
- [4. 小结](#4. 小结)
1. 项目简介
在前文【动手学STM32G4】(5)STM32G431之ADC采样 中,我们介绍了STM32G4的ADC采样系统构建,实现了电位器电压的采集。
本文介绍使用定时器触发 ADC 转换,并使用 DMA 进行数据传输。通过设置定时器的触发间隔,就能实现 ADC 定时采样转换功能。
1.1 定时触发采样的重要性
在嵌入式数据采集系统中,时序精度往往决定了整个系统的性能上限。软件触发 ADC 采样方式存在诸多局限:CPU需要频繁响应中断,采样间隔受限于中断延迟,多任务环境下难以保证时间一致性。
定时触发采样通过硬件机制确保了数据采集的时间精度,实现了纳秒级的时间一致性,这对于需要严格时序控制的电机控制、音频处理等应用至关重要;同时,它将CPU从频繁的中断处理中解放出来,配合DMA大幅提升了系统效率;更重要的是,定时器能够精确协调ADC、PWM、DAC等多个外设的协同工作,为复杂嵌入式系统提供了硬件级的同步保障;在抗干扰方面,硬件触发独立于CPU执行,有效避免了软件调度带来的时序抖动,增强了系统的稳定性。
1.2 定时器触发ADC的原理
STM32G431的TIM1作为高级定时器,具备独立的时钟源和可配置的触发输出功能,能够产生精确的时间基准。当TIM1配置为触发ADC时,其工作原理如下:
定时器在计数达到预设定值后产生更新事件(UEV),这个硬件事件通过TIM1的TRGO(Trigger Output)输出,直接连接到ADC的外部触发输入。ADC检测到这个触发信号后,立即启动转换过程,无需CPU干预。这种硬件级联动确保了采样时刻的精确性和可重复性,将时间抖动控制在纳秒级别。
关键时序关系可表示为:
bash
定时器配置:
时钟源 → PSC分频 → 计数器 → ARR重载 → 更新事件
↓
触发中断 & 触发ADC检测
↓
ADC启动转换 → 开始转换
触发延迟主要包括ADC的采样时间和转换时间,这些参数在CubeMX中可配置,为系统设计提供了灵活性和可预测性。
2. CubeMX工程配置
2.1 CubeMX 工程配置
-
新建工程。
启动 STM32CubeMX,点击 "Start New Project" (或Ctrl-N快捷键)新建工程,进入 New Project 界面。
选择MCU为 STM32G431RBT6(参考开发板的 MCU 型号选择)。
选择开发板为 NUCLEO-G431RB 开发板。
点击右上角 "Start Project" 创建项目。 -
配置系统时钟树。
(1)点击顶部 "Clock Configuration" 选项卡,进入时钟树配置。
(2)设置输入频率 Input frequency=24 MHz,SysCLK frequency=160 MHz。
配置完成后,System Clock 显示为160MHz,HCLK、PCLK1、PCLK2 均为160MHz。
bash
Input frequency: 24 MHz (HSE)
SysCLK frequency: 160 MHz (HSE)
System Clock Mux: PLLCLK
AHB Prescaler: 1 (不分频)
APB1 Prescaler: 1
APB2 Prescaler: 1

-
系统配置:在引脚配置(Pinout & Configuration)中,选择 "System Core -- SYS" 。
(1)设置调试器类型,将 Debug 模式设为 "Serial Wire"。
(2)设置基础时钟源(Timebase Source),可以选择默认设置 "SysTick"。
(3)时钟配置:在引脚配置(Pinout & Configuration)中,选择 "System Core -- RCC" 配置时钟模式。
设置高速时钟为外部晶振,将 High Speed Clock (HSE) 设为 "Crystal/Ceramic Resonator"。
Low Speed Clock (LSE) 设为 "Disable"(本实验不需要)。
-
GPIO 配置。
(1)将 LD2(PA5)配置为输出模式 "GPIO_Output"。
(2)将用户按键(PC13)配置为外部中断 "GPIO_EXTI13"。
-
配置设置虚拟串口(LPUART)
使用串口发送 ADC/DAC 数据到 PC 来显示波形:
启用 LPUART1,模式设为 Asynchronous;波特率设置为 115200;引脚使用默认的 PA2(TX)/PA3(RX),连接到 Nucleo 板上的 ST-LINK 虚拟串口。
用于高频率发送数据到上位机或更新波形时,可以考虑为 LPUART1_TX 分配 DMA 通道。
2.2 ADC 配置
Nucleo-G431RB 开发板上没有板载旋钮,堆叠插接 X-NUCLEO-IHM16M1 电机驱动扩展板 后,PC2 引脚连接到 NUCLEO-IHM16M1 驱动板上的板载旋钮电位器。
- 启用并配置 ADC 通道
(1)选择 "引脚配置(Pinout & Configuration)",从左侧下拉列表中选择 "Analog -- ADC1" ,使用 IN8(PC2) 作为模拟输入通道,将 IN8 设置为 "IN8 Singel-ended"。
(2)开启独立通道 IN8,设置为 "IN1 Single-ended"。
(3)在 Pinout view 视图中,将 PC2 引脚配置为 "ADC1_IN8"。PC2 引脚颜色变为绿色,表示已配置为模拟输入模式。
(4)在 NVIC 设置中为 ADC1 启用中断,以便在回调函数中 ADC 转换结束时可以切换 GPIO。

(5)配置 ADC 工作模式和参数,注意触发源配置为 Timer 1 定时器触发。
bash
ADCs_Common_Settings
Mode: Independent mode
ADC_Settings
Clock Prescaler: Asynchronous clock mode divided by 4
Resolution: ADC 12-bit resolution
Data Alignment: Right alignment(右对齐)
Gain Compensation: 0
Scan Conversion Mode:Disable
End of Conversion Selection:End of single of conversion
Continuous Conversion Mode: Disable(单次转换)
DMA Continuous Requests: Disabled
ADC_Regular_ConversionMode:
Number of Conversion: 1
External Trigger Conversion Source: Timer 1 Trigger Out event
External Trigger Conversion Edge: Trigger detection on the rising edge
Rank 1:
Channel: Channel 8
Smapling Time: 24.5 Cycles
Offset Number: No offset
youcans@qq.com

2.3 TIM 配置
配置定时器 TIM1 中断频率为 1kHz。
计算预分频器(PSC)
TIM1 挂载在APB1总线,时钟频率 = 160MHz。
所需中断频率 = 10kHz。考虑ARR通常设为较小值以获得高分辨率,选择 PSC = 159,分频后频率 = 160MHz / (159+1) = 160MHz / 160 = 1000kHz。
计算自动重载值(ARR):
中断频率 = 分频后频率 / (ARR+1)
ARR = (分频后频率 / 中断频率) - 1 = (1000kHz / 1kHz) - 1 = 1000 - 1 = 999
| 参数 | 设置值 | 说明 |
|---|---|---|
| Clock Source | Internal Clock | 使用内部时钟源 |
| Prescaler (PSC) | 159 | 预分频值 |
| Counter Mode | Up | 向上计数模式 |
| Counter Period (ARR) | 999 | 自动重载值 |
| auto-reload preload | Disabled | 禁用自动重载预装载 |
| Trigger Event Selection | Update Event | 选择更新事件作为触发输出 |
- 启用并配置TIM1 :
(1)选择 "引脚配置(Pinout & Configuration)",从左侧下拉列表中选择 "Timers -- TIM1 -- TIM1 Mode and Configuration " ,将通道1 配置为生成但不输出 PWM: "PWM Generation No Output",用于定时器内部触发 ADC。

2.4 工程配置
- 工程管理配置。
(1)点击 "Project Manager"→ "Project",输入项目名称 "STM32G431_ADC02"。。
(2)点击 "Project Manager"→ "Code Generator",设置如下。
bash
Project:
- Project Name: STM32G431_ADC02
- Toolchain/IDE: STM32CubeIDE
Code Generator:
√ Generate peripheral initialization as a pair of '.c/.h'
√ Keep User Code when re-generating
√ Backup previously generated files
youcans@qq.com
- 生成代码。
(1)点击右上角"GENERATE CODE"按钮,将自动由工程文件 "STM32G431_ADC02.ioc" 生成初始代码。
(2)生成初始代码完成后,弹出代码生成提示窗口,点击 "Open Project" 直接进入 STM32CubeIDE 并打开代码。
3. CubeIDE 编程与调试
2.1 CubeIDE编程
-
在 STM32CubeIDE 打开代码文件 main.c。
代码生成后,已经自动进入 STM32CubeIDE,并打开创建的 STM32G431_DAC01 项目。在 "Core\Src" 目录中,已经生成了 dac.c 和 main.c 等基础程序。
-
程序 main.c 的架构如下:
c
main.c
├── 系统初始化
│ ├── 系统时钟配置(160MHz)
│ ├── GPIO初始化
│ ├── TIM1初始化
│ └── ADC1初始化
├── 主程序
│ ├── ADC校准
│ ├── 启动ADC中断模式
│ ├── 启动TIM1 PWM输出
│ ├── 配置TIM1 主从模式
│ └── 主循环
│ └── 串口输出 ADC 数据(可选)
└── 中断服务
└── HAL_ADC_ConvCpltCallback()
├── 读取ADC转换值
├── 更新转换计数器
└── 每10次转换切换LED(可选)
youcans@qq.com
- 生成代码并添加变量:
c
/* USER CODE BEGIN PV */
// 用户变量
volatile uint16_t adc_raw = 0; // ADC 转换原值
volatile uint16_t adc_count = 0; // ADC 转换计数/
* USER CODE END PV */
- 主函数初始化后添加代码:
c
/* USER CODE BEGIN 2 */
// 校准ADC(youcans@qq.com)
if(HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
Error_Handler();
// 使能 ADC 中断
if(HAL_ADC_Start_IT(&hadc1) != HAL_OK)
Error_Handler();
// 启动 TIM1 CH1 的 PWM 输出(用于触发ADC)
if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
Error_Handler();
- 添加ADC转换完成回调函数:
c
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
// 获取ADC转换值(youcans@qq.com)
adc_raw = HAL_ADC_GetValue(&hadc1);
adc_count++;
// 每10次转换切换一次LED(便于观察)
if (adc_count == 10)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
adc_count = 0;
}
}
}
- 添加串口调试功能:
c
/* Infinite loop youcans@qq.com*/
/* USER CODE BEGIN WHILE */
while (1)
{
// Update the transmission
float Vch1 = adc_raw * 3.3f / 4095.0f;
float Vch2 = 0; // low level
float Vch3 = 3.3; // high level
// New DMA transmission can only be initiated after the previous one is completed
if (txReady)
{
txReady = 0;
frame[0].f = Vch1; // VOFA+CH1
frame[1].f = Vch2; // VOFA+CH2
frame[2].f = Vch3; // VOFA+CH3
// The frame tail at frame[3] is a fixed value
// A total of 4 floats (3 channels of data + 1 frame tail), 16 bytes in total.
HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)frame, 4*sizeof(float));
}
HAL_Delay(1); // 1ms
}

2.2 编译与调试
-
程序编辑、编译与调试:
点击工具栏中 "Build Debug" 按键对程序代码进行编译。
点击工具栏中 "Debug" 按键,将程序下载烧录到目标板 NUCLEO-G431RB 。
点击工具栏中 "Resume" 按键 或 F8 快捷键,运行程序。
-
频率验证:用示波器测量PA5(LD2)引脚,看到 50Hz方波,如下图所示。
TIM1 中断频率为 1kHz,LED 每10次转换(10ms)切换一次,因此 LED 的闪烁周期为 50Hz。这说明 采样频率精确控制为1kHz。

- 采样结果验证:
板载电位器连接到PA0(ADC1_IN8),提供0-3.3V模拟电压。旋转板载电位器,通过串口查看 ADC 值变化,如下图所示。
AD 转换的最小值为 0 ,最大值为 3.3,输出波形随电位器而变化,说明 AD 采样正常。

4. 小结
本实验成功实现了基于STM32G431的定时器触发ADC周期性采样系统。通过配置TIM2定时器产生精确的10kHz触发信号,驱动ADC1对板载电位器电压进行周期性采样,并在每次转换完成后通过中断回调函数处理数据,同时利用LED的50Hz方波输出直观验证系统运行状态。
本实验不仅实现了基本的ADC采样功能,更重要的是构建了一个可扩展、可验证、高可靠性的数据采集框架。通过严格的时序控制和中断管理,为后续更复杂的嵌入式应用开发奠定了坚实基础。
版权声明:
【动手学电机驱动】是 youcans@qq 原创作品,转载必须标注原文链接:【动手学STM32G4】(13)STM32G431之 TIM+ADC (https://blog.csdn.net/youcans/article/details/157174163)
Copyright@youcans 2026
Crated:2026-1