1. 项目缘起与整体设计思路
PLFM_RADAR 这个名字拆开看,PLFM 大概率指代的是Pulse Linear Frequency Modulation,也就是脉冲线性调频,业内更习惯叫它 LFM 或者 chirp 信号。RADAR 就不用多解释了。合在一起,这是一个以相控阵体制为基础、发射线性调频脉冲、通过 FPGA 做实时信号处理、再用 STM32 做系统控制与数据调度的雷达项目。我第一次看到这个标题的时候,脑子里立刻浮现出一个典型的“FPGA 做脏活累活、MCU 做管家”的异构架构,这也是目前中小型雷达系统里最务实、性价比最高的一种组合方式。
为什么这么说?因为雷达信号处理对时序的要求极其苛刻。你想想,一个 LFM 脉冲,脉宽可能只有几十微秒,带宽却能做到几十甚至上百兆赫兹。在这个时间窗口里,你要完成 AD 采样、数字下变频、脉冲压缩、动目标显示,每一步都是纳秒级的时序约束。STM32 再强,主频也就几百兆,跑个 FFT 都费劲,更别说实时处理了。所以 FPGA 必须扛起信号处理这条主线。而 STM32 的价值在于它丰富的外设、成熟的生态和相对友好的开发门槛,适合做上位机通信、波束调度、状态监控、人机交互这些“慢速但复杂”的活。
这个项目的核心需求可以归纳成四条。第一,多通道相控阵收发控制,需要精确的通道相位管理,这直接决定了波束指向和旁瓣性能。第二,高速 ADC 数据采集与实时处理,采样率通常在几十到几百 MSPS 之间,数据吞吐量巨大。第三,FPGA 与 STM32 之间的高速数据交互,既要传原始数据,也要传处理结果和控制指令。第四,系统级的时序同步与状态管理,保证发射、接收、处理、上传各个环节严丝合缝。
适合谁来参考这个项目?我觉得有三类人。一类是电子、通信、雷达相关专业的本科生或研究生,拿它做毕业设计或者竞赛项目,既有深度又有展示度。第二类是做 FPGA 信号处理方向、想找一个完整项目练手的工程师,雷达信号链是一个非常经典的实战场景。第三类是做嵌入式系统、想了解异构架构怎么协同工作的开发者,FPGA 加 MCU 的组合在边缘计算、通信测试终端里越来越常见。
方案选型上,我倾向于这样搭配:FPGA 选 Xilinx Artix-7 或者 Altera Cyclone IV 系列,资源够用、开发工具成熟、板卡价格也友好。STM32 选 F4 或者 H7 系列,F4 性价比高,H7 性能更强、带以太网和高速 USB,适合数据量大的场景。ADC 选 AD9226 或者 AD7606 这类经典芯片,前者是单通道 12 位 65MSPS,后者是八通道 16 位 200kSPS 同步采样,根据你的通道数和带宽需求来定。相控阵前端可以用 ADAR1000 这类集成波束成形芯片,也可以用分立移相器加衰减器自己搭,前者省事,后者灵活。
提示:选型时不要一味追求高性能指标,先明确你的探测距离、分辨率、波束扫描范围这几个核心参数,再倒推带宽、采样率、通道数。很多新手一上来就选最贵的芯片,结果发现根本用不上,还增加了调试难度。
2. 相控阵雷达的核心原理与关键参数
2.1 相控阵波束成形到底是怎么回事
相控阵雷达和传统机械扫描雷达最大的区别,在于它不需要转动天线。它靠的是多个天线单元组成的阵列,通过控制每个单元发射或接收信号的相位差,让电磁波在空间中产生干涉,形成特定方向的波束。你可以把它想象成一排人同时往水里扔石头,如果大家同时扔,水波会向四周扩散;但如果从一端到另一端依次延迟一点点扔,水波就会叠加成一个朝特定方向传播的波前。相位控制就是这个“依次延迟”的电子版本。
具体来说,假设阵元间距为 d,相邻阵元之间的相位差为 Δφ,那么波束指向角度 θ 满足这个关系:
Δφ = 2π · d · sin(θ) / λ
其中 λ 是工作波长。这个公式是整个相控阵的基石。举个例子,工作频率 10GHz,波长就是 3 厘米。如果阵元间距取半波长 1.5 厘米,想让波束指向 30 度,那么相邻阵元的相位差就是 2π × 1.5 × sin(30°) / 3 = π/2,也就是 90 度。这个计算在 FPGA 里可以用 CORDIC 算法实时完成,也可以用查找表预先算好。
在实际项目中,相位控制通常由 FPGA 输出数字控制字给移相器芯片,或者直接通过 DAC 生成控制电压。ADAR1000 这类芯片内部集成了 4 通道的移相和衰减功能,通过 SPI 接口配置,FPGA 只需要算出每个通道的相位字就行。这里有个坑:相位是周期性的,360 度和 0 度是一回事,所以在计算相位差的时候一定要做模 360 处理,否则移相器可能收到一个超出范围的值而工作异常。
2.2 LFM 脉冲压缩为什么这么重要
雷达最核心的两个指标是探测距离和距离分辨率。探测距离取决于发射功率和脉冲能量,距离分辨率取决于信号带宽。问题是,如果发射一个很窄的脉冲来提高分辨率,脉冲能量就小,探测距离就短;如果发射一个很宽的脉冲来增加能量,分辨率又变差了。LFM 脉冲压缩就是解决这个矛盾的关键技术。
LFM 信号的特点是频率随时间线性变化,比如从 9.5GHz 线性扫到 10.5GHz,带宽 1GHz,脉宽 100 微秒。接收的时候,把这个回波信号通过一个匹配滤波器,也就是和发射信号共轭反转的信号做卷积,就能把宽脉冲压缩成一个极窄的尖峰。压缩后的脉宽大约是 1/B,B 是带宽。1GHz 带宽对应 1 纳秒的压缩脉宽,距离分辨率就是光速乘以 1 纳秒除以 2,大约 15 厘米。而发射脉宽 100 微秒对应的能量,让探测距离大大增加。
在 FPGA 里实现脉冲压缩,通常有两种方式。一种是时域卷积,直接用 FIR 滤波器结构,适合脉宽较短、带宽较低的场景。另一种是频域处理,先做 FFT,乘以匹配滤波器的频域响应,再做 IFFT,适合大带宽、大时宽积的场景。我一般推荐频域方式,因为 FPGA 厂商都提供了 FFT IP 核,调用方便,而且计算效率高。Xilinx 的 FFT IP 核支持实时流水线模式,可以连续处理数据流,非常适合雷达的连续脉冲场景。
注意:做频域脉冲压缩时,FFT 的点数要大于等于发射脉宽对应的采样点数加上回波最大延迟对应的点数,否则会出现循环卷积混叠,导致虚假目标。这个点数计算一定要在项目初期就确定好,因为它直接决定了 FPGA 的 Block RAM 资源消耗。
2.3 相控阵的通道一致性有多要命
相控阵雷达的性能高度依赖各通道之间的一致性。如果通道之间的幅度和相位存在偏差,波束方向图就会畸变,旁瓣抬高,甚至出现波束指向错误。在实际工程中,通道一致性校准是一个绕不开的环节。
校准的基本思路是:在暗室或者已知环境下,用一个标准信号源从特定方向照射阵列,采集各通道的接收数据,计算每个通道相对于参考通道的幅度和相位差,然后在后续处理中做补偿。补偿可以在 FPGA 里做,也可以在 STM32 里做。FPGA 做补偿的好处是实时性好,每个采样点都能修正;STM32 做补偿的好处是灵活,可以随时更新校准系数。
我踩过的一个坑是:校准系数会随温度漂移。有一次在夏天做外场测试,早上校准好的系数,到了中午波束就偏了好几度。后来在系统里加了温度传感器,根据温度查表动态调整校准系数,问题才解决。所以如果你的项目要在户外工作,一定要考虑温度补偿。
3. FPGA 信号处理链的详细实现
3.1 高速 ADC 数据采集与接口设计
雷达系统的第一环是 ADC 采集。以 AD9226 为例,它是 12 位、最高 65MSPS 的并行输出 ADC,接口简单,适合入门。但如果你要做大带宽雷达,可能需要 AD9233 这类 125MSPS 甚至更高速的芯片。高速 ADC 的输出通常是 LVDS 或者并行 CMOS 电平,FPGA 这边要选对应的 IO 标准。
对于 LVDS 接口,FPGA 的 SelectIO 资源可以直接支持,但要注意差分对的走线等长和阻抗匹配。我见过一个项目,因为 PCB 上 LVDS 差分对没有做等长,导致采样数据偶尔出错,查了好几天才发现是硬件问题。软件层面,FPGA 接收 LVDS 数据需要用 IDELAY 和 ISERDES 原语做源同步采样,Xilinx 提供了 SelectIO Wizard 工具可以自动生成这部分代码,省了不少事。
数据采集进来之后,第一步通常是数字下变频。因为 ADC 采样率很高,直接处理数据量太大,而且雷达信号通常是带通信号,中心频率不在零频。DDC 的做法是:把 ADC 数据乘以一个由 NCO 生成的复本振信号,然后通过低通滤波器抽取。这样就把高频信号搬到了基带,同时降低了数据率。NCO 可以用 Xilinx 的 DDS Compiler IP 核生成,低通滤波和抽取可以用 FIR Compiler IP 核实现。
// 简化的 DDC 核心逻辑示意 // 输入:adc_data (12位有符号) // 输出:i_out, q_out (16位有符号,抽取后) wire signed [15:0] nco_cos, nco_sin; wire signed [27:0] mult_i, mult_q; // NCO 生成正交本振 dds_compiler_0 u_dds ( .aclk(clk), .m_axis_data_tdata({nco_sin, nco_cos}) ); // 混频 assign mult_i = adc_data * nco_cos; assign mult_q = adc_data * nco_sin; // 低通滤波 + 抽取(假设抽取因子为 4) fir_compiler_0 u_fir_i ( .aclk(clk), .s_axis_data_tdata(mult_i[27:12]), .m_axis_data_tdata(i_out) ); fir_compiler_0 u_fir_q ( .aclk(clk), .s_axis_data_tdata(mult_q[27:12]), .m_axis_data_tdata(q_out) );这段代码只是示意,实际项目中还要考虑位宽增长、滤波器系数设计、抽取控制等问题。滤波器系数可以用 MATLAB 的 fdatool 或者 Python 的 scipy.signal 设计,导出成 COE 文件加载到 FIR IP 核里。
3.2 脉冲压缩的 FPGA 实现细节
脉冲压缩是整个信号处理链里计算量最大的部分。我推荐用频域方式,流程是:对 DDC 输出的 I/Q 数据做 FFT,然后乘以匹配滤波器的频域系数,再做 IFFT,最后取模平方得到距离像。
Xilinx 的 FFT IP 核支持多种配置,对于雷达应用,我建议选Pipelined Streaming I/O模式,这样可以连续处理数据流,不需要在两次 FFT 之间停顿。点数根据你的脉冲采样点数来定,比如 1024 点或者 2048 点。FFT 的输入数据需要做自然序到位反转序的转换,IP 核可以自动处理,但要注意数据格式是定点还是浮点。定点实现资源省,但要注意溢出和精度;浮点实现精度高,但资源消耗大。对于大多数雷达应用,16 位定点就够了。
匹配滤波器的频域系数是发射信号频谱的共轭。如果你发射的是理想 LFM 信号,它的频谱近似矩形,匹配滤波器就是一个带通滤波器。但实际发射信号会有幅度和相位失真,所以最好用实测的发射波形做参考,计算其频谱共轭作为匹配系数。这个系数可以在 MATLAB 里算好,存到 FPGA 的 ROM 里。
IFFT 之后得到的是复数距离像,取模平方就是功率距离像。取模平方可以用两个乘法器加一个加法器实现,也可以用 CORDIC 算法算幅值。我一般用乘法器方案,简单直接,资源也够用。
实操心得:FFT IP 核的配置界面里有一个“Scaling”选项,可以选择自动缩放或者固定缩放。自动缩放会根据每级蝶形运算的溢出情况动态调整,但会引入额外的延迟和控制逻辑。固定缩放需要你提前算好每级的最大可能增益,手动设置缩放因子。我建议新手先用自动缩放,跑通了再优化。
3.3 多端口 DDR 读写:数据缓存的命脉
雷达信号处理会产生大量中间数据,比如原始 ADC 数据、DDC 输出、FFT 结果、距离像等等。这些数据不可能全部放在 FPGA 的 Block RAM 里,必须外挂 DDR 存储器。DDR 的读写控制是 FPGA 项目里最容易出问题的环节之一,尤其是多端口并发访问的时候。
Xilinx 提供了 MIG(Memory Interface Generator)IP 核来生成 DDR 控制器,你只需要配置好 DDR 的型号、位宽、时序参数,MIG 就会生成一个用户接口。但这个用户接口是单端口的,如果你有多个模块需要同时访问 DDR,就需要自己做一个仲裁器。常见的做法是用 AXI 总线把多个主设备连到 DDR 控制器上,Xilinx 的 AXI Interconnect 可以自动处理仲裁。
但 AXI 总线有开销,对于雷达这种高吞吐量场景,有时候直接用 Native 接口更高效。我的做法是:给每个需要访问 DDR 的模块分配一个独立的 FIFO,然后用一个轮询仲裁器依次把 FIFO 里的数据写入 DDR。读的时候类似,根据地址从 DDR 读出数据,分发到对应的 FIFO。仲裁器的优先级可以根据数据的重要性来定,比如原始 ADC 数据的优先级最高,因为丢了就补不回来;处理结果的优先级可以低一些,因为可以重算。
// 简化的多端口 DDR 仲裁器状态机 // 假设有 3 个写端口:ADC 原始数据、DDC 输出、FFT 结果 localparam IDLE = 0, WR_ADC = 1, WR_DDC = 2, WR_FFT = 3; reg [1:0] state; reg [1:0] next_state; always @(posedge clk) begin if (rst) state <= IDLE; else state <= next_state; end always @(*) begin case (state) IDLE: begin if (!fifo_adc_empty) next_state = WR_ADC; else if (!fifo_ddc_empty) next_state = WR_DDC; else if (!fifo_fft_empty) next_state = WR_FFT; else next_state = IDLE; end WR_ADC: begin if (fifo_adc_empty) next_state = IDLE; else next_state = WR_ADC; end // 类似处理其他状态 endcase end这个状态机只是示意,实际项目中还要考虑 DDR 的突发长度、刷新周期、读写切换开销等因素。DDR 的读写切换是有代价的,频繁切换会降低有效带宽。所以仲裁器应该尽量批量处理,比如连续写 64 个数据再切换方向。
4. STM32 在系统中的角色与实现
4.1 STM32 与 FPGA 的通信接口选型
STM32 和 FPGA 之间的通信是整个系统的“神经中枢”。FPGA 处理完的数据要传给 STM32,STM32 的控制指令要下发给 FPGA。接口选型主要看数据量和实时性要求。
如果数据量不大,比如每秒几兆字节,用SPI就够了。STM32 的 SPI 可以跑到几十兆赫兹,FPGA 这边实现一个 SPI 从机也很简单。SPI 的优点是引脚少、协议简单、STM32 端有现成的 HAL 库。缺点是全双工但单向速率有限,而且没有硬件流控。
如果数据量中等,比如每秒几十兆字节,可以用FSMC/FMC接口。STM32 的 FMC 可以把 FPGA 当成一个外部 SRAM 来访问,读写速度很快,而且时序由硬件控制,CPU 负担小。FPGA 这边实现一个 FMC 从机接口,把寄存器映射到特定的地址空间。这种方式的缺点是占用引脚多,PCB 布线麻烦。
如果数据量很大,比如每秒上百兆字节,那就得上高速 USB 或者以太网。STM32H7 系列带 USB 2.0 High Speed 和以太网 MAC,可以满足这个需求。USB 的好处是通用性强,随便插一台电脑就能用;以太网的好处是传输距离远,适合组网。我个人的偏好是:调试阶段用 USB,方便抓数据;正式部署用以太网,稳定可靠。
提示:不管选哪种接口,都建议在 FPGA 和 STM32 之间加一层异步 FIFO。因为两边的时钟域不同,直接跨时钟域传输数据容易出亚稳态问题。异步 FIFO 可以用 FPGA 的 Block RAM 实现,Xilinx 和 Intel 都有现成的 IP 核。
4.2 STM32 端的 USB 设备开发要点
如果你选 USB 作为通信接口,STM32 端的开发有几个关键点。首先是USB 描述符的配置,包括设备描述符、配置描述符、接口描述符、端点描述符。对于雷达数据传输,我建议用CDC 类(虚拟串口)或者Vendor 自定义类。CDC 类的好处是免驱,插上电脑就能识别成串口,用串口助手就能看数据;Vendor 类的好处是灵活,可以自定义传输协议,带宽利用率更高。
STM32 的 USB 库有 HAL 库和 LL 库两种。HAL 库封装度高,上手快,但代码效率略低;LL 库更接近寄存器,效率高,但需要你对 USB 协议有更深的理解。我一般用 HAL 库做快速原型,验证功能没问题后再考虑用 LL 库优化。
USB 传输的模式有控制传输、中断传输、批量传输、同步传输四种。雷达数据适合用批量传输,因为它保证数据完整性,不丢包,虽然延迟不固定,但雷达数据处理本来就不是硬实时。批量传输的端点缓冲区大小可以配置,STM32H7 的 USB HS 支持最大 1024 字节的端点缓冲区,一次传输 1024 字节,效率很高。
// STM32 USB CDC 数据发送示例 // 假设已经初始化好 USB CDC 设备 uint8_t radar_data[1024]; uint32_t data_len = 1024; // 等待 USB 准备好 while (CDC_Transmit_FS(radar_data, data_len) == USBD_BUSY) { // 可以在这里做一些其他事情,或者直接等待 } // 注意:CDC_Transmit_FS 是阻塞式的,如果数据量大, // 建议用 DMA 或者双缓冲机制来提高效率实际项目中,我建议用双缓冲机制:一个缓冲区在发送的时候,另一个缓冲区在填充数据,这样就不会因为等待 USB 传输而阻塞数据采集。STM32H7 的 USB 外设支持 DMA,可以进一步降低 CPU 占用。
4.3 波束调度与系统状态管理
STM32 在雷达系统里最重要的角色是波束调度。相控阵雷达可以电子扫描波束,扫描策略直接决定了雷达的探测性能和响应速度。常见的扫描策略有:顺序扫描,波束从一边扫到另一边,简单但效率低;驻留扫描,在重点区域多停留一段时间,提高检测概率;自适应扫描,根据目标检测结果动态调整扫描区域,效率最高但算法复杂。
在 STM32 里实现波束调度,可以用一个状态机来描述。每个状态对应一个波束指向,状态切换的时候,STM32 计算好相位控制字,通过 SPI 或者 FMC 下发给 FPGA,FPGA 再转发给移相器芯片。状态机的切换时机可以由定时器触发,也可以由外部事件触发。
// 简化的波束调度状态机 typedef enum { BEAM_SCAN_LEFT, BEAM_SCAN_RIGHT, BEAM_TRACK_TARGET, BEAM_IDLE } beam_state_t; beam_state_t current_state = BEAM_SCAN_LEFT; uint16_t beam_angle = 0; void beam_scheduler(void) { switch (current_state) { case BEAM_SCAN_LEFT: beam_angle += 5; // 每次步进 5 度 if (beam_angle >= 60) { current_state = BEAM_SCAN_RIGHT; } break; case BEAM_SCAN_RIGHT: beam_angle -= 5; if (beam_angle <= -60) { current_state = BEAM_SCAN_LEFT; } break; case BEAM_TRACK_TARGET: // 根据目标角度调整波束 break; default: break; } set_beam_phase(beam_angle); // 计算并下发相位控制字 }系统状态管理还包括温度监控、电源监控、故障检测等。STM32 的 ADC 可以采集各路电压和温度传感器的值,通过 DMA 自动扫描,不占用 CPU。如果检测到异常,比如温度过高或者电源电压跌落,STM32 可以立即切换到安全模式,停止发射,保护硬件。
5. 系统联调与常见问题排查
5.1 时钟同步:整个系统的“心跳”
雷达系统对时钟同步的要求极高。FPGA、STM32、ADC、DAC、移相器,所有器件都必须工作在同一个时钟参考下,否则相位关系就乱了。通常的做法是:用一个高稳定度的晶振或者时钟芯片产生参考时钟,通过时钟分配芯片扇出到各个器件。
我踩过的一个坑是:时钟走线不等长导致各器件时钟相位不一致。有一次调试,发现波束方向总是偏几度,查了很久才发现是 FPGA 和 ADC 的时钟走线差了 2 厘米,对应大约 100 皮秒的延迟,在 10GHz 载频下就是 360 度的相位误差。后来在 PCB 上做了等长处理,问题解决。所以如果你的项目对相位精度要求高,PCB 设计阶段一定要重视时钟走线。
另一个坑是时钟抖动。ADC 的采样时钟如果抖动大,信噪比会明显下降。选择时钟芯片的时候,要看它的相位噪声指标,通常要求小于 1 皮秒 RMS。如果预算有限,至少也要用专用的时钟缓冲芯片,不要直接用 FPGA 的普通 IO 输出时钟。
5.2 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 波束指向偏差大 | 通道相位不一致 | 用矢量网络分析仪测各通道相位 | 做通道校准,加温度补偿 |
| 距离像旁瓣高 | 脉冲压缩系数不对 | 对比 MATLAB 仿真和 FPGA 结果 | 重新计算匹配滤波器系数 |
| 数据偶尔出错 | 跨时钟域亚稳态 | 用示波器看时序,检查 FIFO 标志 | 加异步 FIFO,做时序约束 |
| USB 传输丢包 | 端点缓冲区太小 | 用 USB 分析仪抓包 | 增大缓冲区,用 DMA 传输 |
| DDR 读写错误 | 时序参数不对 | 用 MIG 自带的自检功能 | 调整时序参数,重新校准 |
| FPGA 资源不够 | 逻辑设计冗余 | 看综合报告的资源占用 | 优化代码,复用模块,降低位宽 |
| STM32 死机 | 中断优先级冲突 | 用调试器看堆栈 | 调整中断优先级,加看门狗 |
| ADC 采样噪声大 | 电源纹波大 | 用示波器看电源纹波 | 加 LC 滤波,用低噪声 LDO |
这张表里的每一个问题,我都在实际项目中遇到过。最让我头疼的是跨时钟域亚稳态,因为它不是必现的,有时候跑几个小时才出一次错,排查起来非常痛苦。后来我养成了一个习惯:只要信号跨时钟域,一律加两级同步器或者异步 FIFO,绝不心存侥幸。
5.3 调试工具与技巧
调试雷达系统,光靠示波器和万用表是不够的。我强烈建议配一个逻辑分析仪,比如 Saleae 或者 DSLogic,可以同时抓多路数字信号,分析 SPI、FMC、USB 的时序非常方便。如果预算充足,可以上频谱分析仪和矢量网络分析仪,前者用来看发射信号的频谱质量,后者用来测通道的幅度相位一致性。
FPGA 内部的调试,Xilinx 的ILA(Integrated Logic Analyzer)是神器。你可以把内部信号接到 ILA 上,通过 JTAG 实时观察波形,不用引出引脚。我一般会在关键路径上埋几个 ILA,比如 ADC 接口、DDC 输出、FFT 输入输出、DDR 读写接口。这样一旦出问题,可以快速定位是哪一级出了错。
STM32 端的调试,除了常规的 printf 和断点,我还推荐用SEGGER RTT。它可以通过 JTAG 接口实时输出日志,不占用串口,速度也快。在雷达这种数据量大的场景下,RTT 比串口打印方便得多。
实操心得:调试的时候一定要分模块验证,不要等整个系统搭好了再一起调。先单独验证 ADC 采集,确认数据正确;再验证 DDC,用 MATLAB 对比输出;再验证脉冲压缩,用点目标回波测试;最后再联调整个系统。这样出了问题容易定位,不会一团乱麻。
6. 写在最后的一些个人体会
这个项目我从头到尾做了大概半年多,中间经历了无数次调试和返工。最大的感受是:雷达系统是一个典型的木桶,任何一块短板都会让整体性能大打折扣。你可能 FPGA 代码写得很好,但 PCB 上一条走线没处理好,整个系统就废了。所以做这类项目,一定要有系统思维,不能只盯着自己熟悉的那一块。
另外,仿真和实测的差距往往比想象中大。我在 MATLAB 里仿真的脉冲压缩结果非常漂亮,旁瓣抑制比能做到 -40dB 以下。但实际系统里,由于 ADC 量化噪声、时钟抖动、通道不一致等因素,旁瓣抑制比只能做到 -25dB 左右。这不是代码的问题,是物理世界的限制。接受这个现实,然后在系统层面想办法优化,才是正确的态度。
最后分享一个小技巧:给系统加一个自检模式。上电之后,FPGA 产生一个内部测试信号,走一遍完整的信号处理链,STM32 检查输出结果是否在预期范围内。如果自检不通过,就通过 LED 或者串口报错。这个功能在调试阶段能帮你快速判断是硬件问题还是软件问题,在外场部署时也能给操作人员一个明确的系统状态指示。我后来把这个自检模式做成了一个独立的按键触发功能,按一下就能跑一遍,非常实用。