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;
}
相关推荐
FreakStudio8 天前
W55MH32L-EVB 上手测评:硬件 TCP/IP 加持的以太网单片机,MicroPython 零门槛开发
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
✎ ﹏梦醒͜ღ҉繁华落℘13 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
u1521096484913 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
zd84510150013 天前
RS485 总线详解
单片机·嵌入式硬件
半条-咸鱼13 天前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信
牛根生同志13 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
wohoo_wangzi13 天前
苏州晟雅泰电子:关于W25Q128JVSIQ这个芯片物料的参数,规格及应用领域
嵌入式硬件
goldenrolan13 天前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
✎ ﹏梦醒͜ღ҉繁华落℘13 天前
编程基础 --高内聚,低耦合
c语言·单片机
科芯创展13 天前
1A,1MHz,30VIN,XZ4115,降压恒流LED驱动芯片
单片机·嵌入式硬件