利用CUBEMX中的DSP库实现ADC的DMA传输和调用FFT算法

需要实现的目标为计算波形的THD值,对于往年电赛大都都会计算信号的THD值和频率,对于DSP库的添加请参考该文章(有两种方式):https://blog.csdn.net/qq_34022877/article/details/117855263

接下来为时钟树配置,根据自己开发板原理图来选择适合自己的时钟。这里开发板使用的是内部时钟,如图所示:

然后接下来为ADC+DMA配置,这里我配置ADC的时钟为2MHz,对32MHz的时钟进行16分频。点击Rank选择2.5个ADC时钟周期。

 然后是DMA的配置,循环模式为当开启DMA时,数据存储完成后会重新从数据0开始重新存储,这里选择一字(word),选择半字的话在debug的时候数据不知道为什么会显示不正常,如图所示。

 代码部分,变量定义。

uint16 buf[1024]={0}; //ADC原始数据存放
float buf_value[1024]={0}; //ADC原始数据存放
float testInput_f32_1khz[2048] = {0};//FFT输入数组
float testoutput[1024]={0};					 //FFT最终输出为:实部+虚部	
float sum;													 //THD参数求和
float thd_high;											 //开根号之后的值	
float g[5]={0};   //FFT基波分量和各次谐波分量值
float THD;

用户初始化,ADC的DMA传输在hal库的adc.h中能找到。这里设置PWM频率为1000Hz。

	HAL_TIM_PWM_Init(&htim3);																						//PWM生成初始化
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
	
	__HAL_TIM_SET_AUTORELOAD(&htim3, duty-1);				//设置频率
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, duty/2);  //设置占空比
	HAL_Delay(50);
	HAL_ADC_Start_DMA(&hadc1,(uint32_t *)buf,1024);//捕获完1024个值停止ADC和DMA
	HAL_Delay(50);//等待DMA传输完毕,延迟50ms

主函数代码分析,因为使用的是单次DMA传输,如果需要使用多次DMA传输,在while中多次调用 HAL_ADC_Start_DMA();该函数即可,并且需要清除FFT的输入数据(更新数据),防止前一次的计算数据重叠到下一次的数据上。以arm为开头的函数即为DSP库中的函数,比如arm_cfft_f32,这些函数可以网页中查询到具体函数的用法和实现效果,(之前我能打开现在打不开了,自行百度)网页地址:http://www.wisemcu.cn/STM32/docs/DSP/html/

		HAL_ADC_Start_DMA(&hadc1,(uint32_t *)buf,1024);//捕获完1024个值停止ADC和DMA
		HAL_Delay(50);//等待DMA传输完毕,延迟50ms
				for(int i=0;i<1024;i++)
		{
		buf_value[i]=(float)(buf[i])*3.3/4069;//ADC电压转换
		//printf("%02f\r\n",buf_value[i]);//打印电压值
		}
		
			for(int i=0;i<1024;i++)
		{
		testInput_f32_1khz[i*2]=buf_value[i];
		}
		arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_1khz, 0, 1);//FFT变换
		arm_cmplx_mag_f32(testInput_f32_1khz, testoutput, 1024);//求复数模
		testoutput[0]/=1024;//直流分量除以采样点数
		for(int i=1;i<1024;i++)
		testoutput[i]/=512;//其他分量除以(采样点数/2)
		/*for(int i=0;i<1024;i++)
		printf("%02f\r\n",testOutput[i]);//打印数据*/
		
		for(int j=0;j<5;j++)
		{
			uint16 k=(j+1)*1000/130.21+0.5;			//四舍五入
			g[j]=testoutput[k];
		}
		sum=g[1]*g[1]+g[2]*g[2]+g[3]*g[3]+g[4]*g[4];
		arm_sqrt_f32(sum, &thd_high);
		THD=thd_high/g[0]*100;
		
		for(int i=1;i<2048;i++)              //FFT输入数据需要清零,不然计算数据会重叠,重叠的数据为上一次FFT计算值。
		testInput_f32_1khz[i]=0;

进行debug,将数据导出,注意具体到个人需要将导出的数据名称更改,导出的方法(运行脚本)链接:Keil5把变量的数据导出,可视化_完善 自己的博客-CSDN博客_keil怎么导出watch数组

结果分析与显示,图像为excel导出得到,FFT为1024个点即N=1024。这里来计算采样频率(MSPS)=ADC一个数据的转换频率=采样时间+转换时间,采样时间根据数据手册查到为12.5个时钟周期,而转换时间我们设置为2.5个周期,就是Rank中设置的数据。采样频率=15个时钟周期,时钟周期为2MHz,从而计算了ADC的采样频率=133333.3Hz。在FFT中频率分辨率是=采样频率/FFT点数(N),约等于130.21Hz每点。N=0时的数据为直流信号的参数值,这些参数值为一种1024个点之间的比值关系(类似比重),最大的值为基波分量,当N=8时基波分量=8*130.21,实际数据确实N=8时,该参数最大(频率也可以得到了),谐波分量即为基波分量的整数倍,以此类推可以得到各谐波分量值。THD的定义可以参照往年电赛计算公式得到,这里如果为了提高测量频率精度可以提高N的数量,但必须注意N=2的K次,K为整数。最终计算THD约等于46%,与理想情况下还是有一定的差距,若要提高该精度可以从提高N点数 和软件滤波、硬件滤波电路方向进行改善该值。

 

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值