用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引脚变换结果
上一篇:STM32H743 + OV5640图像采集和传输
下一篇:【嵌入式】简评STM32单片机入门之路
推荐阅读最新更新时间:2026-03-19 00:54
- AZ7045RTR低压指示检测器典型应用电路
- LTM4608AMPY 3V 至 5.5Vin、2.5V/8A 输出 DC/DC 稳压器的典型应用电路
- 使用 L6562A 的恒流反向降压 LED 驱动器
- EP53A7HQI 1A同步降压稳压器典型应用电路
- OP284FSZ 缓冲网络补偿容性负载的典型应用
- EVAL-AD5116SDZ,用于仪表的 AD5116 数字电位器的评估板
- 用于音频的 4 通道 D 类音频功率放大器
- 使用 ON Semiconductor 的 ML4841 的参考设计
- LT3755IUD-1 降压模式 500mA LED 驱动器的典型应用电路,用于 20kHz PWM 调光
- RDR-669 - 使用 LinkSwitch-CV 的 5 W、85-265 VAC、恒压 (CV) 适配器

stm32驱动屏IC rm68042
STM32 PMSM FOC 简介
非常经典的关于LLC的杨波博士论文

XC6406PP60DL






京公网安备 11010802033920号