尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Zynq7020串口实验:一套Verilog收发器覆盖RS232/RS485/RS422

发布时间:2026/9/14 6:00:10

资讯中心
01
ARTICLE

Zynq7020串口实验:一套Verilog收发器覆盖RS232/RS485/RS422

Zynq7020串口实验:一套Verilog收发器覆盖RS232/RS485/RS422
简介基于Xilinx Zynq7020 FPGA的串口通信实验资源围绕RS232、RS485、RS422三种常用工业串口协议展开面向正在学习FPGA UART通信、需要工程化验证的开发者旨在解决多协议串口调试与硬件验证落地问题。压缩包内含341个文件大小约18.87MB覆盖Vivado完整工程xpr/xci/bit、Verilog与VHDL源码、XDC约束、Tcl/Bat/Sh脚本、综合实现报告及开发板硬件原理图文件类型齐全便于从代码阅读到上板调试的完整链路学习。资源中RS422顶层模块清晰展示了例化两个uart_test子模块实现双通道收发的方法对理解多路串口扩展与模块化设计很有帮助同时附带的bit文件、时序约束和原理图PDF可直接配合Zynq7020开发板进行实验验证。整体目录结构清晰便于快速定位工程源码、脚本与硬件文档。目前已有129人学习下载内容偏工程实战适合希望快速上手Vivado串口通信项目并对照硬件原理图做二次开发的读者。1. 一套 Verilog 收发器覆盖三种串口总线Zynq7020 串口实验的正确打开方式串口是 FPGA 开发里最容易上手、也最容易被低估的外设。你翻开任意一块 Zynq7020 开发板的原理图几乎都能找到 1 到 2 个 RS232 电平的 DB9 接口旁边的排针或跳线帽则同时引出了 RS485 和 RS422 的收发引脚。很多初学者在第一个 LED 闪烁实验之后第二个目标就是打通 UART 回环——但真正面对“RS232、RS485、RS422 同时出现”的工程时往往会困惑这三种接口到底需要在 FPGA 里写三套逻辑还是同一套 UART 核就能全部搞定答案是后者。从 FPGA 侧看RS232、RS485、RS422 共享完全相同的 UART 帧格式和波特率机制区别只在于物理层电平标准和收发方向控制。本文要做的就是拿到标题里那套“Vivado 工程 Verilog 源码 开发板原理图”之后顺着工程把三者的关系、UART 收发状态机、方向控制时序、上板调试方法一次讲透。你不需要额外买协议分析仪一块 Zynq7020 开发板加一个 USB 转串口模块就能把三种总线全部验证完。2. 三种串口协议的电气边界RS232、RS422、RS485 在 FPGA 侧的真正区别很多人把 RS232、RS422、RS485 当成三种不同的“协议”其实它们是三种不同的电气标准。帧格式都是起始位 8 位数据 停止位通常无校验波特率也都是 9600、115200 这类常见值。FPGA 里跑的那套 UART 逻辑对三种总线完全通用。2.1 从原理图看懂电平转换芯片的角色打开标题压缩包里的硬件原理图你会看到 Zynq7020 的 PL 端引脚比如 BANK500 或 BANK501 上的引脚并没有直接连接到 DB9 或接线端子。中间一定隔着一颗电平转换芯片常见型号有总线类型常见收发器芯片电平标准备注RS232MAX3232 / SP3232单端±5V ~ ±15V必须用电荷泵产生负压RS485MAX3485 / SP3485差分A/B 两线半双工需要方向控制RS422MAX3490 / SP3490差分A/B/Y/Z 四线全双工收发独立原理图里 RS232 部分的电容通常是 0.1uF 或 1uF是电荷泵的储能元件没有这些电容MAX3232 无法产生负压RS232 电平根本出不来。这是一条很实用的排查经验RS232 完全不工作、量不到负压时先检查电荷泵电容是否焊反或虚焊。RS485 和 RS422 的收发器则是把 FPGA 侧的 3.3V TTL 单端信号转换成差分信号。注意 RS485 收发器如 MAX3485的 DE 和 RE 引脚经常在原理图上并在一起由 FPGA 的一个 GPIO 控制——这就是半双工方向控制的硬件基础。RS422 的收发器则没有 DE/RE 概念因为它的发送和接收是独立的两对差分线不需要切换方向。2.2 FPGA 引脚约束上的几个关键点拿到原理图后先别急着写代码把引脚约束梳理清楚。常见做法是按照信号流向分组# RS232 部分 set_property PACKAGE_PIN E6 [get_ports uart232_tx] set_property PACKAGE_PIN E7 [get_ports uart232_rx] set_property IOSTANDARD LVCMOS33 [get_ports uart232_tx] set_property IOSTANDARD LVCMOS33 [get_ports uart232_rx] # RS485 部分包含方向控制 set_property PACKAGE_PIN F6 [get_ports uart485_tx] set_property PACKAGE_PIN F7 [get_ports uart485_rx] set_property PACKAGE_PIN G6 [get_ports uart485_dir] set_property IOSTANDARD LVCMOS33 [get_ports uart485_*] # RS422 部分 set_property PACKAGE_PIN H6 [get_ports uart422_tx] set_property PACKAGE_PIN H7 [get_ports uart422_rx] set_property IOSTANDARD LVCMOS33 [get_ports uart422_*]这段约束里RS485 比 RS232 多了一个uart485_dir信号它控制的是收发器的 DE/RE 引脚。实际工程里引脚号以你自己的原理图为准但结构一定是这个结构每一个与收发器相连的 FPGA 引脚都要写PACKAGE_PIN和IOSTANDARD缺一个 Vivado 综合后就会报 IO 约束不完整。有一个细节值得注意RS485 的rx引脚在 FPGA 侧经常标记为DIReceiver Data In而不是rx在原理图上是收发器的 RO 引脚。看到RO、DI、DE、RE这几个引脚名时要能对应上。2.3 逻辑层视角FPGA 只认识 TTL 电平把三种收发器的数据手册摊开对比会发现 FPGA 侧的接口都是一个发送引脚、一个接收引脚、加上可能的方向控制引脚。MAX3232 和 MAX3485、MAX3490 在 FPGA 侧的接口差异只有 RS485 多了一个dir控制。这意味着你写的 Verilog UART 收发模块完全可以被三个顶层实例各调用一次不需要为每种总线写不同的逻辑。这也是为什么标题里的工程把“RS232、RS485、RS422”放在同一个实验里——工作量就是一份 UART 收发器加上 RS485 的少量方向控制逻辑。搞清楚这个边界后面看源码就不会被三种总线绕晕。3. Vivado 工程里的 UART 最小可运行代码波特率生成与收发状态机这一章给出一个可以在 Zynq7020 上直接落地的 UART 模块。以 115200 波特率、8 位数据位、1 位停止位为例完整的工程包含顶层例化、波特率生成、发送状态机、接收状态机四个部分。3.1 波特率计数的计算方式Zynq7020 的 PL 端时钟通常来自 PS 端的FCLK_CLK0默认 100MHz。波特率计数器的核心公式是计数上限 时钟频率 / 波特率 / 分频系数如果采样方式是每个位周期计数一次那么 100MHz / 115200 ≈ 868。这 868 个时钟周期就是一位的时长。但接收方向通常会做 16 倍过采样原因是需要在位的中间位置采样以避开边沿抖动。16 倍过采样时计数上限就是 54868 / 16 ≈ 54.25取整后实际波特率为 100MHz / 54 / 16 ≈ 115741误差约 0.41%在 UART 的 ±2% 容限内完全可用。3.2 发送状态机的 Verilog 实现module uart_tx #( parameter CLK_FREQ 100_000_000, parameter BAUD_RATE 115200 )( input wire clk, input wire rst_n, input wire [7:0] tx_data, input wire tx_start, output reg tx_line, output reg tx_busy ); localparam BAUD_CNT CLK_FREQ / BAUD_RATE; localparam IDLE 3d0; localparam START 3d1; localparam DATA 3d2; localparam STOP 3d3; reg [2:0] state; reg [8:0] baud_cnt; reg [2:0] bit_cnt; reg [7:0] data_buf; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; tx_line 1b1; tx_busy 1b0; baud_cnt 0; bit_cnt 0; data_buf 0; end else begin case (state) IDLE: begin tx_line 1b1; if (tx_start !tx_busy) begin data_buf tx_data; tx_busy 1b1; state START; end end START: begin tx_line 1b0; if (baud_cnt BAUD_CNT - 1) begin baud_cnt 0; state DATA; bit_cnt 0; end else begin baud_cnt baud_cnt 1; end end DATA: begin tx_line data_buf[bit_cnt]; if (baud_cnt BAUD_CNT - 1) begin baud_cnt 0; if (bit_cnt 3d7) begin state STOP; end else begin bit_cnt bit_cnt 1; end end else begin baud_cnt baud_cnt 1; end end STOP: begin tx_line 1b1; if (baud_cnt BAUD_CNT - 1) begin baud_cnt 0; state IDLE; tx_busy 1b0; end else begin baud_cnt baud_cnt 1; end end endcase end end endmodule这段代码的状态划分是标准做法IDLE 时发送线保持高电平收到tx_start脉冲后进入 START 位输出低电平延时一个位周期后开始输出 8 个数据位最后输出停止位并回到 IDLE。tx_busy信号用于外部逻辑判断“上一帧是否发完”避免在发送过程中写入新数据导致帧错乱。参数说明CLK_FREQ必须与实际时钟匹配如果 Zynq7020 的 FCLK_CLK0 改成了 50MHz 而这里没改波特率会直接翻倍错乱。tx_data在tx_start拉高的同拍被锁存所以外部逻辑只需要保证tx_start是单周期脉冲即可不需要一直维持tx_data不变。3.3 接收状态机的边沿采样策略接收方向的难点在于异步采样——你不知道数据什么时候到达必须在rx_line上检测到下降沿时启动接收。以下给出接收模块的关键部分module uart_rx #( parameter CLK_FREQ 100_000_000, parameter BAUD_RATE 115200 )( input wire clk, input wire rst_n, input wire rx_line, output reg [7:0] rx_data, output reg rx_done ); localparam BAUD_CNT CLK_FREQ / BAUD_RATE; localparam SAMPLE_CNT CLK_FREQ / BAUD_RATE / 16; reg [3:0] rx_shift; reg rx_meta, rx_sync; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_meta 1b1; rx_sync 1b1; end else begin rx_meta rx_line; rx_sync rx_meta; end end wire rx_negedge rx_sync !rx_meta;这里用到两级同步器消除亚稳态。Zynq7020 的引脚电平是异步时钟域信号如果直接拿原始rx_line做下降沿检测在跨时钟域采样时可能采到中间电平导致误判。两级触发器把信号打拍到本地时钟域后下降沿检出的结果是稳定的。接收数据的具体采样点是每个位的 1/2 处。启动后跳过 8 个采样时钟再开始采样数据位这样采到的是位中心值抗干扰能力最强。注意这里不能用divide by 16再采因为起始位下降沿本身有抖动正确的做法是用一个采样计数器在每个位周期的中央触发一次取值。3.4 顶层例化与 Testbench 验证顶层模块只需要三个实例一个uart_tx、一个uart_rx、再加上一个简单的回环逻辑接收到的数据直接送发送端。回环是验证 UART 工程最有效的手段——电脑串口发一个字节开发板返回同一个字节。如果回环数据一致说明收、发两条链路都通了。// 顶层例化片段 wire [7:0] loop_data; wire loop_done; uart_rx u_rx ( .clk (clk_100m), .rst_n (rst_n), .rx_line (uart232_rx), .rx_data (loop_data), .rx_done (loop_done) ); uart_tx u_tx ( .clk (clk_100m), .rst_n (rst_n), .tx_data (loop_data), .tx_start (loop_done), .tx_line (uart232_tx), .tx_busy () );这段例化中tx_start直接接rx_done意味着每收到一个字节就立刻回发一个字节。这是最粗暴但最有效的串口调试方式。Vivado 自带的仿真工具里Testbench 需要造一个模拟的串口发送端用#8680000这样的延时模拟位周期从 0 开始逐位把tx_line拉低再拉高驱动 DUT 的接收端就可以观察波形。4. RS485 方向控制与 RS422 全双工同一收发模块的三种接线拓扑三种总线的 UART 逻辑既然一样差别就全部集中在物理层的接法和控制逻辑上。这一章给出实际工程里 RS485 方向切换的标准做法和 RS422 的接线注意点。4.1 RS485 半双工的方向切换时序RS485 总线上所有节点共享一对差分线同一时刻只能有一个节点发送。FPGA 侧通过dir信号控制 MAX3485 的 DE/RE 引脚// RS485 方向控制 reg dir_reg; always (posedge clk or negedge rst_n) begin if (!rst_n) dir_reg 1b0; // 默认接收状态 else if (tx_start) dir_reg 1b1; // 开始发送占用总线 else if (tx_busy_done) dir_reg 1b0; // 发送结束释放总线 endtx_start是发送启动脉冲tx_busy_done是发送状态机结束脉冲。方向切换的关键是 DE 拉高后必须等发送状态机的第一个位真正输出到总线才能开始计数否则起始位会被吞掉。MAX3485 的 DE 到输出有效延时约 50ns对于 115200 波特率来说一个位周期约 8.68us这个延时可以忽略但方向切换的完成必须晚于发送状态机的最后一个停止位结束。一个非常容易踩的坑是方向控制的释放时机。如果dir_reg在发送状态机进入 IDLE 的同一拍拉低那么最后一个停止位可能还没完整地送出电平。常见做法是在tx_busy下降沿后再延时一到两个时钟周期释放dir_reg。4.2 RS422 全双工不需要方向控制的简化RS422 收发器的发送输出Y/Z和接收输入A/B是两对独立的差分线所以uart422_tx和uart422_rx各连各的线不存在方向切换问题。在顶层例化时RS422 部分直接把 uart_tx 的输出接到 MAX3490 的 DI 引脚MAX3490 的 RO 引脚接 uart_rx 的输入代码和 RS232 几乎一样。RS422 的唯一注意点是终端电阻长距离传输时接收端需要在 A-B 之间跨接 120 欧姆终端电阻开发板上一般通过跳线帽启用。如果距离短、线缆在 1 米以内不接终端电阻也能正常通信。4.3 三总线复用的共享思路如果你的开发板引脚紧张可以只写一套 UART 收发器通过一个 2 位选择信号在三个物理引脚组之间切换wire uart_tx_out, uart_rx_in; // 三个总线的回环共享同一套收发器 assign uart232_tx_out uart_tx_out; assign uart485_tx_out uart_tx_out; assign uart422_tx_out uart_tx_out; // 接收选择 always (*) begin case (bus_sel) 2d0: uart_rx_in uart232_rx_in; 2d1: uart_rx_in uart485_rx_in; 2d2: uart_rx_in uart422_rx_in; default: uart_rx_in uart232_rx_in; endcase end这样做的代价是三个总线不能同时通信但实验验证阶段完全够用。标题工程里的代码很可能是三套独立例化的方式方便同时跑三路回环测试具体看源码里的顶层文件怎么组织。5. 串口不通时先查这些Vivado 综合约束、上板调试与常见误配置Vivado 工程从综合到生成比特流再到上板串口调试每一层都有坑。这一章按排查顺序给出最实用的检查清单。5.1 综合前必须检查的三个约束点首先是时钟约束。Zynq7020 的 FCLK_CLK0 来自 PS 端配置Vivado 识别它需要 PS 的 XML 文件正确导入。在 block design 里勾选FCLK_CLK0并设置频率为 100MHz 后综合工具会自动识别这个时钟不需要手动写create_clock。如果你把引脚上的输入时钟直接用于逻辑比如外接晶振就必须在 XDC 里手动写create_clock -period 10.000 -name sys_clk [get_ports clk_in]这里-period 10.000对应 100MHz。忘记写这一行会导致时序分析结果不可信虽然功能上不一定出错但遇到时序收敛问题时很容易误判。其次是引脚约束。标题工程里如果提供了开发板原理图对应的 XDC 会以表格形式列出每个引脚的物理封装号。注意PACKAGE_PIN的大小写不能错Vivado 的大小写敏感e6和E6是不同的引脚。最后是差分信号约束。RS485 和 RS422 的差分线在 FPGA 侧是单端信号不需要写DIFF_TERM。但 MAX3485 的 A/B 引脚如果接反数据会完全错乱这种问题在原理图阶段就要仔细核对。5.2 上板后串口乱码的排查顺序串口乱码是所有串口实验中最常见的现象可能的原因按概率排列如下波特率不匹配。先在串口助手里确认选的是 115200再确认 Vivado 工程里的CLK_FREQ参数是 100 还是 50。很多开发板默认的时钟是 50MHz代码里参数没改实际波特率就只有 57600收到的肯定乱码。电平标准不匹配。RS232 的电平转换芯片如果用的是 MAX3232供电必须是 3.3V用 5V 供电会让输出电平超过接收端的输入范围。同理RS485 的 A/B 线如果和另一端的 GND 不共地共模电压漂移也会导致乱码。差分线接反。RS485 和 RS422 的 A、B 接反会让数据完全翻转表现为乱码或者完全收不到。排查方法是交换一对差分线再试。电荷泵电容问题。RS232 的负压没建立起来时发送端的输出波形会异常接收端收到的都是 0xFF 或者无法同步。用示波器量一下 DB9 的 3 脚TX是否在空闲状态下为负电平这是最直接的判断依据。5.3 ILA 抓内部的 rx_done 信号当串口助手完全收不到数据时最有效的调试手段是插入 Vivado ILAIntegrated Logic Analyzer核抓内部信号。在 block design 里右键添加 ILA把uart_rx.rx_line、rx_done、rx_data连到探针上上板后通过 hw_server 连接。ILA 有一个非常实用的功能触发条件是rx_done 1b1。如果触发不了说明接收状态机根本没走到完成状态问题出在硬件链路如果能触发且rx_data值正确说明接收侧没问题故障在发送侧或者电脑串口设置。这个二分法能把排查时间缩短一半。如果不想加 ILA 核也可以用 LiteX 或直接写一个简单的状态机把rx_done的情况映射到一个 LED 上闪烁一次作为最简单的调试手段。在纯 Verilog 工程里加一个 LED 指示比开 ILA 更快。6. 多字节帧协议与 FIFO 缓冲把 UART 用到量产固件里的收尾技巧实验回环能跑通只是第一步。真正的工程应用里UART 不会只收发单字节而是收发成帧的数据报文。标题里的工程如果看懂了 UART 状态机再往前一步就是这一章的内容。6.1 定义一个可扩展的帧格式常见的自定义帧格式是“帧头 长度 数据 CRC”| 0xAA | 0x55 | LEN | DATA[0...N-1] | CRC8 |帧头0xAA 0x55用于找帧边界LEN表示数据长度CRC8 用于校验。接收侧的行为是检测到0xAA 0x55后进入接收状态连续读LEN字节然后读 1 字节 CRC 并校验。注意 LEN 不能超过缓冲区的最大值否则会产生越界这是串口协议实现里最常见的漏洞之一。CRC8 的 Verilog 实现用查表法最快也可以用多项式除法逐位计算。对于 Zynq7020 这种资源充裕的芯片查表法更省逻辑且逻辑清晰always (*) begin case (crc_state) 8h00: crc_next 8h00; // 实际工程填 256 项查找表 endcase end6.2 用异步 FIFO 隔离跨时钟域Zynq7020 的常用场景是 PS 和 PL 各跑各的时钟PS 把数据通过 AXI 总线写到 PL 的 FIFOPL 的 UART 从 FIFO 取数发送。这时 FIFO 必须用 Xilinx 的axi_uartliteIP 或者自己例化一个异步 FIFO。Vivado 的 FIFO Generator IP 支持读写时钟不同位宽不同是标准做法。自己写异步 FIFO 时核心是格雷码指针同步。两个时钟域的读写指针不能直接比较要把写指针同步到读时钟域把读指针同步到写时钟域满空标志的生成是关键。Xilinx 官方文档 PG057 描述了基础实现工程上直接用 IP 更省事。6.3 发完最后一帧再释放 RS485 总线一个容易忽略的细节RS485 多机通信时“主动发送方发完最后字节后释放总线”的时序决定了下一个节点能否立刻接管总线。很多工程在发送状态机进入 IDLE 后立刻拉低 DE但远程节点的收发器切换到接收模式也需要时间通常几百纳秒。在 DE 拉低之前插入一个 3~5us 的延时能显著降低总线冲突概率。实现方法很简单方向控制模块里加一个延时计数器当发送状态机的tx_busy结束信号到达后启动延时计数计满后再拉低dir_reg。这个延时的长度不需要太精确只要能覆盖远程节点的切换时间即可。最后还有一个容易被忽略的验证手段。做完三种总线的回环实验后把波特率从 9600 一路调到 921600观察每种总线的最大稳定速率。RS232 在长线缆上超过 115200 就会开始出错RS422 在 10 米内跑到 921600 通常没问题RS485 的表现则取决于终端电阻匹配——连线长度从 1 米换到 50 米这个实验结果会和理论曲线吻合得很好。这些数据记录下来就是你后续项目选型时最可靠的参考。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。