FPGA通信——实现串口通信(Uart)

FPGA通信——实现串口通信(Uart)

一、串口通信介绍

1.1、核心概念

并行通信 (Parallel):像高速公路,8车道同时跑8辆车。速度快,但占用引脚多,且在长距离传输时容易出现“时钟偏差(Skew)”导致数据错位。

串行通信 (Serial):像单行道,车必须一辆接一辆地排队走。引脚少,成本低,且现代高速串行技术(如PCIE, SATA)通过差分信号解决了速度问题。

我们常说的“串口”通常特指 UART (Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)

1.2、逻辑层面

UART 是一种异步通信协议。

  • 异步 (Asynchronous):发送方和接收方之间没有公共的时钟线(不像 SPI 或 I2C 有 CLK 线)。
  • 约定:双方必须提前约定好相同的波特率 (Baud Rate),否则就像两个人语速不同,无法交流。

数据帧格式 (Frame Format)

FPGA 的状态机就是根据这个时序图来写的:

  1. 空闲位 (Idle):默认高电平(1)。
  2. 起始位 (Start Bit):拉低 1 个周期。告诉接收方:“注意,我要开始说话了!”(这是我们在 Verilog 里检测下降沿的原因)。
  3. 数据位 (Data Bits):通常是 8 位(1 Byte),LSB (低位) 先发
  4. 校验位 (Parity):可选(奇/偶/无)。用于简单的检错,但在工业应用中常被 CRC 取代。
  5. 停止位 (Stop Bit):拉高 1 或 2 个周期。表示一帧结束,并为空闲状态做准备。

1.3、物理层

        FPGA 产生的信号只是逻辑上的 0 和 1(TTL电平),要传输出去,需要穿上不同的“外衣”(电气标准),也就是PCB版上的芯片。

标准传输方式电平特征典型距离拓扑典型应用
TTL单端0~5V<30cm点对点芯片间通信
RS232单端±5~±12V<15m点对点老式PC,工控机
RS485差分A-B电压差<1200m多点/总线PLC、电表
RS422差分4线全双工<1200m点对多较少见,类485

注意:

  • RS-232 是负逻辑(-12V是逻辑1),但 MAX3232 芯片会自动帮你翻转,你在 FPGA 里只需按正逻辑写。
  • RS-485 是抗干扰之王,因为它看的是两根线的压差,外界的共模噪声会被抵消。

1.4、FPGA 实现

在设计代码是应特别关注以下影响设计稳定性的细节:

A. 采样策略 (Sampling)

  • 做法:在波特率计数的中间点读取一次 rx_pin
  • 进阶做法 (过采样):为提高抗噪能力,例如使用 16 倍波特率的时钟去采样,在中间连续采 3 次,取众数(多数表决)。

B. 跨时钟域 (CDC)

  • 外部进来的 rx 信号是异步的,与 FPGA 的 sys_clk 无关。
  • 必须使用“打两拍”处理,否则状态机会因为亚稳态而跑飞。

C. 波特率误差

  • 公式:DIV_CNT = CLK_FREQ / BAUD_RATE
  • 如果系统时钟是 50MHz,波特率 115200:
    • 50,000,000 / 115200 ≈ 434.02
    • 取整 434,误差非常小,可以忽略。
  • 但如果时钟频率很奇葩,导致除不尽,累积误差可能会导致这一帧的最后一位采样偏移出界。一般来说,累积误差控制在 5% 以内都能正常通信。

二、Verilog代码

为了保证代码的鲁棒性,我们将设计分为三个部分:

  • UART_RX (接收模块):关键在于跨时钟域处理(打两拍)和中心对齐采样或者过采样,防止亚稳态和噪声干扰。
  • UART_TX (发送模块):相对简单,主要是状态机控制时序。
  • Top_Loopback (顶层回环):用于板级验证,接收什么就发送什么。

2.1、接收模块(uart_rx)

核心设计:加入了输入两级寄存器同步(消除亚稳态)和比特中心采样逻辑。

module uart_rx #( parameter CLK_FREQ = 50_000_000, // 系统时钟频率 parameter BAUD_RATE = 115200 // 目标波特率 )( input wire clk, input wire rst_n, input wire rx_pin, // 异步串口输入 output reg [7:0] rx_data, // 接收到的数据 output reg rx_done // 接收完成脉冲 ); // 计算分频计数最大值 localparam CNT_MAX = CLK_FREQ / BAUD_RATE; localparam CNT_MID = CNT_MAX / 2; // 采样点(波特率中心) // 状态定义 localparam IDLE = 0; localparam START = 1; localparam DATA = 2; localparam STOP = 3; reg [1:0] state; reg [31:0] clk_cnt; reg [2:0] bit_cnt; // 消除亚稳态:对异步信号 rx_pin 打两拍 reg rx_d1, rx_d2; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin rx_d1 <= 1'b1; rx_d2 <= 1'b1; end else begin rx_d1 <= rx_pin; rx_d2 <= rx_d1; end end // 下降沿检测(用于检测起始位) wire rx_negedge = rx_d2 & (~rx_d1); always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; clk_cnt <= 0; bit_cnt <= 0; rx_data <= 0; rx_done <= 0; end else begin rx_done <= 0; // 默认拉低 case (state) IDLE: begin if (rx_negedge) begin // 检测到起始位下降沿 state <= START; clk_cnt <= 0; end end START: begin if (clk_cnt == CNT_MID) begin // 在起始位中间再次确认电平 if (rx_d2 == 1'b0) begin clk_cnt <= 0; state <= DATA; end else begin state <= IDLE; // 误触发,或者是毛刺 end end else begin clk_cnt <= clk_cnt + 1; end end DATA: begin if (clk_cnt == CNT_MAX - 1) begin clk_cnt <= 0; rx_data[bit_cnt] <= rx_d2; // 移位接收 if (bit_cnt == 7) begin bit_cnt <= 0; state <= STOP; end else begin bit_cnt <= bit_cnt + 1; end end else begin clk_cnt <= clk_cnt + 1; end end STOP: begin if (clk_cnt == CNT_MAX - 1) begin state <= IDLE; clk_cnt <= 0; rx_done <= 1'b1; // 接收完成,产生一个脉冲 end else begin clk_cnt <= clk_cnt + 1; end end default: state <= IDLE; endcase end end endmodule

2.2、发送模块(uart_tx)

核心设计:加入了输入两级寄存器同步(消除亚稳态)和比特中心采样逻辑。

module uart_tx #( parameter CLK_FREQ = 50_000_000, parameter BAUD_RATE = 115200 )( input wire clk, input wire rst_n, input wire tx_start, // 发送使能信号 input wire [7:0] tx_data, // 待发送数据 output reg tx_pin, // 串口发送引脚 output reg tx_busy // 忙信号 ); localparam CNT_MAX = CLK_FREQ / BAUD_RATE; localparam IDLE = 0; localparam START = 1; localparam DATA = 2; localparam STOP = 3; reg [1:0] state; reg [31:0] clk_cnt; reg [2:0] bit_cnt; reg [7:0] data_reg; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; tx_pin <= 1'b1; // 空闲时为高 tx_busy <= 1'b0; clk_cnt <= 0; bit_cnt <= 0; data_reg <= 0; end else begin case (state) IDLE: begin tx_pin <= 1'b1; if (tx_start) begin state <= START; data_reg <= tx_data; tx_busy <= 1'b1; clk_cnt <= 0; end else begin tx_busy <= 1'b0; end end START: begin // 发送起始位 0 tx_pin <= 1'b0; if (clk_cnt == CNT_MAX - 1) begin clk_cnt <= 0; state <= DATA; end else begin clk_cnt <= clk_cnt + 1; end end DATA: begin // 发送8位数据(LSB first) tx_pin <= data_reg[bit_cnt]; if (clk_cnt == CNT_MAX - 1) begin clk_cnt <= 0; if (bit_cnt == 7) begin bit_cnt <= 0; state <= STOP; end else begin bit_cnt <= bit_cnt + 1; end end else begin clk_cnt <= clk_cnt + 1; end end STOP: begin // 发送停止位 1 tx_pin <= 1'b1; if (clk_cnt == CNT_MAX - 1) begin clk_cnt <= 0; state <= IDLE; tx_busy <= 1'b0; // 释放忙信号 end else begin clk_cnt <= clk_cnt + 1; end end default: state <= IDLE; endcase end end endmodule

2.3、顶层模块(uart_top)

核心设计:将RX的 rx_done 直接作为 TX的 tx_start,实现“收到什么发回什么”的回环测试功能,这是验证串口最快的方法。

module uart_loopback_top( input wire clk, // 连接到 PL 时钟 ( 50MHz) input wire rst_n, // 复位信号 input wire uart_rx, output wire uart_tx ); // 参数定义 parameter CLK_FREQ = 50_000_000; parameter BAUD_RATE = 115200; wire [7:0] rx_data; wire rx_done; wire tx_busy; // 实例化 RX uart_rx #( .CLK_FREQ(CLK_FREQ), .BAUD_RATE(BAUD_RATE) ) u_rx ( .clk(clk), .rst_n(rst_n), .rx_pin(uart_rx), .rx_data(rx_data), .rx_done(rx_done) ); // 实例化 TX // 当接收完成(rx_done)且发送不忙时,启动发送 uart_tx #( .CLK_FREQ(CLK_FREQ), .BAUD_RATE(BAUD_RATE) ) u_tx ( .clk(clk), .rst_n(rst_n), .tx_start(rx_done), .tx_data(rx_data), .tx_pin(uart_tx), .tx_busy(tx_busy) ); endmodule

2.4、引脚约束(XDC)

create_clock -period 20.000 -name sys_clk [get_ports clk] set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports clk] set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports rst_n] set_property -dict {PACKAGE_PIN T19 IOSTANDARD LVCMOS33} [get_ports uart_rx] set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports uart_tx] set_property SLEW SLOW [get_ports uart_tx]

对时钟信号、复位信号、uart输入输出信号进行引脚及电平绑定,同时对时钟信号进行时序约束。Slew Rate是压摆率控制输出引脚电平变化的“陡峭程度”,通常默认就是SLOW,可用可不用。

三、上板验证

板卡:正点原子启明星ZYNQ7020
软件:Vivado2019.2
串口助手:XCOM

步骤与结果验证:

  1. 参数配置:启动串口调试终端,设置波特率为 115200,8 位数据位,1 位停止位,无校验模式,并打开串口。
  2. 数据收发:在发送缓冲区输入指令“ABCD”等回环测试数据并发送。
  3. 结果判定:接收缓冲区显示的数据与发送数据一致(参见上图)。此现象表明串口数据环回功能运行正常,链路通信可靠。

Read more

node-llama-cpp安装与配置:Windows、Linux和Mac全平台教程

node-llama-cpp安装与配置:Windows、Linux和Mac全平台教程 【免费下载链接】node-llama-cppRun AI models locally on your machine with node.js bindings for llama.cpp. Force a JSON schema on the model output on the generation level 项目地址: https://gitcode.com/gh_mirrors/no/node-llama-cpp node-llama-cpp是一个基于llama.cpp的Node.js绑定库,让你能够在本地机器上运行AI模型,并在生成级别强制模型输出符合JSON模式。本文将为你提供Windows、Linux和Mac全平台的安装与配置教程,帮助你快速上手这款强大的AI工具。 一、准备工作 在开始安装node-llama-cpp之前,请确保你的系统满足以下要求:

GitHub Copilot提示词终极攻略:从“能用”到“精通”的AI编程艺术

摘要:GitHub Copilot作为当前最强大的AI编程助手,其真正的价值不仅在于自动补全代码,更在于开发者如何通过精准的提示词工程与之高效协作。本文系统解析Copilot提示词的核心原理、设计框架与实战技巧,涵盖从基础使用到高级功能的完整知识体系。通过四要素框架、WRAP法则、多场景应用指南,结合表格、流程图等可视化工具,帮助开发者掌握与AI协作的编程范式,提升300%以上的开发效率。文章深度结合当今AI技术发展趋势,提供理论性、可操作性、指导性并存的全面攻略。 关键词:GitHub Copilot、提示词工程、AI编程、代码生成、开发效率、人机协作 🌟 引言:当编程遇见AI,一场思维范式的革命 “写代码就像与一位天才但有点固执的同事合作——你需要用它能理解的语言,清晰地表达你的意图。”这是我在深度使用GitHub Copilot六个月后的最大感悟。 2023年以来,AI编程助手从概念验证走向生产力工具的核心转变,标志着一个新时代的到来。GitHub Copilot不再仅仅是“自动补全工具”,而是具备问答、编辑、自动执行能力的AI开发伙伴。然而,许多开发者仍停留在基础使

手把手教你使用 Faster-Whisper 实时语音输入转文本,本地部署教程

手把手教你使用 Faster-Whisper 实时语音输入转文本,本地部署教程

文章目录 * 前言 * 一、安装环境 * 二、使用步骤 * 1.下载模型 * 2.实时录音转文本脚本 * 3.报错解决方法 * 总结 前言 要想实现像豆包、微信等一样的语音输入功能,通常有两种主流方案:云端 API(轻量、准确度极高)和 本地模型(免费、隐私、无需联网)。由于目前开发的系统需要添加一个语音识别功能,刚好记录一下使用 Faster-Whisper 实时语音输入转文本。Faster-Whisper官网地址链接: Faster-Whisper官网地址 复现成功如下图所示,请看下文教程就能部署本地实时语音输入转文本模型: 电脑有显卡的话可以参考下面这篇文章安装 cuda 和 cudnn cuda和cudnn的安装教程: cuda和cudnn的安装教程(全网最详细保姆级教程) 一、安装环境 在你的虚拟环境安装 faster-whisper,命令如下: pip install faster-whisper 安装录音库

GitHub Copilot安装使用

GitHub Copilot安装使用

GitHub Copilot 怎么安装使用 一、 安装前准备 1. 拥有一个 GitHub 账号:如果没有,请先在 GitHub 官网 注册。 2. 订阅 GitHub Copilot: * 访问订阅页面:登录 GitHub 后,访问 GitHub Copilot 官网。 * 选择订阅计划: * 个人版:适合独立开发者,提供 30 天免费试用,之后每月 $10 或每年 $100。 * 商业版 (Copilot for Business):适用于企业或团队,每位用户每月 $19。 * 教育优惠:学生、教师和热门开源项目维护者可免费使用,需通过身份验证。 * 完成支付:根据所选计划完成支付流程(个人版需绑定信用卡或