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 频率:
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 里还需要确认的点
-
ADC 配置
- External Trigger:选用 TIMx_CHx 作为触发源,边沿 RISING。
- DMA Continuous Requests:ENABLE。
- DMA Mode:循环(Circular)。
- Data Alignment:右对齐(12 位下是 0~4095)。
- NbrOfConversion:1。
-
DMA 通道
- 方向:外设到内存。
- 外设对齐:Half-Word;内存对齐:Half-Word。
- 模式:Circular。
- 要勾选“中断 On Half Transfer”和“中断 On Transfer Complete”。
-
定时器 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(只要能出个方波就行)。
-
-
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()
里确保对应的选项和通道匹配即可。