stm32 ,DMA,adc,fft. . .(基于库函数)

发布者:电子科技爱好者最新更新时间:2025-11-03 来源: bilibili关键字:stm32  DMA  adc  fft  库函数 手机看文章 扫描二维码
随时随地手机看文章

用TIM2定时器触发adc,DMA采集之后进行fft转换.

fft是一种快速傅里叶算法.

官方给的256点运算时间仅需要0.362ms,1024点也只要2.138ms(72mHz)

傅里叶变换就像是将一桶各个颜色混杂在一起的油漆分离出来.并将每种颜料的含量也计算出来. 而各种颜色代表了各种频率,含量代表幅值.(个人的浅显理解)

了解个差不多就可以开始了~

先将fft的官方库添加到自己的工程.(https://pan.baidu.com/s/1Gw1NXCa3q8SHxc-E1H-6yw 提取码:m1et)

添加之后可以看到官方提供了两种快速傅里叶变换函数,分别为256点和1024点.这里视自己情况进行使用.不过要注意在这之前要调用stm32_dsp.h和table_fft.h

官方库.

定时器2配置:

GPIO_InitTypeDef GPIO_InitStruct;

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

TIM_OCInitTypeDef TIM_OCInitStruct;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  //打开定时器2的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //打开GPIOA的时钟

GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Pin =GPIO_Pin_1;

GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStruct); //设置gpio端口的工作方式,为推挽复用输出

TIM_TimeBaseInitStruct.TIM_ClockDivision =0;

TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up;

TIM_TimeBaseInitStruct.TIM_Period =arr;

TIM_TimeBaseInitStruct.TIM_Prescaler =psc;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); //设置定时器的装载值和分频系数

TIM_OCInitStruct.TIM_OCMode =TIM_OCMode_PWM1 ;

TIM_OCInitStruct.TIM_OCPolarity =TIM_OCPolarity_High;

TIM_OCInitStruct.TIM_OutputState =TIM_OutputState_Enable;

TIM_OCInitStruct.TIM_Pulse =10;

TIM_OC2Init(TIM2, &TIM_OCInitStruct); //设置定时器的PWM已经他的占空比 占空比在这里没有实际的意义  只是希望定时器能够提供上升沿信号给adc,然后触发adc的转换

TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //打开输出比较通道2的自动重装值

TIM_Cmd(TIM2, ENABLE); //使能定时器2

ADC配置:

ADC_InitTypeDef ADC_InitStructure; 

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );   //使能ADC1通道时钟

RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

//PA2.3.4.5 作为模拟通道输入引脚                         

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚

GPIO_Init(GPIOA, &GPIO_InitStructure);

ADC_DeInit(ADC1);  //复位ADC1 

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作 方式

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单词转换

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //转换由外部定时器2的通道2输出的PWM波来实现adc的触发

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目

ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器    

ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1

ADC_ResetCalibration(ADC1); //使能复位校准  

while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

ADC_StartCalibration(ADC1); //开启AD校准

while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束

// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能

ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1,ADC_SampleTime_1Cycles5); //ADC1,PA5.

ADC_ExternalTrigConvCmd(ADC1, ENABLE); //使能外部触发中断转换

ADC_DMACmd(ADC1, ENABLE); //开启ADC1的DMA传输

DMA配置:

NVIC_InitTypeDef NVIC_InitStruct;

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输

DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值

DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址

DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取,发送到内存空间

DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;  //数据宽度为32位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //数据宽度为32位

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循环模式

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输

DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据指定的参数初始化DMA的通道

NVIC_InitStruct.NVIC_IRQChannel =DMA1_Channel1_IRQn;

NVIC_InitStruct.NVIC_IRQChannelCmd =ENABLE;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =1;

NVIC_InitStruct.NVIC_IRQChannelSubPriority =0;

NVIC_Init(& NVIC_InitStruct); //开启DMA1通道1的中断,并编写中断服务函数,若有什么任务要执行,则可以再dma传输完成之后挂起标志位,然后执行相应的dma传输完成的程序

DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);//开启定时器dma1的中断,并且使能相应的中断类型

DMA_Cmd(DMA_CHx, ENABLE);  //使能 通道 

使用DMA1通道1,DMA外设地址是(u32)&ADC1->DR,内存地址为自己存储数据的地址.数据长度为256(一次dma转换所采集的数据量,如果是1024点就改为1024)DMA中断处理函数:

void DMA1_Channel1_IRQHandler(void)

{

if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)

{

DMA_ClearITPendingBit(DMA1_IT_TC1);

flag=1;

}

}

dma采集结束进入中断并置1 dma采集结束标志位 flag.

FFT函数调用:

此处建议查看文章(https://blog.csdn.net/qq_16209077/article/details/50493567).

void InitBufInArray()   //dma采集数据完成之后 将采集到的值传递给InBufArray[i].并清零标志位.

{

unsigned short i;

if(flag == 1)

{

fx=0;

for(i=0;i<NPT;i++)

{

InBufArray[i] = ((signed short)ADCBuff[i]) << 16;

}

flag = 0;

}

}

cr4_fft_256_stm32(lBufOutArray, InBufArray, NPT);

lBufOutArray:输出数据地址.InBufArray:输入数据地址.NPT:数据长度.(256)

void GetPowerMag();

进行将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y).

显示:

1:串口输出测试数据.(主要是查看数据结果准确度)

2:将数据进行变换完成之后就是将数据在屏幕上显示出来.

设置TIM2 PWM频率 

arr = 50-1 psc = 72-1   TIM2_Init(50-1,72-1); 

采集频率=PWM频率=(72000k/(psc+1))/ARR

f=20kHz,最大采集频率10kHZ.

分辨率: 20kHz / 256 约为 78Hz.

为了方便测试我让单片机的PB4和PB5分别输出不同的PWM波之后进行检测.

arr = 1000-1 psc=72-1    TIM3_PWM_Init(1000-1,72-1);

f=1kHz

TIM_SetCompare1(TIM3,500);//PB4 占空比50%

TIM_SetCompare2(TIM3,700);//PB5 占空比30%

观察输出...

↓方向是频率(每一粒蓝色色块代表一种频率一共256种),

→方向是各次谐波幅值(避免超出屏幕范围所以进行了一些限制).

PB4引脚变换结果

PB5引脚变换结果


关键字:stm32  DMA  adc  fft  库函数 引用地址:stm32 ,DMA,adc,fft. . .(基于库函数)

上一篇:STM32H743 + OV5640图像采集和传输
下一篇:【嵌入式】简评STM32单片机入门之路

推荐阅读最新更新时间:2026-03-19 00:54

stm32 ,DMA,adc,fft. . .(基于库函数)
用TIM2定时器触发adc,DMA采集之后进行fft转换. fft是一种快速傅里叶算法. 官方给的256点运算时间仅需要0.362ms,1024点也只要2.138ms(72mHz) 傅里叶变换就像是将一桶各个颜色混杂在一起的油漆分离出来.并将每种颜料的含量也计算出来. 而各种颜色代表了各种频率,含量代表幅值.(个人的浅显理解) 了解个差不多就可以开始了~ 先将fft的官方库添加到自己的工程.(https://pan.baidu.com/s/1Gw1NXCa3q8SHxc-E1H-6yw 提取码:m1et) 添加之后可以看到官方提供了两种快速傅里叶变换函数,分别为256点和1024点.这里视自己情况进行使用.不过要注意在这之前要调用
[单片机]
stm32专题二十四:ADC 标准库函数分析
ADC初始化结构体: typedef struct { uint32_t ADC_Mode; /*! Configures the ADC to operate in independent or dual mode. This parameter can be a value of @ref ADC_mode */ FunctionalState ADC_ScanConvMode; /*! Specifies whether the conversion is performed in
[单片机]
STM32ADC库函数的介绍
ADC的基本概念希望各位网友查阅相应的手册,上面对ADC有比较详尽的介绍,包括误差的分析和消除。这里主要介绍ADC的基本库函数的定义和使用。 1.ADC_DeInit函数的功能是将外设ADCx的全部寄存器重设为默认值。 ADC_DeInit(ADC2); 2.ADC_Init函数的功能是根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器。其中ADC_InitTypeDef定义在stm32f10x_adc.h中。其结构体如下所示: typedef struct { u32 ADC_Mode;//可以设置ADC_Mode FunctionalState ADC_ScanConvMode;//规定了模数转换工作在扫描
[单片机]
STM32 ADC DMA与TIM触发采样实战教程:经验避坑指南与源码详解
知识点1【TRGO的介绍】 1、TRGO的概述 TRGO:Trigger Output(触发输出),是定时器的一种功能。 它可以作为外设的启动信号,比如ADC转换,DAC输出,DMA请求等。 对于ADC来说,可以通过TRGO信号来触发ADC开始转换 2、定时器TRGO的生成过程 以通用定时器为例,TRGO信号的来源可以是:更新事件,比较事件,其他事件。如下图 通过TIM_SelectOutputTrigger()进行配置 3、补充 (1)ADC持续转换的两种方式 1、ContinuousConvMode = ENABLE + ExternalTrig = None 启动连续采集,适合实时采样 2、
[单片机]
关于STM32 ADC时采用DMA的一点疑问
手册上有这样的话:只有ADC1能够产生DMA请求,似乎是只有ADC通道1能采用DMA方式传输数据。 但是万利的开发板上的ADC例子,用的是ADC通道10,还用了DMA 方式传数据。 所以我猜测 “只有ADC1能够产生DMA请求”中提到的ADC1 并不是指ADC 通道1, 而是软件可配置的 ADC 通道的分类形式,可分为ADC1和ADC2. 我们可将 通道10(或其他)配置成ADC1 “模式”。通过函数 /* ADC1 regular channel10 configuration */ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_5
[单片机]
STM32定时TIM2触发ADC采样,使用DMA保存结果
1.adc.h文件 //ADC-------------------------------------------------------------------------// #ifndef __EVAL_ADC_H #define __EVAL_ADC_H // Includes ------------------------------------------------------------------// #include stm32f10x.h #include eval.h // Exported types ------------------------------
[单片机]
stm32 TIM2定时器触发ADCDMA采样
使用ADC的定时器触发ADC转换的功能,然后使用DMA进行数据的搬运!采用TIM2定时器的触发间隔,实现ADC定时采样转换的功能,然后可以在程序的死循环中一直检测DMA转换完成标志,然后进行数据的读取,或者使能DMA转换完成中断,这样每次转换完成就会产生中断。 做示波器时,由于要做调整横坐标显示倍数增大和缩小,故需要改变采样频率,也就是改变TIM2 的定时器时间间隔,在TIM2初始化函数定义时,添加u16 Value参数,第一次调试时,设置如下: 定时器2初始化函数void TIM2_Configuration(u16 Value),配置TIM_Period = Value - 1; TIM_TimeBaseStructu
[单片机]
STM32采集多路ADCDMA的方法
最近在做一个手柄,用到了一个游戏摇杆,游戏摇杆的原理就是两个电位器,通过读取ADC的值计算位置,原理和触摸屏类似,那么就需要用到两路ADC了,但是我用的开发板是野火的,火哥给的例程只有单路ADC采集,查阅了相关资料解决了多路的问题,现在我把主要的代码贴在下面,以及一些注意的地方。 #define ADC1_DR_Address ((u32)0x40012400+0x4c) __IO uint16_t ADC_ConvertedValue ; /** * @brief 使能ADC1和DMA1的时钟,初始化PA.0&PA.1 * @param 无 * @retval 无 */ static void ADC1_G
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved