11111

下面是一套针对 FFT_LENGTH=1024、BUF_SIZE=2048、目标采样率 1 MSPS 的 STM32F407 CubeMX 配置参数表。核心思路是用 TIM2 产生 1 MHz 的 TRGO 脉冲去触发 ADC1 单通道采样,DMA 循环模式将数据搬到两个 1024 点的缓冲区,利用 Half-Complete/Complete 回调交替处理前后半缓冲区。


时钟体系 (如截图所示)

名称
SYSCLK 128 MHz (来自主 PLL)
AHB Prescaler ÷1 → HCLK = 128 MHz
APB1 Prescaler ÷4 → PCLK1 = 32 MHz
→ APB1定时器时钟 PCLK1 ×2 = 64 MHz
APB2 Prescaler ÷2 → PCLK2 = 64 MHz (ADC)

TIM2(触发频率 1 MHz)

选项 参数
Mode PWM Generation Channel2
Counter Settings
• Prescaler (PSC) 0
• Counter Period (ARR) 64-1
• Counter Mode Up
• Clock Division No Division
• Auto-reload Preload Disable
PWM Generation Channel2
• Mode PWM mode 1
• Pulse 31 (50% 占空比,可任意,只需方波)
• Output compare preload Enable
Master/Slave & TRGO
• Master/Slave Mode (MSM) Enable (同步 TRGO)
• Trigger Event Selection OC2REF

产生的 TRGO = OC2REF,每次通道 2 输出高电平时(跑满 ARR)触发一次。TRGO 频率:

\[F_{\rm TRGO} = \frac{64\,\mathrm{MHz}}{(0+1)\times(63+1)} = 1\,\mathrm{MHz}. \]


ADC1(1 MSPS,单通道,DMA 循环)

ADCs_Common_Settings

选项 参数
Mode Independent mode
Clock Prescaler PCLK2 divided by 2

ADC_Settings

选项 参数
Resolution 12 bits (15 ADC Clock cycles)
Data Alignment Right alignment
Scan Conversion Mode Disabled
Continuous Conversion Mode Disabled
Discontinuous Conversion Mode Disabled
DMA Continuous Requests Enabled
Overrun Data Overwritten
EOC Selection EOC flag at end of single channel conv.

ADC_Regular_ConversionMode

选项 参数
Number Of Conversion 1
External Trigger Conversion Source Timer2 Trigger Out event
External Trigger Conversion Edge Trigger detection on the rising edge
Rank 1
Channel ADC_CHANNEL_13 (PC3)
Sampling Time 3 cycles (最短,如噪声过大可选15/28/... cycles)

DMA Settings (ADC1)

选项 参数
Mode Circular
Direction Peripheral to Memory
PeriphInc Disable
MemInc Enable
PeriphDataAlign Half-Word
MemDataAlign Half-Word
Priority Low
FIFO Mode Disable

在 NVIC 中打开 ADC_IRQn,并在 HAL_ADC_ConvHalfCpltCallback() / HAL_ADC_ConvCpltCallback() 中分别置位前/后半缓冲区标志。


为什么能满足 FFT 要求?

  • 采样率 1 MSPS 对应 1024 点 FFT 分辨率 1 MHz / 1024 ≈ 976 Hz,接近 1 kHz,足够分辨 1 kHz 以上的谐波。
  • 双缓冲 (BUF_SIZE=2048):DMA 循环填充,前 1024 点和后 1024 点各自触发回调,主循环即可无缝接收并处理。
  • 外部触发 + DMA:硬件定时精度高,无 CPU 干扰,保证采样时序一致。

只要在 CubeMX 中按上表填入对应参数,生成代码后调用:

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, BUF_SIZE);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

即可自动用 TIM2 的 1 MHz PWM 去触发 ADC1,DMA 将连续采样数据写入 adc_buf[0..2047],并在半/全缓冲完成时回调你去做 FFT。这样配置正好满足 1024 点 FFT、1 MSPS 采样、本振谐波分析的要求。
下面是一段示例代码,演示如何在你已经配置好的 ADC + DMA 双缓冲框架下,接收一半就立刻跑 FFT,并且找出幅度谱中最大的两个谱点及其相位。


1. 在全局或某个合适的 .c 文件里添加下面的声明与变量

#include "arm_math.h"    // CMSIS-DSP 库
#include <math.h>

#define FFT_LENGTH    1024
#define HALF_FFT      (FFT_LENGTH/2)
extern uint16_t adc_val_i[];        // 2048 点环形缓冲(已在 MX_ADC1_Init 中启动 DMA)
static float32_t fft_input[FFT_LENGTH];
static float32_t fft_output[FFT_LENGTH];  // RFFT 输出(实部虚部交替存放)
static float32_t mag[HALF_FFT+1];
static float32_t phase[HALF_FFT+1];
static arm_rfft_fast_instance_f32 S;
  • main() 或初始化函数中,初始化 RFFT 结构体:
// 放在 MX_*Init 调用完之后
arm_rfft_fast_init_f32(&S, FFT_LENGTH);

2. 修改你的半/全转换回调,把处理逻辑扔进去

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
    if (hadc->Instance == ADC1)
    {
        // --- 1. 拷贝这一半的 1024 点到浮点输入缓冲
        for (int i = 0; i < FFT_LENGTH; i++)
        {
            // 假设 adc_val_i[0..1023] 存的是这一半
            // 这里把 uint16 转到 [0..3.3] 浮点
            fft_input[i] = (float32_t)adc_val_i[i] * 3.3f / 4096.0f;
        }

        // --- 2. 执行 RFFT(实数快速 FFT)
        arm_rfft_fast_f32(&S, fft_input, fft_output, 0);

        // --- 3. 计算幅度和相位
        // fft_output 存成 [Re(0), Im(0), Re(1), Im(1), …, Re(N/2), Im(N/2)]
        for (int k = 0; k <= HALF_FFT; k++)
        {
            float32_t re = fft_output[2*k];
            float32_t im = fft_output[2*k + 1];
            mag[k]   = sqrtf(re*re + im*im);
            phase[k] = atan2f(im, re);  
        }

        // --- 4. 找出最大的两个谱点
        uint32_t idx1 = 1, idx2 = 1;
        float32_t m1 = 0, m2 = 0;
        for (int k = 1; k <= HALF_FFT; k++)
        {
            if (mag[k] > m1)
            {
                m2 = m1;     idx2 = idx1;
                m1 = mag[k]; idx1 = k;
            }
            else if (mag[k] > m2)
            {
                m2 = mag[k]; idx2 = k;
            }
        }

        // idx1、idx2:最大、次大谱点下标(对应频率 bin)
        // m1、m2:它们的幅度
        // phase[idx1]、phase[idx2]:它们的相位

        // --- 5. 在这里你可以把它们发到上位机,或存到全局变量
        printf("Peak1: bin=%lu, mag=%.3f, phase=%.3f rad\r\n", idx1, m1, phase[idx1]);
        printf("Peak2: bin=%lu, mag=%.3f, phase=%.3f rad\r\n", idx2, m2, phase[idx2]);
    }
}

如果你要在全完成(second half)时也一样处理,就把相同的逻辑放到 HAL_ADC_ConvCpltCallback() 里,只是把索引改成 adc_val_i + FFT_LENGTH


3. CubeMX 里还需要确认的点

  1. ADC 配置

    • External Trigger:选用 TIMx_CHx 作为触发源,边沿 RISING。
    • DMA Continuous RequestsENABLE
    • DMA Mode:循环(Circular)。
    • Data Alignment:右对齐(12 位下是 0~4095)。
    • NbrOfConversion:1。
  2. DMA 通道

    • 方向:外设到内存。
    • 外设对齐:Half-Word;内存对齐:Half-Word。
    • 模式:Circular。
    • 要勾选“中断 On Half Transfer”和“中断 On Transfer Complete”。
  3. 定时器 TIMx(比如 TIM2)

    • 时钟源:Internal Clock。

    • Prescaler 和 Counter Period:算出你想要的采样率(1.024 MSPS)

      f_SAMPLE = TIM_CLK / (PSC+1) / (ARR+1)
      
    • Master Mode Selection:TRGO on Update Event 或者 OCxREF(你上面用的是 OC1REF)。

    • 配置一个通道为 PWM 输出,Pulse = ARR/2(只要能出个方波就行)。

  4. NVIC

    • 确保 ADC_IRQn 优先级高于其他不关键中断。
    • 确保 HAL_ADC_ConvHalfCpltCallback / ConvCpltCallback 被正确回调。

到此,你就拥有一个:

  • 1.024 MSPS PWM 触发 ADC
  • 2048 点环形 DMA + 半/全完成中断
  • 半缓冲触发 FFT → 实时算出最大两点的幅度和相位

只要把上面的回调和初始化贴进你的项目,再在 SystemClock_Config()MX_ADC1_Init()MX_DMA_Init()MX_TIMx_Init() 里确保对应的选项和通道匹配即可。

posted @ 2025-07-20 12:37  无敌烤肉大王  阅读(15)  评论(0)    收藏  举报