DSP与FFT
DSP库的添加
cubemx配置
下载DSP库
添加DSP库到工程中
工程配置
添加宏
ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING
第一个宏:
M3:ARM_MATH_CM3
M4:ARM_MATH_CM4
H7:ARM_MATH_CM7
include
#include "arm_math.h"
#include "math.h"
#include "arm_const_structs.h"
#include "arm_const_structs.h"
如果cubemx生成的库没有,可以找板子的例程,将DSP的lib文件添加到工程中
测试
/* USER CODE BEGIN PV */
uint8_t lcd_id[12]; //存放LCD ID字符串
float data = 0;//定义一个float型变量
float fft_inputbuf[FFT_LENGTH*2]; //FFT输入数组
float fft_outputbuf[FFT_LENGTH]; //FFT输出数组
/* USER CODE END PV */
/* USER CODE BEGIN 1 */
arm_cfft_radix4_instance_f32 scfft;
uint8_t key,t=0;
float time;
uint8_t buf[50];
uint16_t i;
char send[15];
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
LCD_Init();
POINT_COLOR=RED; //画笔颜色:红色
sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。
LCD_ShowString(30,40,210,24,24,"mcudev STM32F4");
LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");
LCD_ShowString(30,90,200,16,16,"mcudev.taobao.com");
arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
data = arm_sin_f32(PI/6); //计算sin30°
sprintf((char*)lcd_id,"LCD ID:%.5f",data);//0.49999
LCD_ShowString(30,110,200,16,16,lcd_id);
for(i=0;i<FFT_LENGTH;i++)//生成信号序列
{
fft_inputbuf[2*i]=100+
10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
50*arm_cos_f32(2*PI*i*8/FFT_LENGTH); //生成输入信号实部
//printf("%.2f\r\n",fft_inputbuf[2*i]);
sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
fft_inputbuf[2*i+1]=0;//虚部全部为0
}
printf("\r\n");
arm_cfft_radix4_f32(&scfft,fft_inputbuf); //FFT计算(基4)
printf("after:");
for(i=0;i<FFT_LENGTH;i++)//生成信号序列
{
sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
}
arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把运算结果复数求模得幅值
printf("\r\n%d point FFT runtime:%0.3fms\r\n",FFT_LENGTH,time/1000);
printf("FFT Result:\r\n");
for(i=0;i<FFT_LENGTH;i++)
{
printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf[i]);
}
/* USER CODE END WHILE */
如果没有LCD
/* USER CODE BEGIN PV */
float data = 0;//定义一个float型变量
float fft_inputbuf[FFT_LENGTH*2]; //FFT输入数组
float fft_outputbuf[FFT_LENGTH]; //FFT输出数组
/* USER CODE END PV */
/* USER CODE BEGIN 1 */
arm_cfft_radix4_instance_f32 scfft;
uint8_t key,t=0;
float time;
uint8_t buf[50];
uint16_t i;
char send[15];
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
data = arm_sin_f32(PI/6); //计算sin30°
//开调试看data值或串口输出
for(i=0;i<FFT_LENGTH;i++)//生成信号序列
{
fft_inputbuf[2*i]=100+
10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
50*arm_cos_f32(2*PI*i*8/FFT_LENGTH); //生成输入信号实部
//printf("%.2f\r\n",fft_inputbuf[2*i]);
sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
fft_inputbuf[2*i+1]=0;//虚部全部为0
}
printf("\r\n");
arm_cfft_radix4_f32(&scfft,fft_inputbuf); //FFT计算(基4)
printf("after:");
for(i=0;i<FFT_LENGTH;i++)//生成信号序列
{
sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
}
arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把运算结果复数求模得幅值
printf("\r\n%d point FFT runtime:%0.3fms\r\n",FFT_LENGTH,time/1000);
printf("FFT Result:\r\n");
for(i=0;i<FFT_LENGTH;i++)
{
printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf[i]);
}
/* USER CODE END WHILE */
DAC
cubemx配置
DAC
- Trigger:Timer2 Trigger Out event
DMA点ADD添加,Circular模式
确认配置无误
TIM2
- 时钟频率为168MHz
- PSC取168-1
- ARR取5-1
输出频率为:168000000/168/5 = 200000Hz
工程配置
BEGIN0 产生正弦波函数
/* USER CODE BEGIN 0 */
void SineWave_Data( uint16_t num,uint16_t *D,float U)
{
uint16_t i;
for( i=0;i<num;i++)
{
D[i]=(uint16_t)((U*sin(( 1.0*i/(num-1))*2*3.14159265358979)+U)*4095/3.3);
}
}
/* USER CODE END 0 */
BEGIN1 正弦波数组
/* USER CODE BEGIN 1 */
uint16_t Sine12bit[32] = {
2048 , 2460 , 2856 , 3218 , 3532 , 3786 , 3969 , 4072 ,
4093 , 4031 , 3887 , 3668 , 3382 , 3042 ,2661 , 2255 ,
1841 , 1435 , 1054 , 714 , 428 , 209 , 65 , 3 ,
24 , 127 , 310 , 564 , 878 , 1240 , 1636 , 2048
};
/* USER CODE END 1 */
BEGIN2 初始化
//DAC
TIM2->ARR = 9; //修改ARR 可不加
HAL_TIM_Base_Start(&htim2); //打开定时器中断
//HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
SineWave_Data(n,DualSine12bit,1.6); //产生正弦波函数
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)Sine12bit, 32, DAC_ALIGN_12B_R);
输出信号
ADC
cubemx配置
ADC1
- 定时器3触发
- 添加DMA
- 打开中断
TIM3
- 时钟频率168MHz
- PSC:168-1
- ARR:2-1
- Update Event
- 打开中断
工程配置
BEGIN PD
#define fft_adc_n 1024 // 采1024个点
BEGIN PV
float adc_data[fft_adc_n*2]={0}; // 存ADC值
uint8_t ad_flag = 0;
BEGIN2
HAL_TIM_Base_Start_IT(&htim3); //打开定时器3中断
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,fft_adc_n); //ADC采集一组数据
BEGIN4
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
ad_flag=1;
}
ADC中断,采集完成后标志位置1
while(1)
while (1)
{
if(ad_flag == 1)
{
//数据处理
//......
ad_flag = 0; //标志位置0
}
}
FFT
float fft_in_adc_data[fft_adc_n*2]={0}; // FFT输入,实部是ADC值,虚部补0
float fft_out_adc_data[fft_adc_n]={0}; // FFT输出
uint16_t flag[fft_adc_n] = {0}; // 数据处理时暂时存放数据
int k = 1; // 找第k大值
float32_t maxValue = 0; // ADC最大值
uint32_t index = 0; // 最大值下标
int i,j;
float freq = 0; // 频率
arm_cfft_radix4_instance_f32 scfft; //FFT对应结构体变量
arm_cfft_radix4_init_f32(&scfft,fft_adc_n,0,1); //初始化scfft结构体,设置FFT相关参数
if(ad_flag == 1)
{
printf("finish\r\n");
ad_flag = 0;
for(i=0;i<fft_adc_n;i++)
{
fft_in_adc_data[i*2] = adc_buffer[i]*3.3/4095; //实部
fft_in_adc_data[i*2+1] = 0; //虚部
}
printf("\r\n");
arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_in_adc_data,0,1);
arm_cmplx_mag_f32(fft_in_adc_data,fft_out_adc_data,fft_adc_n);
fft_out_adc_data[0]/=1024;
for(i=1;i<fft_adc_n;i++)
fft_out_adc_data[i]/=512;
for(i=0;i<fft_adc_n;i++)
{
printf("%.5f\t",fft_out_adc_data[i]);
}
printf("\r\n");
//arm_max_f32(&fft_out_adc_data[1],fft_adc_n/2-1,&maxValue,&index);
/****查找最大值及其下标*********/
memset(flag, 0, sizeof(flag));
for (i = 1; i <= k; i++)
{
maxValue = 0.0;
for (j = 1; j < fft_adc_n / 2; j++)
{
if (flag[j] == 1)
break;
if (fft_out_adc_data[j] > maxValue)
{
maxValue = fft_out_adc_data[j];
index = j;
}
}
flag[index] = 1;
}
printf("index:%d\r\n",index);
printf("Vmax:%.2f\r\n",maxValue);
freq = (float)500000/fft_adc_n * (index) /2; //500000为采样率
printf("freq: %.3f",freq);
ad_flag = 0;
}
arm_cfft_sR_f32_len1024
在arm_const_structs.h
里面,如果报错,检查头文件- 如果更改采样点数,注意修改
arm_cfft_sR_f32_len1024
,如果采样512个点,则改为arm_cfft_sR_f32_len512
,以此类推 fft_out_adc_data[0]
为直流分量电压值maxValue
为基波电压幅值fft_out_adc_data[0]
+maxValue
=正弦波最大值fft_out_adc_data[0]
-maxValue
=正弦波最小值
参考文章
https://blog.csdn.net/Nothing_To_Say_/article/details/123606260
https://blog.csdn.net/qq_34022877/article/details/123190943
https://blog.csdn.net/qq_24426625/article/details/129738537
等