STM32F407 HAL+定时器TIM+ADC+DMA计算正弦波的平均值、均方根、最大值、最小值、峰峰值
STM32F407 HAL+定时器TIM+ADC+DMA计算正弦波的平均值、均方根、最大值、最小值、峰峰值。在STM32F407系列微控制器的开发中,结合定时器、ADC(模数转换器)与DMA(直接存储器访问)控制器,能够显著提升数据采集与传输的效率。本文将指导你如何使用STM32 HAL库,通过定时器触发ADC1的单通道采集,利用DMA传输数据,累加的方式计算正弦波的平均值、均方根、最大值、最小值
在STM32F407系列微控制器的开发中,结合定时器、ADC(模数转换器)与DMA(直接存储器访问)控制器,能够显著提升数据采集与传输的效率。本文将指导你如何使用STM32 HAL库,通过定时器触发ADC1的单通道采集,利用DMA传输数据,累加的方式计算正弦波的平均值、均方根、最大值、最小值、峰峰值,最终通过串口将电压值打印出来,仅显示简单演示运算原理,精度不高。具体实现中,我们将读取ADC1的通道5(对应引脚PA5),并将转换得到的电压值发送到串口助手上进行显示。无需DSP库。
一、开发环境
硬件:正点原子探索者 V3 STM32F407开发板
单片机:STM32F407ZGT6
Keil版本:5.32
STM32CubeMX版本:6.9.2
STM32Cube MCU Packges版本:STM32F4xx_DFP.2.14.0
二、配置STM32CubeMX
- 启动STM32CubeMX,新建STM32CubeMX项目:
- 选择MCU:在软件中选择你的STM32型号-STM32F407ZGT6。
-
选择时钟源:

- 配置时钟:

- 使能Debug功能:Serial Wire

- HAL库时基选择:SysTick

- USART1配置:选择异步模式。

-
开启外部时钟:配置系统时钟,确保ADC和串口的外部时钟已开启。
-
配置ADC:

1)选择ADC1作为采集模块。
2)设置ADC1的通道5(对应引脚PA5)为采集通道。
3)配置采样时间和分辨率。通常,采样时间越长,ADC的转换精度越高,但也会增加转换时间。
4)启用连续禁用模式。
5)禁用扫描模式,因为我们只采集一个通道。
10.配置定时器3:
- 时钟源频率:84 MHz
- 预分频器(Prescaler)的值:0
- 周期(Period)的值:840 - 1 = 839(因为是从0开始计数,所以实际周期是840个时钟周期,但寄存器中存储的是839)
- 定时器时钟频率 = 时钟源频率 / (预分频器值 + 1) = 84 MHz / (1 + 1) = 84 MHz / 1 = 84 MHz
- 定时器输出频率 = 定时器时钟频率 / (周期值 + 1) = 假设的定时器时钟频率 84 MHz / 840 = 100 kHz

11.配置工程参数:在Project标签页中,配置项目名称和位置,选择工具链MDK-ARM。
12.生成代码:在Code Generator标签页中,配置工程外设文件与HAL库,勾选头文件.c和.h文件分开,然后点击Project > Generate Code生成代码。 
三、代码实现与部署
-
main.c增加代码:ADC电压换算时用到公式:待测电压=(ADC的返回值/4095)∗3.3V.
#include "math.h" #include <stdio.h> #include <string.h> #define ADC_DMA_BUF_SIZE 4096 /* ADC DMA采集 BUF大小, 应等于ADC通道数的整数倍 */ uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA BUF */ //测量类型 float *g_RMSBUF; /* 波形通道相关的数据结构 */ typedef struct { float WaveMean; /* 平均值 */ float WavePkPk; /* 峰峰值 */ float WaveFreq; /* 频率值 */ float WaveMax; /* 最大值 */ float WaveMin; /* 峰峰值 */ float WaveRMS; /* 均方根 */ }DATA_T; DATA_T data; void WaveProcess(void);//WaveProcess 功能说明: 波形通道最大值,最小值,平均值,峰峰值和RMS的计算 extern __IO uint8_t g_adc_dma_sta; /* DMA传输状态标志, 0,未完成; 1, 已完成 */ // 当前ADC值的索引 volatile uint16_t adc_index = 0; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); MX_TIM3_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(&htim3); //开启定时器3 // 启动ADC采集并通过DMA传输数据到缓冲区 HAL_ADC_Start_DMA(&hadc1, (uint32_t*)g_adc_dma_buf, ADC_DMA_BUF_SIZE); while (!g_adc_dma_sta); //等待转换完毕 for (uint16_t i = 0; i < ADC_DMA_BUF_SIZE; i++) { printf("%.3f\r\n", g_adc_dma_buf[i] * 3.3 / 4095); //数据打印,查看结果 } WaveProcess(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }/* USER CODE BEGIN 4 */ /* ********************************************************************************************************* * 函 数 名: DSO1_WaveProcess * 功能说明: 波形通道1的最大值,最小值,平均值,峰峰值,RMS的计算 * 形 参: 无。 * 返 回 值: 无 ********************************************************************************************************* */ void WaveProcess(void) { unsigned short i; unsigned int uiCycle, uiCount = 0; data.WaveMin = 4095; /* 自动触发模式才计算FFT */ /* 求4096个数值的最大值和最小值 */ for (i = 0; i < ADC_DMA_BUF_SIZE; i++) { data.WaveMean += g_adc_dma_buf[i]; data.WaveRMS += g_adc_dma_buf[i]*g_adc_dma_buf[i]; if(g_adc_dma_buf[i] < data.WaveMin) { data.WaveMin = g_adc_dma_buf[i]; } if(g_adc_dma_buf[i] > data.WaveMax) { data.WaveMax = g_adc_dma_buf[i]; } } /* 求RMS */ data.WaveRMS = sqrt(data.WaveRMS/ADC_DMA_BUF_SIZE) * 3.3f / 4095; /* 求平均值 */ data.WaveMean = data.WaveMean / ADC_DMA_BUF_SIZE *3.3f / 4095; /* 求最大值 */ data.WaveMax = data.WaveMax * 3.3f / 4095; /* 求最小值 */ data.WaveMin = data.WaveMin *3.3f / 4095; /* 求峰峰值 */ data.WavePkPk = data.WaveMax - data.WaveMin; printf("WaveRMS:%.3f\r\n", data.WaveRMS); //数据打印,查看RMS结果 printf("WaveMean:%.3f\r\n",data.WaveMean);//数据打印,查看平均值 printf("WaveMax:%.3f\r\n", data.WaveMax);//数据打印,查看最大值 printf("WaveMin:%.3f\r\n", data.WaveMin);//数据打印,查看最小值 printf("WavePkPk:%.3f\r\n", data.WavePkPk);//数据打印,查看峰峰值 } /* USER CODE END 4 */ - stm32f4xx_it.c增加代码:
ADC(模数转换器)负责进行数据采集,而DMA(直接存储器访问)则负责将这些数据高效地搬运到指定的内存区域。当DMA完成数据的搬运任务后,整个ADC的采集过程也随之宣告结束。此时,DMA会触发一个中断信号,通知CPU搬运操作已经完成。
响应这个DMA中断的是
DMA2_Stream0_IRQHandler函数。一旦此函数被调用,它便会执行一系列操作来告知CPU:ADC的采集工作已经全部完成。在程序层面,我们可以通过检查一个名为g_adc_dma_sta的标志位的状态变化,来确认ADC的采集过程是否已经结束。/* USER CODE BEGIN 0 */ __IO uint8_t g_adc_dma_sta =0; /* USER CODE END 0 *//** * @brief This function handles DMA2 stream0 global interrupt. */ void DMA2_Stream0_IRQHandler(void) { /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */ /* USER CODE END DMA2_Stream0_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_adc1); /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */ g_adc_dma_sta = 1; /* USER CODE END DMA2_Stream0_IRQn 1 */ } - usart.c增加代码:usart.c的第1行添加头文件#include <stdio.h>
#include <string.h>,在末尾用户代码区增加如下代码。printf调用“fputc()”,“fgetc()”,该函数会使用HAL_UART_Transmit发送数据。/* * 添加如下代码,可不在工程设置中勾选Use MicroLIB */ #pragma import(__use_no_semihosting) struct __FILE { int a; }; FILE __stdout; FILE __stdin; void _sys_exit(int x) { } /***************************************************** *function: 写字符文件函数 *param1: 输出的字符 *param2: 文件指针 *return: 输出字符的ASCII码 ******************************************************/ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 10); return ch; } /***************************************************** *function: 读字符文件函数 *param1: 文件指针 *return: 读取字符的ASCII码 ******************************************************/ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, (uint8_t*)&ch, 1, 10); return (int)ch; } - 连接USART1:用USB转TTL工具连接当前硬件USART1的PA9、PA10,GND。
- 打开串口助手:
- 编译代码:Keil编译生成的代码。
-
烧录程序:将编译好的程序用ST-LINK烧录到STM32微控制器中。
四、运行结果
观察结果:一旦程序烧录完成并运行,串口助手打印出PA5的电压,当接地的时候得出的电压值是0,当输入50hz,幅度1v,偏置0.5V正弦波得到如下结果。

当输入50hz,幅度1v,偏置0.5V正弦波得到如下结果。

导入excel看波形

五、注意事项
1.确保你的开发环境和工具已经正确安装和配置。
2.如果没有打印电压值,按一下复位键,检查连接和电源是否正确,注意根据你所用的硬件来接线,不要接错线。
3.在串口打印数据时,要确保波特率等参数与串口助手设置一致。
通过上述步骤,你就可以在STM32F407上实现基于定时器触发的单通道ADC采集与DMA数据传输,并通过累加的方式计算正弦波的平均值、均方根、最大值、最小值、峰峰值,最终通过串口将电压值打印出来,代码尚有不完善的地方,仅显示简单演示运算原理,精度不高,仅供参考。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)