前言
本次分享的是 2023 年全国大学生电子设计竞赛 H 题(信号分离装置)的 FPGA+STM32 协同设计方案。题目要求从混合信号 C 中解调出原始的两路波形 A'和 B'。除了增益为 1 的加法器部分外,核心处理逻辑均由 FPGA 与 STM32 配合完成。
题目背景
系统需从信号发生器输出的两路波形 A 和 B 经加法器生成信号 C,任务是从 C 中还原出 A'和 B'。由于重构波形与原信号时钟不同步,存在相位漂移风险,因此需要引入锁相机制。

系统架构
整体数据流向如下: C 信号 → 高速 ADC → FPGA → FIFO → 串口 → STM32 → FFT 识别 → 串口回传 → DDS 重构 → 锁相环 → 高速 DAC → A', B'
通信链路设计
FPGA 与 STM32 之间的数据交互是系统的神经中枢。考虑到采样率(1MHz)与串口波特率(115200)的差异,我们采用了异步 FIFO 进行跨时钟域缓冲。
1. FPGA 端 FIFO 与串口收发
写入速率匹配 ADC 采样频率 1MHz,读取速率适配串口发送 12.5KHz。这里以 AX7035B 开发板为例,状态机控制 FIFO 的读写使能。
assign ad9238_clk_ch0 = clk_1M;
wire wr_en, rd_en, full, empty;
reg [7:0] w_data;
localparam W_IDLE = 1, W_FIFO = 2;
reg[2:0] write_state, next_write_state;
always@(posedge clk_1M or negedge rst) begin
if(rst == 1'b0) write_state <= W_IDLE;
else write_state <= next_write_state;
end
always@(*) begin
case(write_state)
W_IDLE: if(empty == 1'b1) next_write_state <= W_FIFO;
else next_write_state <= W_IDLE;
W_FIFO: if(full == 1'b1) next_write_state <= W_IDLE;
else next_write_state <= W_FIFO;
default: next_write_state <= W_IDLE;
endcase
end
assign wr_en = (next_write_state == W_FIFO) ? 1'b1 : 1'b0;
串口发送模块采用标准 UART 协议,注意 tx_valid 信号的时序对齐,避免丢包。
uart_tx#(.CLK_FRE(50), .BAUD_RATE(115200)) uart_tx(
.clk(clk), .rst_n(rst), .tx_data(tx_data),
.tx_data_valid(tx_valid), .tx_pin(tx_pin)
);
接收端则通过帧头(FF)和帧尾(FE)界定数据包,解析出波形类型及频率信息。
2. STM32 端数据处理
STM32 负责核心的频谱分析。使用 DMA 或中断接收数据后,调用 DSP 库进行 FFT 运算。
波形识别逻辑
已知频率范围在 20KHz-100KHz,步进 5KHz。正弦波频谱集中,三角波含三次谐波导致基波幅值削弱。通过比较基波能量大小即可区分波形。
// 频域能量聚集
for (int i=35; i<1000; i++) {
freq = (int)(((float)i*1000000/2048/5000)+0.5);
if(freq==freq_last) {
v_max = FFT_output[i]*FFT_output[i]+v_max;
} else {
v_max = sqrt(v_max);
freq_change[cnt] = v_max;
fre[cnt] = freq;
freq_last=freq; cnt++;
}
}
// 寻找最大两个能量点
for(int i=0;i<100;i++) {
if(freq_change[i]>max[0]) { /* 更新最大值 */ }
else if(freq_change[i]>max[1] && freq_change[i]<max[0]) { /* 更新次大值 */ }
}
// 阈值判断波形类型
if(max[0]>5.7) wave[0] = 0; // 正弦波
else wave[0] = 1; // 三角波
完整的主循环逻辑包含初始化、FFT 计算、结果回传及 OLED 显示刷新。
波形重构与锁相
得到参数后,FPGA 端利用 DDS 技术重构波形。但直接生成的波形因时钟源差异会产生相位漂移,必须引入锁相环(PLL)。
1. DDS 模块
ROM 表中存储正弦波和三角波的采样点。根据传入的频率字累加相位,查表输出数据。
module dds(input clk, input rst, input [31:0] freq1, output [13:0] dataout1);
reg [31:0] phase_acc;
always @(posedge clk) phase_acc <= phase_acc + freq1;
assign dataout1 = rom_data[phase_acc[21:12]];
endmodule
2. 锁相环实现
鉴相器将输入信号与输出信号相乘并低通滤波,环路滤波器采用 PI 结构调节 Kp 和 Ki,最终反馈相位至 DDS 地址寄存器,消除累积误差。
mult_mix mult_mix_A (.A(ADC_signed), .B(DAC_A), .P(mix_A));
LPF LPF_A (.s_axis_data_tdata(A_filter), .m_axis_data_tdata(A_filter_out));
Loop_filter Loop_filter_A(.i_pd(A_filter_out), .o_frequency_df(A_phase));
3. 用户交互
支持按键消抖、移相设置及数码管显示。通过前三个按键调整相位差,第四个按键确认零点,数码管实时反馈当前相位值。
总结
本方案通过 FPGA 的高速并行处理能力与 STM32 的灵活算法执行能力互补,实现了混合信号的分离与重构。关键点在于跨时钟域的数据传输稳定性以及 PLL 对相位漂移的有效抑制。实际调试中需注意 ADC 采样同步及串口波特率的精度校准。

