
特别说明:完整45期数字信号处理教程,原创高性能示波器代码全开源地址:链接 第32章 实数FFT的实现 本章主要讲解实数的浮点和定点Q31,Q15的实现。关于这部分的知识点和函数的计算结果上,官方的文档有一些小错误,在章节中会跟大家详细讲述,还有一个要注意的问题,调用实数FFT函数一定要使用CMSIS-DSP V1.4.4及其以上版本,以前的版本有bug。 本章节使用的复数FFT函数来自ARM官方库的TransformFunctions部分 32.1 复数FFT 32.2 复数FFT-基2算法 32.3 复数FFT-基4算法 32.4 总结 32.1 实数FFT 32.1.1 描述 CMSIS DSP库里面包含一个专门用于计算实数序列的FFT库,很多情况下,用户只需要计算实数序列即可。计算同样点数FFT的实数序列要比计算同样点数的虚数序列有速度上的优势。 快速的rfft算法是基于混合基cfft算法实现的。 一个N点的实数序列FFT正变换采用下面的步骤实现: ![]() 由上面的框图可以看出,实数序列的FFT是先计算N/2个实数的CFFT,然后再重塑数据进行处理从而获得半个FFT频谱即可(利用了FFT变换后频谱的对称性)。 一个N点的实数序列FFT逆变换采用下面的步骤实现: ![]() 实数FFT支持浮点,Q31和Q15三种数据类型。 32.2 实数FFT 32.2.1 arm_rfft_fast_f32 函数定义如下: void arm_rfft_fast_f32( arm_rfft_fast_instance_f32 * S, float32_t * p, float32_t * pOut, uint8_t ifftFlag) 参数定义: [in] *S points to an arm_rfft_fast_instance_f32 structure. [in] *p points to the input buffer. [in] *pOut points to the output buffer. [in] ifftFlag RFFT if flag is 0, RIFFT if flag is 1 注意事项: 结构arm_rfft_fast_instance_f32的定义如下(在文件arm_math.h文件): typedef struct { arm_cfft_instance_f32 Sint; /**< Internal CFFT structure. */ uint16_t fftLenRFFT; /**< length of the real sequence */ float32_t * pTwiddleRFFT; /**< Twiddle factors real stage */ } arm_rfft_fast_instance_f32 ; 下面通过在开发板上运行函数arm_rfft_fast_f32和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
运行如上函数可以通过串口打印出函数arm_rfft_fast_f32和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_fast_f32的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数 n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率 subplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); subplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值'); Matlab运行结果如下: ![]() 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_fast_f32输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_fast_f32的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。 函数arm_rfft_fast_f32在计算直流分量(也就是频率为0的值)的虚部上是有错误的。关于这点大家可以将实际的实部和虚部输出结果打印出来做对比,但差别很小,基本可以忽略。 32.2.2 arm_rfft_q15 函数定义如下: void arm_rfft_q15( const arm_rfft_instance_q15 * S, q15_t * pSrc, q15_t * pDst) 参数定义: [in] *S points to an instance of the Q15 RFFT/RIFFT structure. [in] *pSrc points to the input buffer. [out] *pDst points to the output buffer. return none. 注意事项: 结构arm_rfft_instance_q15的定义如下(在文件arm_math.h文件): typedef struct { uint32_t fftLenReal; uint8_t ifftFlagR; uint8_t bitReverseFlagR; uint32_t twidCoefRModifier; q15_t *pTwiddleAReal; q15_t *pTwiddleBReal; const arm_cfft_instance_q15 *pCfft; } arm_rfft_instance_q15; 下面通过在开发板上运行函数arm_rfft_q15和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数 n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率 subplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); subplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值'); Matlab运行结果如下: ![]() 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。 32.2.3 arm_rfft_q31 函数定义如下: void arm_rfft_q31( const arm_rfft_instance_q31 * S, q31_t * pSrc, q31_t * pDst) 参数定义: [in] *S points to an instance of the Q31 RFFT/RIFFT structure. [in] *pSrc points to the input buffer. [out] *pDst points to the output buffer. return none. 注意事项: 结构arm_rfft_instance_q31的定义如下(在文件arm_math.h文件): typedef struct { uint32_t fftLenReal; uint8_t ifftFlagR; uint8_t bitReverseFlagR; uint32_t twidCoefRModifier; q31_t *pTwiddleAReal; q31_t *pTwiddleBReal; const arm_cfft_instance_q31 *pCfft; } arm_rfft_instance_q31; 下面通过在开发板上运行函数arm_rfft_q31和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数 n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率 subplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); subplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值'); Matlab运行结果如下: ![]() 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。 32.3 总结 使用实数FFT计算的时候要特别的注意本章节提到的几个错误点。 |
谢谢!希望继续支持啊~~
程序是这样子:
用ADC采集值填入fft输入buff
void ADC_proc(void)
{
uint16_t ai,cnt;
if(adc_conv_done)
{
adc_conv_done = 0;
for(ai=0;ai<NPT;ai++)
{
lbufin[ai*2] = (float)(adc_buf[ai*2]-2048);
lbufin[ai*2+1] = (float)0;
}
FFT_proc();
HAL_ADC_Start_DMA(&hadc,(uint32_t*)adc_buf,sizeof(adc_buf)/2);
}
}
FFT处理
float lbufin[NPT*2]; /* Complex input vector */
float lbufout[NPT]; /* Complex output vector */
float lbufmag; /* Magnitude vector */
uint16_t fftSize = 64;
uint8_t ifftFlag = 0;
uint8_t doBitReverse = 1;
uint16_t audio_mag;
extern uint8_t audio_intf_flag;
//uint32_t refIndex = 213,
uint32_t testIndex = 0;
__IO uint8_t new_mag_flag;
void FFT_proc()
{
arm_cfft_f32(&arm_cfft_sR_f32_len64, lbufin, ifftFlag, doBitReverse);
arm_cmplx_mag_f32(lbufin,lbufout,fftSize);
arm_max_f32(lbufout, NPT, &lbufmag, &testIndex);
}