手搓一个UART串口,难度有多大

strongerHuang 2025-08-16 08:00

对于学“电”的同学来说,一提到串口肯定不陌生,工业自动化领域普遍使用的RS485/RS232/RS422总线通信,或者电子DIY时使用的各种USB-TTL模块,串口通信简直就是电子世界的"普通话"——简单、通用、无处不在

资讯配图

 图

1 USB转TTL USB串口转换器


你真的了解它的工作原理吗?你真的了解串口通信的底层协议吗?今天,我们将从零开始实现一个真正的UART通信系统并且在FPGA上实现它

资讯配图

UART:电子世界的通用语言

资讯配图

前面我们提到的串口通信,他们的底层接口指的都是UART接口或者叫通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。

在工业应用中存在多种串行通信方式,其中RS-232是一种最常用的串行数据通信接口标准之一,它在UART协议的基础上使用特殊的电平标准来表示逻辑“0”和逻辑“1” 当电压差值在+3V ~ +15V之间表示逻辑“0”,当电压差值在-3V ~ -15V之间表示逻辑“1”。除此之外,还有其他电平标准的串行通信接口标准,比如使用差分电平来表示逻辑信号的RS485、RS422等。他们本质上使用的还是UART协议,只是传输时所使用的电气标准不一样而已。现在的商用计算机或笔记本电脑上已经不再集成RS232接口了,但是大量的工业自动化设备、智能仪器仪表或嵌入式设备还在使用RS232接口作为设备与设备之间的远距离通信。

串行通信的两端设备一般使用一个叫做DB9的硬件接口,如图4所示。这个串行通信端口有9个引脚,其中引脚 2 (RX)和引脚 3 (TX)可以直接连接到另一个设备的RS232端口进行串行通信,因此DB9连接器分为公头(引脚是针)和母座(引脚是孔),如图2所示,有的串行传输线的两端往往是一公一母,分别连接两端设备的母座和公头。

资讯配图

2 DB9连接器示意图

资讯配图

3 DB9接口的串行连接线


我们今天使用的大多数计算机不再提供这种RS232连接器,我们电脑现在常用的串行通信接口是USB,因此如果我们想在硬件设备和计算机之间建立RS232串行通信,那么必须使用一个叫做USB转TTL的USB串口转换器,如图4所示。该硬件的核心是一颗USB-UART协议转换芯片,比如CP2102、CH340、FT232等。


资讯配图

USB转TTL USB串口转换器


USB串口转换器是一个转换桥,将电脑的USB接口和设备的UART接口连接在一起,并且在电脑端模拟一个串行通信设备。当我们使用USB数据线连接USB转TTL 串口转换器和电脑时,在电脑的设备管理器,端口一栏将显示一个USB串行设备并分配COM端口编号,如图5所示。

资讯配图

5在设备管理器端口一栏下显示的串行通信设备

资讯配图

什么是UART通信?

资讯配图

UART(通用异步收发传输器)是一种广泛使用的串行通信协议,用于在设备之间传输数据。它在工业自动化、嵌入式系统和电子DIY项目中都有广泛应用。

资讯配图

UART的基本原理

资讯配图

UART通信是异步通信,这意味着发送端和接收端不需要共享时钟信号。相反,它们通过约定的波特率(每秒传输的位数)来同步数据传输。

资讯配图

UART的数据帧格式

资讯配图

一个标准的UART数据帧包含以下部分:


资讯配图

UART通信协议

资讯配图

在硬件层面,两个设备A和B想要在 UART协议中“交谈”,每个设备需要两个GPIO,TX用于发送和RX用于接收,并按照图6所示的方式连接。

资讯配图

为两个硬件建立物理UART连接


UART是异步传输,尽管没有同步的时钟线,但是为了“在一个频道上说话”,两边总要约定好“沟通的语言”,也就是通信协议。在UART通信中,数据以字节为单位发送,这意味着每次有8位数据依次从设备A的TX引脚传输到设备B的RX引脚,为了确保接收端能正确地接收数据,在每个字节的两端添加了逻辑0(低电平)的起始位和逻辑1(高电平)的停止位。我们也可以在MSB(最高有效位)后添加奇偶校验,这是可选的。发送1字节数据的协议7所示

资讯配图

TX线发送1字节的数据格式


两端“说话的语速”也就是数据传输的速度是以单位时间内的波特数为单位来衡量的,有时称为波特率,定义为1秒内数据通过传输线时符号数变化的速率。在UART通信中,我们可以将波特率视为在1秒内传输的位数。只要通信的两端约定好了波特率,不管波特率多少,都是可以沟通的,但是为了减少沟通的成本,我们还是要约定几种标准的波特率。如表1所示,列出了一些标准的波特率及位传输时间。第三列是Verilog 设计的分频参数,我们稍后会提到。


在不同的波特下发送每个比特的时间

标准波特率

位持续时间(ms)

分频系数BPS_PARA(基准12MHz)

1200

8.33

10000

2400

4.16

5000

4800

2.08

2500

9600

1.04

1250

19200

0.52

625

38400

0.26

313

57600

0.176

208

115200

0.0868

104

要注意的是,波特率和比特率不是同一个概念,两者经常混淆,后者的单位是位每秒(bps)。在UART中,每个符号由1位数据表示,因此UART中的波特率与比特率相同。然而,电信中的一些其他编码机制使用多个比特来表示一个符号。例如,在曼彻斯特编码中,每个符号携带2位信息,因此10波特率在曼彻斯特编码中意味着20bps。举个例子,我们将展示一个完整的数据传输过程,其中一个字节10111100)2从硬件A发送到硬件B,整个过程分三步进行。

资讯配图

8 UART通信演示-数据发送


资讯配图

UART通信演示-数据传输


资讯配图

10 UART通信演示-数据接收

资讯配图

FPGA实现UART

资讯配图

如果你之前有使用过STM32一类的单片机Arduino、Raspberry Pi等开源硬件的经验,那么使用串口调试助手的小软件,将硬件微控制器的字符发送到计算机上打印出来,这种操作对你来说肯定很简单但是我们现在要说的是如何设计一个UART通信模块并让它跑起来呢?生产一块芯片肯定是不现实的,最容易的方式就是在FPGA上来实现你的想法。

相比使用现成的串口芯片,用FPGA实现UART其实是非常有意思的事情

这种掌控全局,一切自己来实现的快感不正是我们工程师想要的吗。

下面准备工作:

资讯配图

11小脚丫FPGA开发板

接下来我们分步骤介绍实现过程。

资讯配图

手搓UART核心:从理论到实践

资讯配图

1. 波特率生成器 - 通信的"心跳"

精确的波特率是UART通信的基础,通过FPGA内部计数器实现:

首先,我们需要一个生成不同标准波特率的波特率分频模块。波特率分频模块的基本定义如图12所示。要设置不同的波特率,请根据表1更改其他标准波特的参数BPS_PARA。

资讯配图

12 波特率分频模块定义

module Baud # (parameter BPS_PARA = 1250)(	input					clk, rst_n,		input					bps_en,		// Connects to bps_en on UART_Tx	output	reg				bps_clk		// Connects to bps_clk on UART_Tx);	 reg				[12:0]	cnt;always @ (posedge clk or negedge rst_n) beginif(!rst_n		cnt <= 1'b0;else if((cnt >= BPS_PARA-1)||(!bps_en)) // if bps_en is low, stop Bauds		cnt <= 1'b0;						else 		cnt <= cnt + 1'b1;end // Setup for different Bauds according to the parameter givenalways @ (posedge clk or negedge rst_n)beginif(!rst_n			bps_clk <= 1'b0;else if(cnt == (BPS_PARA>>1)) 	// Use the middle point for sampling, see Figure 5.6.x 			bps_clk <= 1'b1;	else 			bps_clk <= 1'b0;	endendmodule

我们还需要一个模块,它以字节为单位接收数据,然后按顺序一位一位的输出数据。UART_Tx模块的结构如图13所示,连续地输入8位的dataByte,并通过输出线tx依次发送每一位。

资讯配图

13  UART_Tx的模块定义

module Uart_Tx (    input  clk,rst_n,            input  bps_clk,     // 连接到 BaudGen 模块的 bps_clk    output  reg bps_en, // 连接到 BaudGen 模块的 bps_en     input tx_en,        // 发生使能位    input [7:0] tx_data,// 待发送数据    output  reg tx       // 串行输出信号);

14显示了UART_Send模块的完整结构。该模块接受8位输入数据 dataByte(在这里我们可以设置一个模拟的输入数据,比如一个固定的8位数据0x55),并通过tx线以特定的波特率(默认为9600)顺序发送出去。接下来,我们将把这个模块rs32_tx连接到USB-TTL串口转换模块上,并通过COM端口观察串口转换模块输出数据。

资讯配图

14通过UART发送数据的数字模块结构


2. UART接收器 - 数据的"解译者"

先对RX信号多级缓存消除亚稳态,同时检测下降沿,程序实现如下:

input                   uart_rx,        //UART接收输入 reg uart_rx0,uart_rx1,uart_rx2; //多级延时锁存去除亚稳态always @ (posedge clk) begin    uart_rx0 <= uart_rx;    uart_rx1 <= uart_rx0;    uart_rx2 <= uart_rx1;end //检测UART接收输入信号的下降沿wire    neg_uart_rx = uart_rx2 & ~uart_rx1;

当检测RX有下降沿后,使能节拍使能信号,同时自锁直到完成接收操作后再复位节拍使能信号。程序实现如下:

//接收时钟使能信号的控制always @ (posedge clk or negedge rst_n) begin    if(!rst_n)        bps_en <= 1'b0;    else if(neg_uart_rx && (!bps_en)) //当检测到传输,使能节拍使能信号        bps_en <= 1'b1;         else if(num==4'd9)              //完成UART接收操作,复位节拍使能信号        bps_en <= 1'b0;         end

根据节拍信号完成UART总线的数据采样,得到8位有效数据,程序实现如下:

reg             [7:0]   rx_data;//当处于工作状态中时,按照接收时钟的节拍获取数据always @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        num <= 4'd0;        rx_data <= 8'd0;    end else if(bps_en) begin           if(bps_clk) begin                       num <= num + 1'b1;            if(num<=4'd8) rx_data[num-1] <= uart_rx1; //先低位后高位        end else if(num == 4'd9) begin              num <= 4'd0;                        end    end else begin        num <= 4'd0;    endend

UART接收操作完成后,将得到的8位有效数据输出给后级电路,程序实现如下:

//将接收的数据输出,同时控制输出有效信号产生脉冲always @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        rx_data_out <= 8'd0;        rx_data_valid <= 1'b0;    end else if(num == 4'd9) begin          rx_data_out <= rx_data;        rx_data_valid <= 1'b1;    end else begin        rx_data_out <= rx_data_out;        rx_data_valid <= 1'b0;    endend

最后将节拍模块Baud和接收模块Uart_rx实例化并连接,完成发送功能的设计,如15

资讯配图

15 通过UART接收数据的数字模块结构


整个UART驱动设计是由两个独立的功能组合而成:发送功能部分和接收功能部分。UART功能总体设计框图如下16所示

资讯配图

16 UART功能总体设计框图


当我们需要UART发送数据的时候只需要实例化发送功能部分设计,需要UART接收数据的时候只需要实例化接收功能部分设计,例如本设计中FPGA驱动UART模块接收电脑串口调试助手发出的数据,所以我们就只需要实例化接收功能部分设计即可。


资讯配图

电子工程师的终极浪漫

资讯配图

当我们用Verilog从零构建UART收发器时,才真正理解了:

“造轮子”不是重复劳动,而是对本质的追问。


------------ END ------------

资讯配图
●专栏《嵌入式工具
●专栏《嵌入式开发》
●专栏《Keil教程》
●嵌入式专栏精选教程

关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

点击“阅读原文”查看更多分享。

声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
AR
more
J Hazard Mater:基于3D Ag-rGO-f-Ni(OH)2/NF信号放大和热毛细对流的微流控Pb2+电化学传感平台
【央视AI盛典】姚期智、Gillian Hadfield 、Stuart Russell、周伯文:解码时刻-AI人才培养
GPT-5发布,OpenAI放出System Card:安全、事实性、推理全面升级,迈向AGI新拐点?
提升AR眼镜防护能力,扬杰科技TVS技术革新
2026年,Arm为GPU引入专用神经加速器
GPT-5口碑雪崩后,Garry Marcus直言:奥特曼应为GPT-5的灾难引咎辞职
英飞凌完成对Marvell汽车以太网业务的收购 | 区势·半导体
Tape-out生死时速:华大九天Argus重塑大规模SoC芯片物理验证效率!
迅路创新获得数千万元融资,推出定价超五万的高端智能E-cargo bike|早起看早期
【投融资】Meta系初创公司NectarSocial获860万美元融资,用AI解码全网消费动因与情绪
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号