STM32单片机芯片与内部114 DSP-变换运算 实数 复数 FFT IFFT 不限制点数
介绍了SMT32官方FFT汇编库 64点 256点 1024点FFT运算,介绍了DSP库支持的2048、4096点FFT,支持复数、实数的定点数 浮点数的单精度 双精度FFT IFFT,介绍了不限制点数的FFT实现。
目录
一、ST 官方汇编 FFT 库(64点, 256 点和 1024 点)
一、ST 官方汇编 FFT 库(64点, 256 点和 1024 点)
这个汇编的FFT库是来自STM32F10x DSP library,由于是汇编实现的,而且是基4算法,所以实现FFT在速度上比较快。
1、cr4_fft_xxx_stm32
其实现32位定点数的运算,其中高16位为虚部,低16位为实部。
cr4_fft_1024_stm32 | 函数用于实现1024点数据的FFT计算 |
cr4_fft_256_stm32 | 函数用于实现256点数据的FFT计算 |
cr4_fft_64_stm32 | 函数用于实现64点数据的FFT计算 |
2、计算幅频响应
uint32_t input[1024], output[1024], Mag[1024];/* 输入,输出和幅值 */
float32_t Phase[1024]; /* 相位*/
/*
*********************************************************************************************************
* 函 数 名: PowerMag
* 功能说明: 求模值
* 形 参: _usFFTPoints FFT点数
* 返 回 值: 无
*********************************************************************************************************
*/
void PowerMag(uint16_t _usFFTPoints)
{
int16_t lX,lY;
uint16_t i;
float32_t mag;
/* 计算幅值 */
for (i=0; i < _usFFTPoints; i++)
{
lX= (output[i]<<16)>>16; /* 实部*/
lY= (output[i]>> 16); /* 虚部 */
arm_sqrt_f32((float32_t)(lX*lX+ lY*lY), &mag); /* 求模 */
Mag[i]= mag*2; /* 求模后乘以2才是实际模值,直流分量不需要乘2 */
}
/* 由于上面多乘了2,所以这里直流分量要除以2 */
Mag[0]= Mag[0]>>1;
}
3、计算相频响应
/*
*********************************************************************************************************
* 函 数 名: Power_Phase_Radians
* 功能说明: 求相位
* 形 参: _usFFTPoints FFT点数, uiCmpValue 阀值
* 返 回 值: 无
*********************************************************************************************************
*/
void Power_Phase_Radians(uint16_t _usFFTPoints, uint32_t _uiCmpValue)
{
int16_t lX, lY;
uint16_t i;
float32_t phase;
float32_t mag;
for (i=0; i <_usFFTPoints; i++)
{
lX= (output[i]<<16)>>16; /* 实部 */
lY= (output[i] >> 16); /* 虚部 */
phase = atan2(lY, lX); /* atan2求解的结果范围是(-pi, pi], 弧度制 */
arm_sqrt_f32((float32_t)(lX*lX+ lY*lY), &mag); /* 求模 */
if(_uiCmpValue > mag)
{
Phase[i] = 0;
}
else
{
Phase[i] = phase* 180.0f/3.1415926f; /* 将求解的结果由弧度转换为角度 */
}
}
二、复数浮点 FFT、IFFT(支持单精度和双精度)
新版 DSP 库浮点 FFT 推荐使用混合基函数 arm_cfft_f32,而基 2 函数 arm_cfft_radix2_f32 和基 4函数 arm_cfft_radix4_f32 将废弃。 ARM 说明如下:
DSP 库的早期发行版提供了单独的 radix-2 和 radix-4 对浮点数据进行运算的算法。 这些功能仍然提供,但已弃用。 相比新版函数,老版的功能较慢且通用性较低
1、基础支持
当前复数 FFT 函数支持三种数据类型,分别是浮点,定点 Q31 和 Q15。这些 FFT 函数有一个共同的特点,就是用于输入信号的缓冲,在转化结束后用来存储输出结果。这样做的好处是节省了 RAM 空间,不需要为输入和输出结果分别设置缓存。由于是复数 FFT,所以输入和输出缓存要存储实部和虚部。存储顺序如下: {real[0], imag[0], real[1], imag[1],………………} ,在使用中切记不要搞错。
2、单精度函数 arm_cfft_f32
支持16、32、64、128、256、512、1024、2048、4096点单精度复数FFT、IFFT。
3、双精度函数 arm_cfft_f64
支持16、32、64、128、256、512、1024、2048、4096点双精度复数FFT、IFFT
三、实数浮点 FFT(支持单精度和双精度)
CMSIS DSP 库里面包含一个专门用于计算实数序列的 FFT 库,很多情况下,用户只需要计算实数序列即可。计算同样点数 FFT 的实数序列要比计算同样点数的虚数序列有速度上的优势。
快速的 rfft 算法是基于混合基 cfft 算法实现的。
1、基础支持
一个 N 点的实数序列 FFT 正变换采用下面的步骤实现:
由上面的框图可以看出,实数序列的 FFT 是先计算 N/2 个实数的 CFFT,然后再重塑数据进行处理从而获得半个 FFT 频谱即可(利用了 FFT 变换后频谱的对称性)。
一个 N 点的实数序列 FFT 逆变换采用下面的步骤实现:
实数 FFT 支持浮点, Q31 和 Q15 三种数据类型。
2、单精度函数 arm_rfft_fast_f32
支持32、64、128、256、512、1024、2048、4096点单精度实数FFT、IFFT。
3、双精度函数 arm_rfft_fast_f64
支持32、64、128、256、512、1024、2048、4096点双精度实数FFT、IFFT
四、不限制点数 FFT 实现
可以看到前面的由于 ARM DSP 库限制最大只能 4096 点,有点不够用,所以整理了个更大点数的。不限制点数,满足 2^n 即可, n 最小值 4, 即 16 个点的 FFT,而最大值不限。
此 FFT 算法没有再使用 ARM DSP 库,重新实现了一个。
1、函数 InitTableFFT
这个函数用于 FFT 计算过程中需要用到的正弦和余弦表。 对于 8192 点和 16384 点已经专门制作了数值表,存到内部 Flash,其它点数继续使用的 RAM 空间。
/*
*********************************************************************************************************
* 函 数 名: Int_FFT_TAB
* 功能说明: 正弦和余弦表
* 形 参: 点数
* 返 回 值: 无
*********************************************************************************************************
*/
#if (MAX_FFT_N != 8192) && (MAX_FFT_N != 16384)
float32_t costab[MAX_FFT_N/2];
float32_t sintab[MAX_FFT_N/2];
void InitTableFFT(uint32_t n)
{
uint32_t i;
/* 正常使用下面获取 cos 和 sin 值 */
#if 1
for (i = 0; i < n; i ++ )
{
sintab[ i ]= sin( 2 * PI * i / MAX_FFT_N );
costab[ i ]= cos( 2 * PI * i / MAX_FFT_N );
}
/* 打印出来是方便整理 cos 值和 sin 值数组,将其放到内部 Flash,从而节省 RAM */
#else
printf("const float32_t sintab[%d] = {\r\n", n);
for (i = 0; i < n; i ++ )
{
sintab[ i ]= sin( 2 * PI * i / MAX_FFT_N );
printf("%.11ff,\r\n", sintab[ i ]);
}
printf("};\r\n");
printf("const float32_t costab[%d] = {\r\n", n);
for (i = 0; i < n; i ++ )
{
sintab[ i ]= cos( 2 * PI * i / MAX_FFT_N );
printf("%.11ff,\r\n", sintab[ i ]);
}
printf("};\r\n");
#endif
}
#endif
2、函数 cfft
这个函数用于复数 FFT 变换,如果需要实数则直接把虚部设置为0即可。
void cfft(struct compx *_ptr, uint32_t FFT_N )
/*
*********************************************************************************************************
* 函 数 名: cfft
* 功能说明: 对输入的复数组进行快速傅里叶变换(FFT)
* 形 参: *_ptr 复数结构体组的首地址指针 struct 型
* FFT_N 表示点数
* 返 回 值: 无
*********************************************************************************************************
*/
void cfft(struct compx *_ptr, uint32_t FFT_N )
{
float32_t TempReal1, TempImag1, TempReal2, TempImag2;
uint32_t k,i,j,z;
uint32_t Butterfly_NoPerColumn; /* 每级蝶形的蝶形组数 */
uint32_t Butterfly_NoOfGroup; /* 蝶形组的第几组 */
uint32_t Butterfly_NoPerGroup; /* 蝶形组的第几个蝶形 */
uint32_t ButterflyIndex1,ButterflyIndex2,P,J;
uint32_t L;
uint32_t M;
z=FFT_N/2; /* 变址运算,即把自然顺序变成倒位序,采用雷德算法 */
for(i=0,j=0;i<FFT_N-1;i++)
{
/*
如果 i<j,即进行变址 i=j 说明是它本身, i>j 说明前面已经变换过了,不许再变化,注意这里一般是实数有虚数部分 设置成结合体
*/
if(i<j)
{
TempReal1 = _ptr[j].real;
_ptr[j].real= _ptr[i].real;
_ptr[i].real= TempReal1;
}
k=z; /*求 j 的下一个倒位序 */
while(k<=j) /* 如果 k<=j,表示 j 的最高位为 1 */
{
j=j-k; /* 把最高位变成 0 */
k=k/2; /* k/2,比较次高位,依次类推,逐个比较,直到某个位为 0,通过下面那句 j=j+k 使其变为 1 */
}
j=j+k; /* 求下一个反序号,如果是 0,则把 0 改为 1 */
}
/* 第 L 级蝶形(M)第 Butterfly_NoOfGroup 组(Butterfly_NoPerColumn)第 J 个蝶形(Butterfly_NoPerGroup)****** */
/* 蝶形的组数以 2 的倍数递减 Butterfly_NoPerColumn,每组中蝶形的个数以 2 的倍数递增 Butterfly_NoPerGroup */
/* 在计算蝶形时,每 L 列的蝶形组数,一共有 M 列,每组蝶形中蝶形的个数,蝶形的阶数(0,1,2.....M-1) */
Butterfly_NoPerColumn = FFT_N;
Butterfly_NoPerGroup = 1;
M = log2(FFT_N);
for ( L = 0;L < M; L++ )
{
Butterfly_NoPerColumn >>= 1; /* 蝶形组数 假如 N=8,则(4,2,1) */
//第 L 级蝶形 第 Butterfly_NoOfGroup 组(0,1, ....Butterfly_NoOfGroup-1)
for ( Butterfly_NoOfGroup = 0;Butterfly_NoOfGroup < Butterfly_NoPerColumn;Butterfly_NoOfGroup++ )
{
/* 第 Butterfly_NoOfGroup 组中的第 J 个 */
for ( J = 0;J < Butterfly_NoPerGroup;J ++ )
{ /* 第 ButterflyIndex1 和第 ButterflyIndex2 个元素作蝶形运算,WNC */
/* (0,2,4,6)(0,1,4,5)(0,1,2,3) */
/* 两个要做蝶形运算的数相距 Butterfly_NoPerGroup, ge=1,2,4 */
/* 这里相当于 P=J*2^(M-L),做了一个换算下标都是 N (0,0,0,0)(0,2,0,2)(0,1,2,3) */
ButterflyIndex1 = ( ( Butterfly_NoOfGroup * Butterfly_NoPerGroup ) << 1 ) + J;
ButterflyIndex2 = ButterflyIndex1 + Butterfly_NoPerGroup;
P = J * Butterfly_NoPerColumn;
/* 计算和转换因子乘积 */
TempReal2 = _ptr[ButterflyIndex2].real * costab[ P ] + _ptr[ButterflyIndex2].imag *
sintab[ P ];
TempImag2 = _ptr[ButterflyIndex2].imag * costab[ P ] - _ptr[ButterflyIndex2].real *
sintab[ P ] ;
TempReal1 = _ptr[ButterflyIndex1].real;
TempImag1 = _ptr[ButterflyIndex1].imag;
/* 蝶形运算 */
_ptr[ButterflyIndex1].real = TempReal1 + TempReal2;
_ptr[ButterflyIndex1].imag = TempImag1 + TempImag2;
_ptr[ButterflyIndex2].real = TempReal1 - TempReal2;
_ptr[ButterflyIndex2].imag = TempImag1 - TempImag2;
}
}
Butterfly_NoPerGroup<<=1; /* 一组中蝶形的个数(1,2,4) */
}

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)