1. 项目概述:从零打造数字示波器系统
去年在调试一个高频信号调理电路时,我发现自己手头的示波器带宽不够用。商用高端示波器价格动辄上万,于是萌生了自己搭建数字采样系统的想法。这个项目整合了FPGA高速采样、STM32实时处理和串口屏可视化三大模块,最终实现了一台带宽50MHz、采样率100MS/s的简易示波器。
整套系统的核心在于发挥各器件的优势:AD9200负责模拟信号的高精度数字化,FPGA实现高速数据采集和预处理,STM32通过SPI协议获取数据后完成运算处理,最后通过USART将波形渲染到串口屏。这种架构既保证了采样速率,又降低了整体功耗,特别适合需要便携性的场测场景。
2. 硬件架构设计
2.1 关键器件选型分析
AD9200-100模数转换器是这个系统的前端核心,选择它主要基于三个考量:
- 100MS/s采样率满足中频信号采集需求
- 10位分辨率在成本和性能间取得平衡
- 1.8V低功耗供电适合电池供电场景
FPGA选用Xilinx Artix-7系列XC7A35T,其优势在于:
- 足够多的IO Bank支持LVDS接口
- 内置时钟管理模块实现采样时钟倍频
- 逻辑资源足够实现FIFO缓冲和SPI从机
STM32F407作为主控的考虑点:
- 硬件SPI接口支持最高42MHz时钟
- 192KB RAM可缓存多帧波形数据
- 浮点运算单元加速FFT等算法
2.2 信号链路设计要点
模拟前端需要特别注意:
circuit复制Vin --[1kΩ]--+--[100Ω]-- ADC_IN
|
[10nF]-- GND
这个RC网络实现了:
- 1kΩ电阻限制输入电流保护ADC
- 100Ω阻抗匹配减少反射
- 10nF电容滤除高频噪声
时钟电路采用Si5338时钟发生器,通过FPGA的MMCM模块将25MHz参考时钟倍频到100MHz,为ADC提供低抖动采样时钟。实测时钟相位噪声在100kHz偏移处达到-120dBc/Hz。
3. FPGA数据采集实现
3.1 ADC接口时序设计
AD9200采用DDR(双倍数据速率)输出模式,在ISE中需要配置IDDR原语实现数据采集:
verilog复制IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
) iddr_inst (
.Q1(data[9:0]),
.Q2(),
.C(adc_clk),
.CE(1'b1),
.D(adc_data[i]),
.R(1'b0),
.S(1'b0)
);
关键时序约束:
tcl复制create_clock -period 10.000 -name adc_clk [get_ports adc_clk]
set_input_delay -clock adc_clk -max 2.500 [get_ports adc_data*]
3.2 数据缓冲方案
采用双Bank乒乓缓冲结构:
- BankA接收新数据时,BankB通过SPI输出
- 每采集1024点触发Bank切换
- 状态机控制切换时的无缝衔接
FIFO深度计算公式:
code复制所需深度 = (SPI传输时间 - 采集时间) × 采样率
= (1024×8bit/20MHz - 1024/100MHz) × 100M
≈ 410点 → 取512深度
4. STM32与FPGA的SPI通信
4.1 SPI从机实现技巧
FPGA端采用状态机实现SPI从机:
verilog复制always @(posedge spi_clk or posedge spi_ss) begin
if(spi_ss) begin
bit_cnt <= 0;
end else begin
shreg <= {shreg[14:0], spi_mosi};
if(bit_cnt==15) begin
addr <= shreg[7:0];
bit_cnt <= 0;
end
end
end
STM32配置为SPI主机模式:
c复制hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
4.2 数据包协议设计
采用帧头+长度+数据+校验的格式:
code复制typedef struct {
uint8_t header; // 0xAA
uint16_t len; // 数据长度
uint8_t *data; // 采样数据
uint16_t crc; // CRC-16/CCITT
} WavePacket;
CRC校验采用查表法加速:
c复制uint16_t crc16(const uint8_t *data, uint32_t len) {
uint16_t crc = 0xFFFF;
while(len--) {
crc = (crc << 8) ^ crc_table[((crc >> 8) ^ *data++) & 0xFF];
}
return crc;
}
5. 串口屏显示优化
5.1 波形渲染算法
采用差分绘制技术减少刷新量:
c复制void draw_waveform(int16_t *new_data) {
static int16_t prev_data[DISPLAY_WIDTH];
for(int x=0; x<DISPLAY_WIDTH; x++) {
if(abs(new_data[x]-prev_data[x])>2) {
lcd_draw_line(x, prev_data[x], x, new_data[x], BG_COLOR);
lcd_draw_pixel(x, new_data[x], WAVE_COLOR);
}
}
memcpy(prev_data, new_data, sizeof(prev_data));
}
5.2 界面交互设计
通过触摸事件实现功能切换:
c复制void handle_touch(uint16_t x, uint16_t y) {
if(y < 30) { // 标题栏区域
if(100<x && x<180) ch1_enable ^= 1;
else if(200<x && x<280) ch2_enable ^= 1;
} else if(y > 210) { // 底部菜单
if(x < 80) timebase /= 2;
else if(x > 240) timebase *= 2;
}
}
6. 系统调试经验
6.1 信号完整性保障
实测中发现当SPI时钟超过15MHz时,数据误码率显著上升。通过以下措施改善:
- 将PCB走线从直连改为蛇形等长走线
- 在FPGA端添加IO延迟约束
tcl复制set_input_delay -clock spi_clk -min 1.000 [get_ports spi_miso]
set_output_delay -clock spi_clk -max 2.000 [get_ports spi_miso]
- 在STM32端配置SPI的采样边沿调整
c复制hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
6.2 电源噪声抑制
系统最初在采样高频信号时出现周期性毛刺,排查发现是开关电源噪声:
- 改用LDO为ADC供电
- 在1.8V电源轨添加π型滤波:
code复制[10μF]--[1Ω]--[0.1μF]
- 时钟电源单独采用LC滤波:
code复制[22μH]--[100μF]--[0.1μF]
7. 性能优化技巧
7.1 采样率动态调整
通过FPGA的MMCM模块实现时钟分频:
verilog复制MMCME2_BASE #(
.CLKOUT0_DIVIDE_F(4.0), // 100MHz
.CLKOUT1_DIVIDE(8), // 50MHz
.CLKOUT2_DIVIDE(16) // 25MHz
) mmcm_inst (
.CLKOUT0(adc_clk_100M),
.CLKOUT1(adc_clk_50M),
.CLKOUT2(adc_clk_25M)
);
7.2 数据压缩算法
采用差分编码减少传输数据量:
c复制void delta_encode(int16_t *data, uint16_t len) {
int16_t prev = data[0];
for(uint16_t i=1; i<len; i++) {
int16_t temp = data[i];
data[i] -= prev;
prev = temp;
}
}
这套系统最终实现了:
- 100MS/s实时采样(10bit分辨率)
- 50MHz模拟带宽(-3dB点)
- 12种时基可调(10ns/div~1s/div)
- 波形捕获内存深度达16K点
- 整机功耗仅3.5W(锂电池供电)
