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;
}
相关推荐
xiaoyuchidayuma4 小时前
永磁同步发电机的线电压和直流母线电压的关系
嵌入式硬件
潜创微科技4 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
rit84324994 小时前
基于C#的USB HID设备读取测试软件
嵌入式硬件
三佛科技-187366133975 小时前
FT32F103C8AT7兼容GD32F103C8T632 位通用微控制器MCU,替代性分析
单片机·嵌入式硬件
iCxhust5 小时前
8086汇编 word ptr
汇编·单片机·嵌入式硬件·微机原理·8088单板机
嵌入式ZYXC5 小时前
第3篇:《面试题:I2C为什么要加上拉电阻?阻值怎么选?》
stm32·单片机·嵌入式硬件·面试·职场和发展
leo__5206 小时前
C# 虚拟键盘(软键盘)实现
单片机·c#·计算机外设
你疯了抱抱我6 小时前
【STM32】使用 STM32CubeMX 生成项目,LED测试;上位机:STM32F411CEU6
stm32·单片机·嵌入式硬件
今天的你比昨天进步了?8 小时前
单片机程序,keil可以正常编译,VScode编译报错处理
vscode·单片机·嵌入式硬件