串口收发UART(Verilog HDL)

uart,verilog,hdl · 浏览次数 : 2

小编点评

本文介绍了一种基于UART的串行通信设计,旨在实现计算机与嵌入式系统之间的数据交换。文章首先概述了UART通信的基本概念和接口规范,然后详细描述了设计目标、框图、时序图以及发送和接收端的处理逻辑。最后,通过仿真程序验证了设计的正确性。 1. **UART通信概述**:本文首先介绍了UART通信的基本概念,包括其异步特性、帧格式、波特率等关键技术参数。 - UART是一种异步串行通信协议,用于计算机和嵌入式系统之间的数据交换。 - 接口规范包括RS-232、RS449、RS423和RS485等,规定了通信标准的电气特性、传输速率等。 2. **设计目标与框图**:文章提出了设计目标,即实现PC上位机RS232总线信号的接收、重新打包转发至PC端显示,并确保数据完整无错码。 - 设计目标:格式为起始位+8位数据(无校验)+停止位的串口收发。 - 设计框图:展示了串行帧格式、发送和接收信号线的连接方式。 3. **时序分析与仿真**:文章详细分析了UART的发送和接收时序,包括起始位、数据位、校验位和停止位的处理逻辑。 - 时序分析:讨论了亚稳态问题及其对信号传输的影响。 - 仿真程序:通过编写Verilog HDL程序,验证了接收和发送时序的正确性。 4. **整体验证与设计成功**:文章通过实际仿真验证了设计的正确性,确保了数据传输的准确性和可靠性。 - 数据处理:验证了接收到的数据与源数据的一致性。 - 设计成功:通过实例化设计框架,验证了设计的实用性和有效性。 总的来说,本文成功设计并验证了一种基于UART的串行通信方案,满足了计算机与嵌入式系统间数据交换的需求,并具备良好的可靠性和稳定性。

正文

UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是一种异步串行通信协议,主要用于计算机和嵌入式系统之间的数据交换。

实现UART通信的接口规范和总线标准包括RS-232、RS449、RS423和RS485等,接口标准规定了通信标准的电气特性、传输速率、连接特性和机械特性。

文章摘要:本篇文章目标设计一个格式为起始位+8位数据(无校验)+停止位的串口收发,接收PC上位机RS232总线信号后,重新打包转发至PC端显示(形成回环),数据完整无错码情况。

关键词: 异步时钟;亚稳态;异步串行通信;Verilog HDL

目标设计框图:

image

【串行帧格式】

UART通信使用两条信号线进行数据传输:发送数据线(TX)和接收数据线(RX)。数据以数据帧的形式传输,每个数据帧由起始位、数据位(通常为5到9位)、校验位(可选)和停止位(通常为1到2位)组成。

发送方在发送数据之前,会先发送一个起始位(通常为低电平),然后按照设定的数据位和校验位发送数据,最后发送一个或多个停止位(通常为高电平)以标识数据传输的结束。接收方在检测到起始位后,会开始接收数据,直到检测到停止位为止(起止式协议)。同时,接收方会根据约定的校验方式对数据进行校验。

image

1、起始位(Start Bit):通常是一个单独的位,逻辑值为0(低电平),表示字符开始,接收端同步到发送端的数据流。

2、数据位(Data Bits):帧的主体部分,包含传输信息,数据位数量可以根据需要进行配置,常见的有5位、6位、7位、8位等。

3、校验位(Parity Bit):可选位,校验位可以是偶校验(Even Parity)、奇校验(Odd Parity)或无校验(No Parity),偶校验意味着数据位中1的个数(包括校验位)应该是偶数;奇校验则是奇数。

4、停止位(Stop Bits):标志字符的结束,并允许接收器在接收下一个字符之前有时间复位。停止位的数量可以是1位、1.5位或2位。

5、空闲位(Idle Line):在两个字符之间,线路通常保持在高电平状态(逻辑值为1)称为空闲线状态,它允许接收端在字符之间有时间来检测和响应线路状态的变化。


数据传输中,波特率(Baud rate)是一个重要的参数,表示单位时间内传输的码元符号的个数(通常指二进制位)。波特率并不直接等同于比特率(bit rate),因为每个码元符号可能包含多个比特。但在许多情况下,特别是在串行通信中,一个码元符号就是一个比特,此时波特率就等于比特率。

传送速率为960字符/秒,而每个字符为10位(可能是1个起始位、8个数据位和1个停止位)。总的二进制位数传送速率是960字符/秒 × 10位/字符 = 9600位/秒,所以波特率就是9600。

【异步传输存在亚稳态】

在数字电路设计中同步建立至关重要,异步时钟域之间的信号通信不可避免存在亚稳态问题。UART接收过程,起始位的检测尤为关键,错误的起始位检测可能导致整个数据包的接收失败。为了避免因亚稳态导致的采样错误和电路故障,设计者必须在接口处采取可靠的同步化措施。

亚稳态:简单来说,亚稳态是指触发器(Flip-Flop)或其他数字电路元件无法在某个规定的时间段内达到一个可确认的稳定状态。

产生原因:主要由于违反了触发器的建立和保持时间(Setup and Hold Time)要求。在时钟上升沿前后的特定时间窗口内,如果数据输入端口上的数据发生变化,就会产生时序违规,从而导致亚稳态的出现,如下图。

image

表现特征:在稳定期间,触发器可能输出一些中间级电平,或者处于振荡状态。并且,这种无用的输出电平可以沿信号通道上的各个触发器级联式传播下去,导致整个系统的不稳定。

汽车芯片的可靠性设计:控制亚稳态,提升稳定性 - 知乎

减少亚稳态影响:

亚稳态震荡时间(Tmet)关系到后级寄存器的采集稳定问题,Tmet影响因素包括器件的生产工艺、温度、环境等。

当Tmet1时间长到大于一个采样周期后,那第二级寄存器就会采集到亚稳态,但是从第二级寄存器输出的信号就是相对稳定的了。由于寄存器本身就有减小Tmet时间让数据快速稳定的作用,第二级寄存器的Tmet2的持续时间继续延长到大于一个采样周期这种情况虽然会存在,但是其概率是极小的。在第三级寄存器时,数据传输几乎达到稳态。

image

单比特信号从慢速时钟域同步到快速时钟域需要使用打两拍的方式消除亚稳态。第一级寄存器产生亚稳态并经过自身后可以稳定输出的概率为70%~80%,第二级寄存器可以稳定输出的概率为99%左右,再多加寄存器的级数改善效果就不明显了,Verilog HDL程序如下。

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	rx_reg1 <= 1'b1;
    else	rx_reg1 <= rx;
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	rx_reg2 <= 1'b1;
    else    rx_reg2 <= rx_reg1;
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	rx_reg3 <= 1'b1;
    else    rx_reg3 <= rx_reg2;
end

【接收端处理逻辑】

在本篇中,通信格式:起始位+8位数据+无校检+1位停止位。sys_clk为系统时钟(在这里仅作为时钟参考)设置为50Mhz(20ns),异步通信的波特率设定为9600Baud,则波特率计数器Baud_cnt最大值 = 1/9600x50_000_000 ≈ 5208。

bit_flagbit_cnt:位标志和位计数器,用于跟踪当前接收到的位的数量和状态。rx_reg1, rx_reg2, rx_reg3:接收寄存器,用于暂存接收到的数据,rx_flagpo_flag:接收和发送的标志,用于指示接收或发送过程的状态。rxrx_data_out:接收到的数据和输出数据。具体UART接收端时序图如下。

image

数据流:start信号被触发时,UART开始接收数据,并使能work_en信号,数据位(XData[0]XData[7])被连续接收并存储在rx_reg1rx_reg2rx_reg3等寄存器中。在数据接收过程中,Baud_cnt[12:0]bit_flagbit_cnt 用于确保数据以正确的波特率接收,并跟踪当前位的状态。一旦所有数据位和停止位(Xstop)都被接收,UART将rx_data_out设置为接收到的数据,并可能设置rx_flag以指示数据已准备好。

依据时序图,接续完成其他类型信号时序程序逻辑:

always @(posedge sys_clk or negedge sys_rst)begin//处理start_flag逻辑
    if(!sys_rst)	 start_flag <= 1'b0;
    else if((rx_reg3 == 1'b1)&&(rx_reg2 == 1'b0)&&(work_en == 1'b0))start_flag <= 1'b1;
    else start_flag <= 1'b0;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理work_en逻辑
    if(!sys_rst)	work_en <= 1'b0;
    else if(start_flag)		work_en <= 1'b1;
    else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))	work_en <= 1'b0;
    else work_en <= work_en;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理baud_cnt逻辑
    if(!sys_rst)	baud_cnt <= 13'd0;
    else if((baud_cnt == BAUD_CNT_MAX - 1'b1)||(work_en == 1'b0))baud_cnt <= 13'd0;
    else 	baud_cnt <= baud_cnt + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理bit_flag逻辑
    if(!sys_rst)	bit_flag <= 1'b0;
    else if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1)	bit_flag <= 1'b1;
    else bit_flag <= 1'b0;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理bit_cnt逻辑
    if(!sys_rst)	 bit_cnt <= 4'd0;
    else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))	 bit_cnt <= 4'd0;
    else if(bit_flag)	bit_cnt <= bit_cnt + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理rx_data逻辑
    if(!sys_rst)	rx_data <= 8'b0;
    else if((bit_cnt >= 4'd1)&&(bit_cnt<= 4'd8)&&(bit_flag))
        rx_data <= {rx_reg3,rx_data[7:1]};
end

always @(posedge sys_clk or negedge sys_rst)begin//处理rx_flag逻辑
    if(!sys_rst)	rx_flag <= 1'd0;
    else if((bit_cnt == 4'd8)&&(bit_flag))	rx_flag <= 1'b1;
    else 	rx_flag <= 1'd0;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理po_data逻辑
    if(!sys_rst)	po_data <= 8'd0;
    else if(rx_flag)	po_data <= rx_data;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理po_flag逻辑
    if(!sys_rst)	po_flag <= 1'b0;
    else	po_flag <= rx_flag;
end

完备后,编写了一个简单的仿真程序,验证接收的时序情况。从下图可知,三级寄存器依次往后延迟了一个时钟周期,即20ns,这实现了目标期望,用于减小亚稳态影响,保证时序准确。
image

再看接收rx信号线数据的处理情况,待一轮字符数据处理完成后,po_data得到了正确的串行输入数据。波特率计数器在计数到13’d5027后归零,等待使能信号拉高。

image

【发送端处理逻辑】

较于接收端,发送端逻辑比较简单。pi_data :输入的8位数据信号,pi_flag:输入信号标志。Baud_cnt为波特率计数器。输入发送标志为高电平,发送端将准备的数据,按位计数输出到串行信号总线tx,依次拉低信号线,发送起始标志+8位数据信号。比特计数器计数到4'd9拉低使能线,表示单个字符数据输出完毕,拉高信号线标志停止至空闲状态。

image

always @(posedge sys_clk or negedge sys_rst)begin//处理work_en逻辑
    if(!sys_rst)	 work_en <= 1'b0;
    else if((bit_cnt == 4'd9)&&bit_flag)	work_en <= 1'b0;
    else if(pi_flag)	work_en <= 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理baud_cnt逻辑
    if(!sys_rst)	 baud_cnt <= 13'b0;
    else if((baud_cnt == BAUD_CNT_MAX - 1'b1)||(!work_en))	 baud_cnt <= 13'b0;
    else if(work_en)	baud_cnt <= baud_cnt + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理bit_cnt逻辑
    if(!sys_rst)	bit_cnt <= 4'd0;
    else if((bit_cnt == 4'd9)&&(bit_flag == 1'b1))	bit_cnt <= 4'd0;
    else if((work_en == 1'b1)&&(bit_flag == 1'b1))	bit_cnt <= bit_cnt + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理bit_flag逻辑
    if(!sys_rst)	 bit_flag <= 1'b0;
    else if(baud_cnt == 13'd1)	bit_flag <= 1'd1;
    else 	bit_flag <= 1'b0;
end

always @(posedge sys_clk or negedge sys_rst)begin//处理tx发送逻辑
    if(!sys_rst)	 tx <= 1'b0;
    else if(bit_flag == 1'b1)begin
            case(bit_cnt)
            0   :tx <= 1'b0;
            1   :tx <= pi_data[0];
            2   :tx <= pi_data[1];
            3   :tx <= pi_data[2];
            4   :tx <= pi_data[3];
            5   :tx <= pi_data[4];
            6   :tx <= pi_data[5];
            7   :tx <= pi_data[6];
            8   :tx <= pi_data[7];
            9   :tx <= 1'b1;
            default: tx<= 1'b1;
            endcase
        end
end

完备后,编写了仿真程序,验证发送的时序情况。检测到输入标志为高电平,work_en使能有效,波特率计数器开始计数,比特位计数器待其计数到13'd1时计数加1,这样避免比特错判。

image

从下面仿真图可以看到,tx发送输出的串行信号时序是与pi_data保持一致的。

image

【整体验证UART】

首先,建立五组任意字符数据,经UART发送端向UART接收端转发。通过仿真程序进行分析,由于接收端与PC源数据相比,对数据的处理要迟后一个字符周期,即5208x20x10ns,从下图可得到,数据处理串行数据tran_data后保持正确,并且输出数据out_data与源数据保持一致,即可验证模块设计暂时是没有大问题。

image

最后,将UART接收端和发送端模块实例化为文章开头的设计框架。在PC端发送任意三组数据后,如下图COM11端口接收数据并且正确,设计到此验证成功,满足期望。

image

文献参考:

[1] 野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)

[2] FPGA中亚稳态——让你无处可逃 - 屋檐下的龙卷风 - 博客园 (cnblogs.com)

[3] ww1.microchip.com/downloads/en/DeviceDoc/70000582e.pdf

[4]万敏. 异步时序电路中的亚稳态设计与分析[D]. 上海:上海交通大学,2008.


本篇文章中使用的Verilog程序模块,若有需见网页左栏Gitee仓库链接:憨大头的妙妙屋: 真奇妙! (gitee.com)

与串口收发UART(Verilog HDL)相似的内容:

串口收发UART(Verilog HDL)

文章摘要:本篇文章目标设计一个格式为起始位+8位数据(无校验)+停止位的串口收发,接收PC上位机RS232总线信号后,重新打包转发至PC端显示(形成回环),数据完整无错码情况。 关键词:异步时钟;亚稳态;异步串行通信;Verilog HDL

张高兴的 MicroPython 入门指南:(三)使用串口通信

目录什么是串口使用方法使用板载串口相互通信硬件需求电路代码使用板载的 USB 串口参考 什么是串口 串口是串行接口的简称,这是一个非常大的概念,在嵌入式中串口通常指 UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)。使用串口进行的通信

从原理聊JVM(二):从串行收集器到分区收集开创者G1

随着Java的进化过程,涌现出各种不同的垃圾回收器,从串行执行到并行执行,从高吞吐到低延迟,终极目标就是让开发人员专注于程序的代码书写而无需关注内存管理。

QUIC在京东直播的应用与实践

本文将分别从推流端、中台源站、直播云CDN及播放端四个部分串烧式地介绍与直播相关的一些技术实践,并重点介绍QUIC技术的应用情况及收益。

硬件开发笔记(十七):RK3568底板电路串口、485、usb原理图详解

前言 原理图有一些常用电路。 本篇就将集中常用电路分析完,如uart口,涉及usart串口、rs485、usb口。 串口 串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点

NET工控,上位机,Modbus485网口/串口通讯(鸣志步进电机,鸣志伺服电机,松下伺服电机,华庆军继电器模块)

先上两个通用Modbus帮助类,下面这个是多线程不安全版,在多线程多电机同一端口通信下,可能造成步进电机丢步或者输出口无响应等,还有个多线程安全版,只是基于这个不安全版加上了LOCK,THIS using Modbus.Device; using Sunny.UI; using System; us

基于WebSocket的modbus通信(三)- websocket和串口

WebSocket传递ModbusTCP数据包 错误纠正 上一篇还有个错误,就是客户端写数据时服务端不需要响应,但我的服务端响应了的。我选择改客户端,把写数据时接收到的响应丢弃。 PrintBytes(ADUMessage.Serialze(request), "请求"); if (Client !

【单片机入门】(三)应用层软件开发的单片机学习之路-----UART串口通讯和c#交互

引言 在第一章博客中,我们讲了Arduino对Esp32的一个环境配置,以及了解到了常用的一个总线通讯协议,其中有SPI,IIC,UART等,今天我为大家带来UART串口通讯和c#串口进行通讯的一个案例,以及什么是中断,中断的作用和实践,话不多说,让我们正式开始。 UART 在第一篇博客中,我们讲了

.NET周报【1月第1期 2023-01-06】

国内文章 【开源】基于.net6+gtksharp实现的Linux下的图形界面串口调试工具 https://www.cnblogs.com/flykai/p/17007554.html 由于公司的上位机等硬件项目都是基于Linux操作系统的,软硬件通讯调试麻烦,市面上又没有好用的图形操作软件来实现便

全网最适合入门的面向对象编程教程:18 类和对象的 Python 实现-多重继承与 PyQtGraph 串口数据绘制曲线图

本文主要介绍了Python中创建自定义类时如何使用多重继承、菱形继承的概念和易错点,同时讲解了如何使用PyQtGraph库对串口接收的数据进行绘图。