STM32F单片机实现ADC采集正弦波的FFT变换和逆变换

目标:C语言实现对ADC采集的带有杂波信号的正弦波进行FFT滤波,实现有效的数字滤波功能

方法:

  1. ADC通过DMA采集,将数据传入SourceBufN;

  2. FFT变换:直接调用void ProcessADCData(unsigned short *SourceBuf, float *DestBuf) ,将逆变换的结果传入DestBufN

实测可用,以下为代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "platform_config.h"

#define PI 3.141592653589793f
#define N 256  // 采样点数
#define V_REF 3.3
#define V_AMP 7.5
extern unsigned short ADC_ConvertedBuffer[];
extern char GlgStr[];
extern  void DbgTextOutput(char *ShowMsg);
extern void DbgInfoOutput(unsigned short sts, char *Title, unsigned char IsText, unsigned char *lpInfo, unsigned short InfoLen);

unsigned short SourceBuf[N];
float DestBuf[N];

typedef struct {
    float real;
    float imag;
} Complex;
// 优化版位反转函数
void bit_reverse(Complex *x, int n) {
    unsigned int target = 0;
    for (unsigned int source = 0; source < n; source++) {
        // 计算目标索引 (位反转)
        if (source < target) {
            // 交换源和目标位置
            Complex temp = x[source];
            x[source] = x[target];
            x[target] = temp;
        }
        
        // 计算下一个目标索引 (位反转算法)
        unsigned int mask = n >> 1;
        while (target & mask) {
            target &= ~mask;
            mask >>= 1;
        }
        target |= mask;
    }
}

// FFT函数修改为接受长度参数
void fft(Complex *x, int n) {
    bit_reverse(x, n);  // 传递长度n
    
    for (int stage = 2; stage <= n; stage <<= 1) {
        int half = stage >> 1;
        float angle = -2 * PI / stage;
        Complex w = {cosf(angle), sinf(angle)};
        
        for (int k = 0; k < n; k += stage) {
            Complex v = {1.0f, 0.0f};
            for (int j = 0; j < half; j++) {
                Complex a = x[k + j];
                Complex b = x[k + j + half];
                
                // 优化复数乘法
                float b_real = b.real * v.real - b.imag * v.imag;
                float b_imag = b.real * v.imag + b.imag * v.real;
                
                x[k + j].real = a.real + b_real;
                x[k + j].imag = a.imag + b_imag;
                x[k + j + half].real = a.real - b_real;
                x[k + j + half].imag = a.imag - b_imag;
                
                // 更新旋转因子
                float v_real = v.real * w.real - v.imag * w.imag;
                v.imag = v.real * w.imag + v.imag * w.real;
                v.real = v_real;
            }
        }
    }
}


// IFFT逆变换函数,增加参数n
void ifft(Complex *x, int n) {
    // 取共轭
    for (int i = 0; i < n; i++) {
        x[i].imag = -x[i].imag;
    }
    
    // FFT变换
    fft(x, n);
    
    // 再次取共轭并归一化
    for (int i = 0; i < n; i++) {
        x[i].real = x[i].real / n;
        x[i].imag = -x[i].imag / n;
    }
}

// 主处理函数
void ProcessADCData(unsigned short *SourceBuf, float *DestBuf) {
    // 使用静态存储,避免栈溢出
    static Complex fft_buf[N];
    float sum = 0.0f;
    
    // 1. 去直流分量 (时域去均值)
    for (int i = 0; i < N; i++) {
        sum += SourceBuf[i];
    }
    float mean = sum / N;
    
    // 2. 转换到复数域并去直流
    for (int i = 0; i < N; i++) {
        fft_buf[i].real = (float)SourceBuf[i] - mean;
        fft_buf[i].imag = 0.0f;
    }
    
    // 3. 执行FFT (传递N)
    fft(fft_buf, N);
    
    // 4. 频域滤波 (仅保留90Hz分量)
    int target_bin = 1;  // 90Hz对应bin=1 (23040Hz采样率/256点)
    for (int i = 0; i < N; i++) {
        if (i != target_bin && i != (N - target_bin)) {
            fft_buf[i].real = 0.0f;
            fft_buf[i].imag = 0.0f;
        }
    }
    
    // 5. 执行IFFT (传递N)
    ifft(fft_buf, N);
    
    // 6. 存储实部结果
    for (int i = 0; i < N; i++) {
        DestBuf[i] = fft_buf[i].real;
    }
}
float calculate_rms(float *buffer, int len) {
    double sum_sq = 0.0;
    for (int i = 0; i < len; i++) {
        sum_sq += (double)buffer[i] * (double)buffer[i];
    }
    return (float)sqrt(sum_sq / len);
}

float calculate_vpp(float *buffer, int len) {
    float min = 4096;
		float max = -4096;
    for (int i = 0; i < len; i++) {
        if(min>buffer[i]) min = buffer[i];
				if(max<buffer[i]) max = buffer[i];
    }
    return (max-min);
}
int fft2_main() {
	int i;
	
	float v_rms;
	float v_vpp;
	float v_input;
	for(i=0; i<N; i++) {
		 SourceBuf[i] = ADC_ConvertedBuffer[i];
	}
	sprintf(GlgStr,"FFT_IN:\r\n");
	for(i=0;i<256;i++){
		sprintf(&GlgStr[strlen(GlgStr)], "%d,",SourceBuf[i]);
	}
	sprintf(&GlgStr[strlen(GlgStr)], "\r\nend\r\n");
	DbgInfoOutput(FftDbgInfo+1, "fft input", 1, (unsigned char*)GlgStr, strlen(GlgStr));
//		
	ProcessADCData(SourceBuf, DestBuf);
	sprintf(GlgStr,"FFT_OUT:\r\n");
	for(i=0;i<256;i++){
		sprintf(&GlgStr[strlen(GlgStr)], "%0.1f,",DestBuf[i]);
	}
	sprintf(&GlgStr[strlen(GlgStr)], "\r\nend\r\n");
	DbgInfoOutput(FftDbgInfo+1, "fft output", 1, (unsigned char*)GlgStr, strlen(GlgStr));
	
	v_rms = calculate_rms(DestBuf, N);
	v_rms = (v_rms/4095)*V_REF;
	
	v_vpp = calculate_vpp(DestBuf, N);
	v_vpp = (v_vpp/4095)*V_REF;
	
	v_input = v_vpp*V_AMP;
	sprintf(GlgStr,"ADC,rms=%0.5f, vpp=%0.5f, vin=%0.5f\r\n", v_rms,v_vpp, v_input);
	DbgTextOutput(GlgStr);
	
	return -1;
}
相关推荐
虎王物联8 小时前
STM32 HAL库串口DMA空闲中断:高效接收不定长Modbus数据帧
stm32·单片机·嵌入式硬件·dma·modbus
自小吃多8 小时前
DXF文件导入与PCB板框定义笔记
笔记·嵌入式硬件
SUNNYSPY0018 小时前
AO4402-ASEMI便携设备电源开关AO4402
单片机
单片机仿真设计10 小时前
【proteus仿真】基于 STC89C52 单片机家用智能台灯设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·毕设
2601_9507906810 小时前
深圳国产单片机厂商怎么选?技术能力与服务能力检查表
单片机·嵌入式硬件·国产mcu·芯片厂商·深圳单片机·单片机选型
hongmai66688812 小时前
MP2348GTL-Z:这颗24V/4A同步降压芯片,把性能和体积平衡得恰到好处
c语言·开发语言·arm开发·单片机·5g
笨笨饿13 小时前
#135_代码中的类型重定义艺术:从基础封装到函数类型设计
开发语言·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库
西城微科方案开发13 小时前
高精度低功耗胎压计方案——气压表解决方案
单片机·嵌入式硬件
LavaLin0114 小时前
STM32H562RGT6 与 STM32F722RET6 规格对比
stm32
VALENIAN瓦伦尼安教学设备14 小时前
设备状态检测振动分析实训台案例分析
大数据·数据库·人工智能·嵌入式硬件·算法