返回文章列表

利利普杯C题硬件

电赛超声相控阵系统:FPGA 引脚分配与外设连接设计

在做超声阵列定向音频传输系统时,很多人一开始会把注意力放在波束形成算法、40 kHz PWM、上位机控制界面或者声压测试结果上。但真正调试硬件时,最容易出问题的往往是一个看起来很“基础”的部分:引脚分配

本项目使用 Mizar Z7 作为主控板,核心是 Zynq-7000 SoC。FPGA 侧负责产生 25 路相位和幅度可控的 40 kHz PWM,再通过 74HC595D 串并转换扩展输出,最后由 UCC27624DR 驱动 5×5 超声换能器阵列。系统还预留了接收板 ADC 接口,用于麦克风采样和后续闭环控制。

这篇文章主要记录本项目中 FPGA 引脚分配、74HC595 扩展、阵列通道映射、ADC 接口和调试经验,适合作为同类电赛或 FPGA 外设扩展项目的参考。


1. 为什么不能直接用 25 个 FPGA IO?

5×5 超声阵列共有 25 个换能器,如果每个阵元都使用 FPGA 一个 IO 直接输出 PWM,逻辑上最简单:

FPGA_CH1  -> 阵元 1
FPGA_CH2  -> 阵元 2
...
FPGA_CH25 -> 阵元 25

但这样做有几个问题:

  1. 占用 FPGA IO 数量较多;
  2. 连接线数量多,布线和焊接复杂;
  3. 阵列板与主控板之间接口臃肿;
  4. 后续增加通道数量时扩展性差。

因此本项目采用 74HC595D 串并转换扩展 IO 的方式。FPGA 只需要输出三根控制线:

DATA   串行数据
CLK    移位时钟
LATCH  锁存时钟

通过 4 片 74HC595D 级联,可以扩展出 32 路并行输出,其中使用前 25 路控制 5×5 阵列,其余通道预留。

这种结构虽然增加了移位逻辑,但大大减少了 FPGA 与驱动板之间的连接线数量,非常适合本项目这种“多路同步、低速更新、高速载波输出”的场景。


2. 系统引脚整体结构

整个系统的引脚可以分为四类:

┌────────────────────────────┐
│        Mizar Z7 FPGA        │
│        Zynq PL 逻辑         │
└─────────────┬──────────────┘

              ├── 74HC595 控制接口
              │     ├── DATA
              │     ├── CLK
              │     └── LATCH

              ├── ADC 接收接口
              │     ├── ADC_CS
              │     ├── ADC_CLK
              │     └── ADC_DATA

              ├── 调试接口
              │     ├── carrier/debug
              │     └── LED/status

              └── UART/BLE 通信
                    ├── RX
                    └── TX

其中最关键的是驱动板接口。实际系统中,FPGA 并不是直接输出 25 路 PWM 到换能器,而是先在 FPGA 内部生成 25 路通道状态,然后按顺序打包为 32 bit,并通过 74HC595D 移位输出。


3. Mizar Z7 GPIO 选择原则

Mizar Z7 提供两组 2×20 的 40Pin GPIO 扩展口,适合外接自制驱动板。使用时需要注意下面几点。

3.1 优先选择 3.3V IO

74HC595D、ADC 接口和常见串口模块都可以工作在 3.3V 逻辑电平,因此 FPGA 侧 IO 尽量选择 3.3V 电平的 GPIO。这样可以减少电平转换芯片,调试也更方便。

如果使用可调电压 Bank,一定要确认当前 Bank 电压是否为 3.3V。否则可能出现以下问题:

  • FPGA 输出高电平不足,74HC595 识别不稳定;
  • 外设输出电平过高,反向损伤 FPGA IO;
  • 仿真正确但实物无法通信。

3.2 控制线尽量放在同一扩展口

驱动板的 DATA、CLK、LATCH 三根线建议放在同一个 GPIO 扩展口附近,原因是:

  • 走线更短;
  • 接线不容易出错;
  • 地线参考更稳定;
  • 示波器调试时更方便。

本项目中 74HC595 的控制线可以按如下逻辑规划:

信号名方向功能建议说明
DATAFPGA → 74HC595串行数据输入每个时钟写入 1 bit
CLKFPGA → 74HC595移位时钟上升沿移位
LATCHFPGA → 74HC595锁存时钟一帧 32 bit 移完后更新输出
GND公共地地参考必须与驱动板共地
FPGA_3V3电源/参考逻辑电平供电或参考视实际驱动板供电方式确定

4. 74HC595D 级联输出设计

74HC595 是一个 8 位串入并出移位寄存器。单片只能输出 8 路,4 片级联后可以输出 32 路。

本项目使用 4 片 74HC595D:

FPGA DATA  ──> 74HC595 #1 ──> 74HC595 #2 ──> 74HC595 #3 ──> 74HC595 #4
FPGA CLK   ──> 四片共用 SRCLK
FPGA LATCH ──> 四片共用 RCLK

输出关系可以理解为:

Q0  ~ Q7   -> 第 1 片 74HC595
Q8  ~ Q15  -> 第 2 片 74HC595
Q16 ~ Q23  -> 第 3 片 74HC595
Q24 ~ Q31  -> 第 4 片 74HC595

其中 Q0 到 Q24 用于 25 个换能器通道,Q25 到 Q31 预留。


5. FPGA 到 74HC595 的时序

74HC595 的控制时序非常简单,但必须保证顺序正确。

一次完整更新流程如下:

1. LATCH 拉低
2. 按顺序输出 32 bit 数据
3. 每输出 1 bit,CLK 产生一个上升沿
4. 32 bit 全部移入后,LATCH 产生一个上升沿
5. 74HC595 并行输出同步更新

伪代码如下:

for (i = 31; i >= 0; i = i - 1) begin
    DATA <= shift_data[i];
    CLK  <= 1'b1;
    CLK  <= 1'b0;
end

LATCH <= 1'b1;
LATCH <= 1'b0;

实际 FPGA 代码中需要使用状态机,而不是这种软件式写法。推荐状态机分为:

IDLE      空闲
LOAD      装载 32 bit 通道状态
SHIFT     移位输出
LATCH     锁存更新
DONE      完成

这样可以保证 DATA、CLK、LATCH 的时序稳定。


6. 为什么不能随便改变移位顺序?

74HC595 的移位顺序会直接影响通道编号。

例如 FPGA 内部生成的通道状态为:

channel_bits[0]  -> CH1
channel_bits[1]  -> CH2
...
channel_bits[24] -> CH25

但经过 74HC595 级联后,最先移入的数据可能最终出现在最后一片的 QH,也可能出现在第一片的 QA,具体取决于代码中是先发高位还是先发低位。

因此,一定要做一张明确的映射表:

FPGA 逻辑通道74HC595 输出位物理阵元
CH1Q0第 1 行第 1 列
CH2Q1第 1 行第 2 列
CH3Q2第 1 行第 3 列
CH25Q24第 5 行第 5 列

调试时可以只点亮或输出某一路 PWM,用示波器或简单 LED 检查实际通道是否对应正确。


7. 5×5 阵列的蛇形映射

在 PCB 布线时,为了减少走线交叉,阵列通道往往不会严格按“从左到右、从上到下”的自然顺序排列。本项目采用蛇形映射。

逻辑上可以理解为:

第 1 行:CH01  CH02  CH03  CH04  CH05
第 2 行:CH10  CH09  CH08  CH07  CH06
第 3 行:CH11  CH12  CH13  CH14  CH15
第 4 行:CH20  CH19  CH18  CH17  CH16
第 5 行:CH21  CH22  CH23  CH24  CH25

这样布线更方便,但软件计算波束时必须使用真实物理坐标,而不能直接按照通道号推算位置。

推荐在上位机或 FPGA 中建立一个映射表:

const channelMap = [
  [1,  2,  3,  4,  5],
  [10, 9,  8,  7,  6],
  [11, 12, 13, 14, 15],
  [20, 19, 18, 17, 16],
  [21, 22, 23, 24, 25]
];

在计算相位时,使用阵元的几何位置;在下发数据时,再根据映射表转换为实际通道号。


8. 通道相位和幅度如何落到引脚上?

上位机计算得到的是每个阵元的相位字节和幅度字节:

phase[25]      0 ~ 255
amplitude[25]  0 ~ 255

FPGA 接收后并不会直接把这些字节输出到引脚,而是先生成每一路 PWM:

phase[i]      -> PWM 相位偏移
amplitude[i]  -> PWM 高电平宽度

然后在每个 40 kHz 载波周期内,FPGA 判断当前计数器是否落在某一路 PWM 的高电平窗口内。如果是,该通道输出 1;否则输出 0。

25 路通道状态会被打包:

shift_data[0]  = pwm_ch1;
shift_data[1]  = pwm_ch2;
...
shift_data[24] = pwm_ch25;
shift_data[31:25] = 7'b0;

然后通过 DATA、CLK、LATCH 更新到 74HC595D。

因此,真正连接到外部的 FPGA 引脚只有几根,但 FPGA 内部仍然实现了 25 路独立 PWM。


9. UCC27624DR 直驱换能器的连接说明

驱动板初版在 UCC27624DR 后面预留了 MOSFET 开关级,但在实测过程中发现,对于本项目的 5×5 阵列和测试功率要求,直接使用 UCC27624DR 的 OUTA/OUTB 驱动超声换能器即可满足需求。

最终实物中拆除或旁路了 MOSFET 级,连接关系简化为:

74HC595 输出  -> UCC27624DR 输入
UCC27624 OUT -> 超声换能器通道

这样做有几个优点:

  1. 少一级开关器件,延迟更小;
  2. 减少 MOSFET 栅极振铃和寄生影响;
  3. 焊接和排查更简单;
  4. 与实际测试样机一致。

当然,这种做法是否适合其他项目,需要根据换能器数量、驱动电压、电流能力和芯片散热情况重新评估。


10. ADC 接收板引脚设计

除了发射端,本项目还预留了接收板,用于麦克风采样和自解调音频验证。接收板主要包括:

麦克风输入
LMV358 放大
半电源偏置
串行 ADC
FPGA 接口

ADC 接口可以抽象为三线 SPI 类接口:

信号名方向功能
ADC_CSFPGA → ADC片选
ADC_CLKFPGA → ADC采样/移位时钟
ADC_DATAADC → FPGA串行采样数据

部分原理图中还出现 FPGA_CS、FPGA_CLK、FPGA_DATA 等命名,这是为了在接收板和 FPGA 接口之间做信号对应。实际写代码时建议统一命名为:

output adc_cs;
output adc_clk;
input  adc_data;

这样更符合 FPGA 顶层模块的方向定义。


11. 引脚约束文件 XDC 写法示例

实际工程中,需要在 XDC 文件中把 Verilog 顶层端口绑定到 FPGA 物理管脚。

下面给出一个模板,具体 PACKAGE_PIN 需要根据实际接线修改:

## 74HC595 control
set_property PACKAGE_PIN <PIN_DATA>  [get_ports shift_data]
set_property IOSTANDARD LVCMOS33     [get_ports shift_data]

set_property PACKAGE_PIN <PIN_CLK>   [get_ports shift_clk]
set_property IOSTANDARD LVCMOS33     [get_ports shift_clk]

set_property PACKAGE_PIN <PIN_LATCH> [get_ports shift_latch]
set_property IOSTANDARD LVCMOS33     [get_ports shift_latch]

## ADC interface
set_property PACKAGE_PIN <PIN_ADC_CS>   [get_ports adc_cs]
set_property IOSTANDARD LVCMOS33        [get_ports adc_cs]

set_property PACKAGE_PIN <PIN_ADC_CLK>  [get_ports adc_clk]
set_property IOSTANDARD LVCMOS33        [get_ports adc_clk]

set_property PACKAGE_PIN <PIN_ADC_DATA> [get_ports adc_data]
set_property IOSTANDARD LVCMOS33        [get_ports adc_data]

## Debug output
set_property PACKAGE_PIN <PIN_CARRIER> [get_ports carrier_debug]
set_property IOSTANDARD LVCMOS33       [get_ports carrier_debug]

如果使用 Mizar Z7 的 JP1/JP2 扩展口,需要根据开发板手册找到对应 GPIO 的 FPGA Pin No.,再写入 XDC。


12. 顶层 Verilog 端口建议

为了方便维护,顶层模块不要直接写一堆无意义的端口名,而应该按功能命名:

module top_ultrasonic_array (
    input  wire clk_50m,
    input  wire rst_n,

    // 74HC595 interface
    output wire shift_data,
    output wire shift_clk,
    output wire shift_latch,

    // ADC interface
    output wire adc_cs,
    output wire adc_clk,
    input  wire adc_data,

    // UART interface
    input  wire uart_rx,
    output wire uart_tx,

    // Debug
    output wire carrier_debug
);

这样做的好处是:

  1. 看顶层端口就知道系统结构;
  2. XDC 约束更清晰;
  3. 后续换引脚不影响内部逻辑;
  4. 写报告和答辩时更容易解释。

13. UART/BLE 通信引脚

上位机通过串口或 BLE 透传向 FPGA 发送任务参数,包括:

任务编号
25 路幅度
25 路相位
应用命令
状态查询

从 FPGA 角度看,BLE 透传模块和 USB-UART 本质上都是串口设备,只需要 RX/TX 两根信号:

信号名方向功能
UART_RX上位机/BLE → FPGA接收控制帧
UART_TXFPGA → 上位机/BLE返回状态或应答

调试时建议先使用 USB-UART,因为它稳定、方便抓取日志;BLE 透传可以在基础通信稳定后再接入。


14. 调试引脚一定要预留

很多时候,FPGA 内部逻辑没有问题,但外部波形并不符合预期。如果没有调试引脚,就只能靠猜。

本项目建议至少预留以下调试信号:

调试信号用途
carrier_debug观察 40 kHz 载波频率是否正确
shift_clk_debug观察 74HC595 移位时钟
latch_debug观察锁存时刻
selected_ch_debug观察某一路 PWM
uart_rx_flag判断是否收到上位机数据

其中最重要的是 carrier_debug。只要示波器能看到稳定的 40 kHz,就说明时钟分频、载波计数器和基础 PWM 逻辑基本正常。


15. 常见问题与排查方法

问题 1:74HC595 没有输出

优先检查:

DATA 是否有变化
CLK 是否有跳变
LATCH 是否在 32 bit 后产生上升沿
OE 是否使能
SRCLR 是否保持非清零状态
74HC595 是否有 3.3V 供电
FPGA 和驱动板是否共地

问题 2:通道顺序错乱

常见原因是移位顺序和实际通道顺序不一致。

解决方法:

  1. 每次只打开一个通道;
  2. 用示波器逐个测量 CH 输出;
  3. 记录逻辑 CH 与物理阵元位置;
  4. 在上位机或 FPGA 中建立映射表。

问题 3:波束方向和设定方向相反

常见原因包括:

X/Y 坐标定义反了
阵列正反面方向理解错了
蛇形映射没有处理
相位补偿符号写反

建议先测试:

全零相位:中心方向声压最大
θx = +12°:声束向一侧偏转
θx = -12°:声束向相反方向偏转

如果正负方向相反,只需要在相位计算中调整符号,不一定是硬件问题。

问题 4:ADC 数据异常

检查顺序:

麦克风偏置是否正常
LMV358 输出是否在 ADC 输入范围内
ADC_CS 时序是否正确
ADC_CLK 频率是否过高
ADC_DATA 是否需要电平转换
FPGA 输入是否设置为 LVCMOS33

16. 本项目的引脚设计经验总结

这个超声相控阵系统的引脚设计可以总结为一句话:

用尽可能少的 FPGA 外部引脚,换取尽可能多的可控阵元通道。

具体做法是:

  1. 使用 Mizar Z7 的 PL GPIO 作为外设接口;
  2. 用 3 根线控制 4 片 74HC595D;
  3. 通过 32 路并行输出控制 25 路阵元;
  4. 用 UCC27624DR OUT 直接驱动换能器;
  5. 用蛇形映射解决 PCB 布线与几何坐标不一致的问题;
  6. ADC、UART、调试信号分开规划,避免后期混乱;
  7. XDC 文件中所有信号按功能命名,方便维护。

对于电赛项目来说,引脚设计不是简单的“能接上就行”,而是直接影响系统调试效率、波束控制精度和后期扩展能力。尤其是多通道阵列类项目,一定要在画板前先确定好:

逻辑通道编号
物理阵元位置
74HC595 输出顺序
FPGA 顶层端口
XDC 管脚约束
上位机映射表

只要这几张表保持一致,后面的 PWM、相位控制、聚焦算法和上位机下发都会顺畅很多。