当前位置: 首页 > news >正文

STM32单片机芯片与内部114 DSP-变换运算 实数 复数 FFT IFFT 不限制点数

目录

一、ST 官方汇编 FFT 库(64点, 256 点和 1024 点)

1、cr4_fft_xxx_stm32

2、计算幅频响应

3、计算相频响应

二、复数浮点 FFT、IFFT(支持单精度和双精度)

1、基础支持

2、单精度函数 arm_cfft_f32

3、双精度函数 arm_cfft_f64

三、实数浮点 FFT(支持单精度和双精度)

1、基础支持

2、单精度函数 arm_rfft_fast_f32

3、双精度函数 arm_rfft_fast_f64

四、不限制点数 FFT 实现

1、函数 InitTableFFT

2、函数 cfft


一、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) */
}

相关文章:

  • linux进程通信之共享内存实例
  • 【Javascript网页设计】在线图片画板案例
  • BambuStudio学习笔记:FaceDetector类
  • 模块13.异常_Object
  • 服务器CPU微架构
  • LeetCode 解题思路 10(Hot 100)
  • XTDrone+Mavros+Gazebo仿真——配置与控制不同的无人机
  • DeepSeek赋能智慧工厂:推动制造业高效智能可持续,开启制造业转型升级
  • hi3516cv610适配AIC8800D80的连接路由器记录
  • 文件上传和下载前后端交互逻辑
  • leetcode1 两数之和 哈希表
  • 极狐GitLab 17.9 正式发布,40+ DevSecOps 重点功能解读【四】
  • Golang的多团队协作开发
  • 设计模式|策略模式 Strategy Pattern 详解
  • BambuStudio学习笔记:FlushVolCalculator类
  • doris: Oracle
  • ROM修改进阶教程------修改安卓机型SELinux宽容的几种方式方法 以及第三方系统中如何关闭SELinux宽容
  • 【AD】5-2 DXF结构导入与板框自定义
  • skynet简单游戏服务器的迭代
  • Spring AI 接入 DeepSeek AI
  • 泽连斯基表示将在土耳其“等候”普京
  • 陈宝良 高寿仙 彭勇︱明清社会的皇权、商帮与市井百态
  • 著名蒙古族音乐学者马•斯尔古愣逝世,享年86岁
  • 印度军方否认S-400防空系统被摧毁
  • 上海国际电影节推出三大官方推荐单元,精选十部优秀影片
  • 视频丨习近平同普京会谈:共同弘扬正确二战史观,维护联合国权威和地位