STM32 HAL库电赛信号处理教程详解
目录
adc基础-adc-tim-dma
前言:
相关知识:
1、ADC-模拟数字转换器
2、TIM-定时器
3、DMA-直接储存器访问
正文:
一、时钟树配置
二、基本配置
三、TIM定时器配置
四、adc配置
五、代码示例
补充:
编辑
结语:
adc基础-adc-tim-dma
前言:
刚刚参加完2024年全国大学生电子设计竞赛,在备赛期间上学习了很多信号类的知识点,所以想跟大家分享一下。本次教程基于stm32f407系列单片机,使用keil5、stm32cubemx开发,将持续分享关于adc、dma、tim、fft、fir的使用。本次将先分享adc基础使用——adc-tim-dma HAL库配置与使用。
相关知识:
1、ADC-模拟数字转换器
STM32F4xx 系列一般都有 3 个 ADC,这些 ADC 可以独立使用(即独立模式),也可以使用双重/三重模式以提高采样率。它们都可以配置分辨率、采样方式、通道。
——具体可参考《STM32F4之ADC介绍》
2、TIM-定时器
STM32F4 板载了高级控制定时器(TIM1和TIM8,这两是挂在APB总线上)、通用定时器以及基本定时器,总共达14个定时器之多。
——可参考《STM32F4_定时器精讲(TIM)》
3、DMA-直接储存器访问
DMA用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。adc采样时往往涉及到大量数据的转移,用dma可以节省CPU资源。stm32f4dma具有两个dma,每个dma具有8个数据流,每个数据流又有8个通道可以选择。
正文:
一、时钟树配置
这边就不详细解释时钟树的原理,大家可以自行学习。为了省时省力,大家可以如图,直接将其配置为168。注意:时钟树的配置将影响tim-adc的采样频率,配置符合大家要求就好。
二、基本配置
串口配置:
使用USART1,在代码使用到了printf,需要先进行重定向,有多种方法,且网上教程很多,这里就不在赘述啦。
三、TIM定时器配置
这里我们选择TIM3定时器,它是挂在APB1总线上(可在数据手册中查阅),时钟为168/2=84M。我们配置分频为x,重装载值为y,则采样率为84M/(x+1)/(y+1)=z。注意:stm32单片机单ADC独立模式最大采样率为2.4M,使用三重ADC交替采样最大采样率为7.2M(后续会分享配置方法),如果采样率设置过高,会导致采样结果出错。
四、adc配置
ADC基础配置:
DMA配置:
五、代码示例
先进行数组定义等,这里我将ADC数据个数、采样率进行了宏定义,方便程序修改与调用。采样率为10K,ADC数据个数为1024(这里我是为了方便进行fft,大家可以按需求更改)。
注意:这里数组定义使用的是uint32_t,是因为我们前面DMA配置的是也word进行传输。如果将前面DMA配置为 half word 到 half word,则需要使用uint16_t,否则采集的数据数据传输出来会出错,这是一个小坑。
#define SAM_FRE 10000 //采样率
#define ADC1_DMA_Size 1024 //ADC数据个数
uint32_t ADC1_ConvertedValue[ ADC1_DMA_Size ]; //定义ADC数据存储数组
在main.c中先打开定时器三,再调用函数 HAL_ADC_Start_DMA开启ADC-DMA传输,这里我在while中也加上了 HAL_ADC_Start_DMA函数,是什么原因呢?大家应该还记得,我们在DMA配置时使用的是普通模式,这意味着DMA传输完这1024个数据后就会自动停止,等待我们再次开始它,所以在while中加入这个函数不断开启。但如果我们前面使用循环模式,就不需要了,在循环模式下,DMA会不断的传输,完成后有自动打开。
HAL_TIM_Base_Start(&htim3);
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_ConvertedValue,ADC1_DMA_Size);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_ConvertedValue,ADC1_DMA_Size);
在while里我们就可以进行数据处理,并把结果打印出来。(我这进行了处理,打印出来的是电压真实值)。
for(i=0;i<FFT_LENGTH;i++)
{
printf("%f\r\n",ADC1_ConvertedValue[ i ]*3.3f/4095.0f);
}
补充:
每次DMA传输完成后都会进入一个中断函数 DMA2_Stream0_IRQHandler,大家可以在这里面设置一个标志位,防止在处理数据时DMA传输导致数据改变。
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 */
flag=1;
/* USER CODE END DMA2_Stream0_IRQn 1 */
}
结语:
ADC、DMA都是stm32中比较复杂的外设,我也是在不断尝试中发现问题、解决问题。第一次发博客,如果有错误或建议,欢迎大家指出。后续会给大家分享fft、fir的使用和三重adc同步采样、交替采样的配置。祝大家学业进步!
作者:哦呜呜呜呜