ADC+TIM+DMA采集交流
前言
本文主要讲解定时器触发ADC去采集交流信号,DMA把数据搬移到内存。
所需工具:
- 开发板:STM32F103C8T6
- STM32CubeMX
- IDE: Keil-MDK
相关文章:
模式简介
ADC+TIM+DMA采集交流信号是电赛中使用范围最为广泛的一个技术。这个模式下单个ADC可以实现0-1M的任意可调采样率,采集20khz一下的信号轻轻松松。
F1的ADC支持许多触发信号,这里选择TIM3的TRGO事件作为触发信号,其中TRGO选择更新时间来引起。(这段新手看不懂没关系,不耽误使用)
工程建立
时钟配置
ADC配置
相对于ADC采集直流,这里的触发源不是软件上的一行代码来触发,而是选择外部触发,这里选择TIM3的TRGO信号。
对于新手来说这里可能有疑惑,换成硬件触发有什么好处吗?查看系列的上一篇文章,软件触发ADC采样一次,需要写几行代码,才能让他们采集一次,如果我们想实现100hz的采样率,可以设置一个100hz的定时器中断,在中断里用代码(软件)触发ADC采样,这样确实可以达到100hz采样的效果。可是如果100k采样呢?CPU代码执行的速度是有限的,100hz可以勉强达到,100k就来不及了。但是我让TIM这样的硬件去触发ADC采样,ADC采集完成后,DMA硬件搬运数据,整个采集过程不需要CPU参与。
直观上看就是你告诉ADC,TIM,DMA你们仨给我100k采样率采集1000个点.说完这句话后,他们三就去采集了,CPU只需要等他们采集完成就可以。采集过程CPU不管的,也就是不需要写任何代码。
DMA配置为normal模式。如果配置成circular的话,ADC采集完成指定个数后,不会停下来,不方便管理。读者可以修改成circular看看效果。
采样率控制在100kz,那么TIM就需要产生100khz的TRGO的信号,我们这里选择的更新时间产生TRGO信号,那么TIM3的计数器从0计算到ARR的频率为100khz。于是我们这里设置PSC=0,ARR=720-1。换算下: 72 M 720 = 100 k \frac{72M}{720}=100k 72072M=100k
配置串口
代码生成
代码编写
串口重定向
#include <stdio.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
这里是串口重定向的主体部分
#include <stdio.h>
在mian.c里面包含stdio.h头文件,mian.c里面就可以printf了。别的.c文件同理。
勾选MicroLIB库,否则没法使用printf
ADC采集代码
uint16_t adc_buff[200];//存放ADC采集的数据
/*
AdcConvEnd用来检测ADC是否采集完毕
0:没有采集完毕
1:采集完毕,在stm32f1xx_it里的DMA完成中断进行修改
*/
__IO uint8_t AdcConvEnd = 0;
在main.c里面定义两个变量,一个存放ADC采集到的数据,一个标志ADC是否采集完毕。
特别注意__IO修饰AdcConvEnd。他的含义是volatile。避免AdcConvEnd被MDK优化掉。
HAL_TIM_Base_Start(&htim3); //开启定时器3
HAL_ADCEx_Calibration_Start(&hadc1); //AD校准,F4不用校准没用这行函数。
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buff, 200); //让ADC1去采集200个数,存放到adc_buff数组里
while (!AdcConvEnd) //等待转换完毕
;
for (uint16_t i = 0; i < 200; i++)
{
printf("%.3f\n", adc_buff[i] * 3.3 / 4095); //数据打印,查看结果
}
这里写的采集程序,如每一步的含义都在注释里写明了。
希望读者养成随手写注释的好习惯。
extern uint8_t AdcConvEnd;//引入外部变量
AdcConvEnd = 1;
ADC采集,DMA搬运,当DMA搬运结束后,整个采集过程也就完成了。DMA搬运结束,程序会接收到DMA中断,就会执行DMA1_Channel1_IRQHandler函数,告诉CPU,采集完毕了。程序上则根据AdcConvEnd的变化,得知采集完毕。
硬件连接
引脚 | 连接对象 | 释义 |
---|---|---|
PA9 | CH340的RX | 单片机的TX连接CH340的RX |
PA10 | CH340的TX | 单片机的RX连接CH340的TX |
PA0 | 信号发生器信号端 | 图中红线 |
GND | 信号发生器地 | 跟信号发生器共地 |
上面总共有STlinkV2,ch340,供电线,信号发生器接过来的夹子线
运行结果
ADC去采集信号发生器产生的1k正弦信号,数据打印到VOFA上,结果如图。
为了验证采样率是否是100k,ADC去采集信号发生器产生的5k信号,打印到VOFA上,可以看到一个周期20个点。 5 k ∗ 20 = 100 k 5k*20=100k 5k∗20=100k采样率为100k验证完毕。
VOFA的使用可以在电赛小站里查看到教程。
练习
-
尝试在例程的基础上更改采样率200k、500k等,看看效果。
练练手,打野怪刷熟练度。
-
思考如何借助均值滤波来提高采样精度,并付诸实践。
这是最容易实现的降噪方法。你可以不会FIR、滑动滤波器等,这个还是要会的。元气骑士拿破旧的手枪也是可以通关的嘛。
提示:如果你没什么思路请看这里。比如我们去采集1k的正弦,想采集一个周期100个点,你可以设置采样率为100k,那么采集100个点就结束了。也可以设置采样率为200k,然后一个周期采集200个点,然后每两个点取平均,这样就可以达到2次均值滤波。
啥?还是不懂?就是数组的下标0和下标1取平均( X [ 0 ] + X [ 1 ] 2 = Y [ 0 ] \frac{X_{[0]}+X_{[1]}}{2}=Y_[0] 2X[0]+X[1]=Y[0])作为第一个采样点,下标2和下标3取平均( X [ 2 ] + X [ 3 ] 2 = Y [ 1 ] \frac{X_{[2]}+X_{[3]}}{2}=Y_{[1]} 2X[2]+X[3]=Y[1])作为第二个采样点。
这个方法有个高级的称呼:过采样。H7自带硬件过采样STM32H7 ADC 过采样对精度的影响效果
-
先以100k的采样率采集一组信号,再通过程序更改定时的频率来将采样率改成200k,再采集一组数据。期间不能重新烧录代码
学会动态更改采样率。
提示一下可以采用这种方式更改:
TIM1->ARR=...; TIM1->PSC=...;
-
如果你会测量频率,尝试控制采样率始终是待测信号的100倍。
这技巧在FFT变化方面对提高精度有奇效。
后记
本文章收录于:
本文为系列文章中的冰山一角,欢迎进入小站查看。
配套程序:
公众号:四臂西瓜
公众号回复:32交流采样