UART协议及串口回环

article/2025/9/16 23:17:24

UART协议及串口回环

      • 一、异步通信的分类
        • 1、UART(通用异步收发器)
        • 2、RS422
        • 3、RS485
        • 4、Modbus
        • 5、接口标准
      • 二、UART协议要求
        • 1、空闲状态
        • 2、起始位
        • 3、数据位
        • 4、校验位
        • 5、停止位
        • 6、波特率
        • 7、比特率
      • 三、汉字发送
      • 四、串口回环
        • uart_tx
        • uart_rx
        • ctrl
        • top
      • 五、参考
      • 六、源码

一、异步通信的分类

1、UART(通用异步收发器)

一般是指元器件之间的通信,用一位低电平作为起始位,再来8位数据位后面加校验和高电平的停止位,信号一般对地电压5V、3.3V等等都可以,这就是uart通信,

2、RS422

后来人们发现这种通信方案传输距离不长,于是将这种电平信号改成差分信号,每个差分信号需要两条线,那rx和tx就一共需要四条线,不需要gnd了,这种通信就叫RS422,

3、RS485

但人们发现一般情况下发送和接收很少一起于是就掐掉一路变成一对差分,这就是RS485。

4、Modbus

在实践中人们又发现在这每家都通信协议都不一样很难集成到一起,于是就统一定义一下发送的协议,每个设备有一个地址,在一个网络中主机首先发送从机地址然后再发送功能码字节长度和实际数据以及CRC校验,这样不仅稳定而且还能兼容很多产品,于是modbus就诞生了,也就是说modbus是建立在uart或者422或者485之上的一种协议,其实modbus还可以建立在tcp/ip上面

5、接口标准

在这里插入图片描述

二、UART协议要求

1、空闲状态

保持高电平

2、起始位

master设备由空闲状态的高电平拉为低电平,且持续一个bit数据的保持时间(跟波特率有关),用于告知slave设备准备接受数据

3、数据位

支持5~8bit的有效数据传输,由通信双方约定好通信的格式。先发送数据的低位,LSB表示低位,MSB表示高位
格式:起始位,bit0~bit7,校验位,停止位

4、校验位

用于数据校验的,校验的方式分为奇偶校验,校验位的作用是平衡数据中高电平数据的个数是奇数个/偶数个。
缩位异或(^A) 用于判断1的个数,0:偶数 1:奇数
缩位同或(~^A) 用于判断0的个数,0:奇数 1:偶数
奇校验(odd):保证数据(数据位+校验位)中奇数个逻辑高电平
偶校验(even):保证数据中偶数个逻辑高电平
例如:传输的数据为0100_0011
奇校验的校验位:0
偶校验的校验位:1

5、停止位

两台设备通信可能会存在小段时间的不同步情况,从而影响数据的传输结果,因此master必须保证有停止位,即数据线拉高一段时间。停止位不仅仅表示数据传输结束,也是矫正时钟同步的机会,让slave设备能够正确的识别下一轮数据的起始位。

6、波特率

码元:携带数据信息的信号单元(uart是单根线传输,所以码元就是一个二进制数)
波特率:每秒钟信道传输的码元数(二进制数据),符号Baud,单位波特每秒(Bps)
计算方法:50M时钟,周期为20ns
(1_000_000_000/115200)/20 = 434
传输1bit信号,在115200的波特率下,需要持续434个时钟周期

7、比特率

比特率:每秒钟信道传输的信息量,单位每秒比特数(bps)
公式:比特率 = 波特率*码元(对于一次传输一个bit数据而言,波特率=比特率)

三、汉字发送

UART每次只能发送8bit的数据,发送汉字信息需要连续发送16bit的信息
(一个汉字2个字节)发送的时ASCII码,需要使用软件查询汉字的ASCII码。发送汉字,先发送汉字对应16bit的ASCII码中的高位8bit,再发送低8bit

四、串口回环

uart_tx

`include "param.v"module  uart_tx(       //发送数据  并串转换input               clk     ,input               rst_n   ,input   [1:0]       baud_sel,input   [7:0]       din     ,input               din_vld ,output              tx_dout ,output              tx_busy  //发送状态指示   
);//信号定义reg     [12:0]      cnt0        ;//波特率计数器wire                add_cnt0    ;wire                end_cnt0    ;reg     [3:0]       cnt1        ;//bit计数器wire                add_cnt1    ;wire                end_cnt1    ;reg                 add_flag    ;reg     [`DATA_W+1:0] tx_data   ;reg     [12:0]      baud        ;//选择分频系数reg                 tx_bit      ;//计数器always @(posedge clk or negedge rst_n) begin if (rst_n==0) begincnt0 <= 0; endelse if(add_cnt0) beginif(end_cnt0)cnt0 <= 0; elsecnt0 <= cnt0+1 ;endendassign add_cnt0 = (add_flag);assign end_cnt0 = add_cnt0  && cnt0 == (baud)-1 ;always @(posedge clk or negedge rst_n) begin if (rst_n==0) begincnt1 <= 0; endelse if(add_cnt1) beginif(end_cnt1)cnt1 <= 0; elsecnt1 <= cnt1+1 ;endendassign add_cnt1 = (end_cnt0);assign end_cnt1 = add_cnt1  && cnt1 == (`DATA_W)+1;//add_flag  计数器使能信号always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginadd_flag <= 1'b0;endelse if(din_vld)beginadd_flag <= 1'b1;endelse if(end_cnt1)begin add_flag <= 1'b0;endendalways  @(*)begincase(baud_sel)0:baud = `BAUD_RATE_115200;1:baud = `BAUD_RATE_57600;2:baud = `BAUD_RATE_38400;3:baud = `BAUD_RATE_9600;default:baud = `BAUD_RATE_115200;endcase end//输出`ifdef PARITY_ENABLE    //使能校验功能//reg     [`DATA_W+1:0] tx_data     ;wire            parity_bit  ;assign parity_bit = (`PARITY_TYPE == 1'b1) ? (~^din) : (^din);always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begintx_data <= 0;endelse if(din_vld)begin   //收到请求时,将 停止位 校验位 数据 起始位 拼接tx_data <= {1'b1,parity_bit,din,1'b0};endend`else      //未使能校验功能//reg     [`DATA_W+1:0] tx_data     ;always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begintx_data <= 0;endelse if(din_vld)begin   //收到请求时,将 校验位 数据 起始位 拼接tx_data <= {1'b1,din,1'b0};endend`endif always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begintx_bit <= 1'b1;endelse if(add_cnt0 && cnt0 == 1-1)begintx_bit <= tx_data[cnt1];endendassign tx_busy = din_vld | add_flag;assign tx_dout = tx_bit;endmodule 

uart_rx

`include "param.v"module uart_rx (       //接收串行数据 串并转换input               clk     ,input               rst_n   ,input   [1:0]       baud_sel,input               rx_din  ,output  [7:0]       rx_dout ,output              rx_vld   
);//定义信号reg     [12:0]      cnt0        ;//波特率计数器wire                add_cnt0    ;wire                end_cnt0    ;reg     [3:0]       cnt1        ;//bit计数器wire                add_cnt1    ;wire                end_cnt1    ;reg                 add_flag    ;reg     [12:0]      baud        ;//选择分频系数reg     [2:0]       rx_din_r    ;//同步、打拍寄存器wire                n_edge      ;//下降沿检测reg     [`DATA_W:0] rx_data     ;//采样数据寄存器//计数器always @(posedge clk or negedge rst_n) begin if (rst_n==0) begincnt0 <= 0; endelse if(add_cnt0) beginif(end_cnt0)cnt0 <= 0; elsecnt0 <= cnt0+1 ;endendassign add_cnt0 = (add_flag);assign end_cnt0 = add_cnt0  && cnt0 == (baud)-1 ;always @(posedge clk or negedge rst_n) begin if (rst_n==0) begincnt1 <= 0; endelse if(add_cnt1) beginif(end_cnt1)cnt1 <= 0; elsecnt1 <= cnt1+1 ;endendassign add_cnt1 = (end_cnt0);assign end_cnt1 = add_cnt1  && (cnt1 == (`DATA_W) || rx_data[0]);//add_flag  计数器使能信号always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginadd_flag <= 1'b0;endelse if(n_edge)beginadd_flag <= 1'b1;endelse if(end_cnt1)beginadd_flag <= 1'b0;endendalways  @(*)begincase(baud_sel)0:baud = `BAUD_RATE_115200;1:baud = `BAUD_RATE_57600;2:baud = `BAUD_RATE_38400;3:baud = `BAUD_RATE_9600;default:baud = `BAUD_RATE_115200;endcase end//下降沿检测  同步打拍always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginrx_din_r <= 3'b111;endelse beginrx_din_r <= {rx_din_r[1:0],rx_din};//rx_din_r[0] <= rx_din;      //同步//rx_din_r[1] <= rx_din_r[0]; //打拍//rx_din_r[2] <= rx_din_r[1]; //打拍endendassign n_edge = rx_din_r[2] & ~rx_din_r[1];//采样数据always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginrx_data <= 0;endelse if(add_flag && cnt0 == (baud >> 1))begin//rx_data <= {rx_din,rx_data[`DATA_W:1]};//右移rx_data[cnt1] <= rx_din;endend//校验`ifdef PARITY_ENABLE       //开启校验wire        parity_result  ;assign parity_result = ^rx_data[`DATA_W:1];assign rx_dout = rx_data[`DATA_W-1:1];assign rx_vld  = end_cnt1 & ~rx_data[0] & parity_result == `PARITY_TYPE;`elsif          //不开启校验assign rx_dout = rx_data[`DATA_W:1];assign rx_vld  = end_cnt1 & ~rx_data[0];`endif //输出//    assign rx_dout = rx_data[8:1];
//    assign rx_vld  = end_cnt0 & cnt1 == (9)-1 & ~rx_data[0];endmodule 

ctrl

module control (     //缓存input               clk     ,input               rst_n   ,input   [7:0]       din     ,input               din_vld ,input               busy    ,output  [7:0]       dout    ,output              dout_vld    
);//信号定义reg                 rd_flag     ;reg     [7:0]       tx_data     ;reg                 tx_data_vld ;wire                fifo_rdreq  ;wire                fifo_wrreq  ;wire                fifo_empty  ;wire                fifo_full   ;wire    [7:0]       fifo_qout   ;wire    [3:0]       fifo_usedw  ;always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginrd_flag <= 1'b0;endelse if(fifo_usedw >= 8)beginrd_flag <= 1'b1;endelse if(fifo_empty)begin rd_flag <= 1'b0;end endalways  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begintx_data <= 0;tx_data_vld <= 1'b0; endelse begintx_data <= fifo_qout;tx_data_vld <= fifo_rdreq;endend//FIFO例化fifo u_fifo(.aclr   (~rst_n     ),.clock  (clk        ),.data   (din        ),.rdreq  (fifo_rdreq ),.wrreq  (fifo_wrreq ),.empty  (fifo_empty ),.full   (fifo_full  ),.q      (fifo_qout  ),.usedw  (fifo_usedw ));assign fifo_wrreq = din_vld & ~fifo_full;assign fifo_rdreq = rd_flag & ~busy;  //发送模块 非忙状态下 给发送请求//    assign dout_vld = fifo_rdreq;
//    assign dout = fifo_qout;assign dout_vld = tx_data_vld;  //时序逻辑输出assign dout = tx_data;endmodule 

top

module top(input               clk     ,input               rst_n   ,input               uart_rxd,output              uart_txd    
);//信号定义wire        [7:0]       rx_byte     ;wire                    rx_byte_vld ;wire        [7:0]       tx_byte     ;wire                    tx_byte_vld ;wire                    tx_busy     ;wire        [1:0]       baud_sel    ;assign baud_sel = 2'd0; //选择波特率//模块例化uart_rx u_rx(       //接收串行数据 串并转换/*input               */.clk     (clk           ),/*input               */.rst_n   (rst_n         ),/*input   [1:0]       */.baud_sel(baud_sel      ),/*input               */.rx_din  (uart_rxd      ),/*output  [7:0]       */.rx_dout (rx_byte       ),/*output              */.rx_vld  (rx_byte_vld   ) );control u_ctrl(     //缓存/*input               */.clk     (clk           ),/*input               */.rst_n   (rst_n         ),/*input   [7:0]       */.din     (rx_byte       ),/*input               */.din_vld (rx_byte_vld   ),/*input               */.busy    (tx_busy       ),/*output  [7:0]       */.dout    (tx_byte       ),/*output              */.dout_vld(tx_byte_vld   )    );uart_tx u_tx(       //发送数据  并串转换/*input               */.clk     (clk           ),/*input               */.rst_n   (rst_n         ),/*input   [1:0]       */.baud_sel(baud_sel      ),/*input   [7:0]       */.din     (tx_byte       ),/*input               */.din_vld (tx_byte_vld   ),/*output              */.tx_dout (uart_txd      ),/*output              */.tx_busy (tx_busy       ) //发送状态指示   );endmodule 

五、参考

UART的fpga实现

六、源码

https://github.com/IvanXiang/FPGA_UART


http://chatgpt.dhexx.cn/article/sTo5Sj1R.shtml

相关文章

基于verilog的uart协议实现

目录 1、理论介绍 2、架构设计 3、代码设计 一、发送模块代码 二、接收代码设计 三、顶层模块设计 四、测试代码 4、仿真实验 1、理论介绍 uart&#xff1a;通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;是一种串行的收发方式…

UART协议快速扫盲(图文并茂+超详细)

文章目录 1 UART发展历史1.1 早期的串行通讯设备1.2 早期的芯片级UART1.3 现代UART的发展 2 预备知识3 协议层起始位数据校验位停止位波特率 4 传输过程5 物理层6 优缺点 1 UART发展历史 1.1 早期的串行通讯设备 早期的电报机器使用长度可变的脉冲信号进行数据传输&#xff0…

UART协议学习

通信协议分层 物理层 物理层规定通讯系统具有的机械、电子功能部分的特性&#xff0c;确保原始数据在物理媒体的传输。如RS232、RS485等就是电气协议&#xff0c;规定了数据传输时的电平标准&#xff0c;网络上许多博主将UART看作一个协议族&#xff0c;这些电气协议都是UART…

c语言模拟uart协议的收发

这篇文章注重思想的讲解,理解下来肯定对uart协议有一个更深的认识。 uart协议,通常用在嵌入式设备之间的通信。像下面这样: 问题一:uart是全双工还是半双工? 你完全可以将两个设备想象成两个人,上图中的两条线想象成A和B的对话。A对B说话和B对A说话的一个场景。问大家一个问题…

UART协议详解

通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;通常称作UART。 定义&#xff1a;UART是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用于主…

Linux·UART协议

目录 一、什么是UART&#xff1f; 二、UART的帧格式 2.1 为什么UART的传输需要起始位&#xff1f; 2.2 UART基本的数据形式 2.3 为什么UART的数据位可变&#xff1f; 三、UART的波特率 3.1 什么是波特率 3.2 如何换算波特率 3.3 波特率和采样频率是一样的吗&#xff1f…

通信协议(一)——UART协议

1、知识点 基础部分参考&#xff1a;UART串口发送模块设计Verilog_发光中请勿扰的博客-CSDN博客_uart设计verilog &#xff08;1&#xff09;什么是串口&#xff08;UART&#xff09;&#xff1f; 串口作为常用的三大低速总线&#xff08;UART、SPI、IIC&#xff09;之一&#…

uart协议学习,从了解到入门,看这篇文章

uart协议从了解到入门 背景知识介绍&#xff1a;1、并行和串行的意思&#xff1a;2、串转并和并转串传输&#xff1a;3、单工、半双工、全双工区别&#xff1a; uart协议介绍1、uart简介2、uart通信3、uart工作原理 uart的优缺点 背景知识介绍&#xff1a; 1、并行和串行的意思…

FPGA实现uart协议

简介 使用verilog实现uart协议&#xff0c;能够和pc进行通信&#xff0c;实现串口回环功能&#xff0c;各参数设置如下&#xff1a; 波特率&#xff1a;115200数据位&#xff1a;8停止位&#xff1a;任意校验位&#xff1a;无 系统时钟为50M&#xff0c;115200波特率下&…

协议篇之UART协议

协议篇之UART协议 一、写在前面二、UART协议简介三、UART协议数据帧结构3.1 UART发送过程3.2 UART接收过程3.3 UART传输速率 四、UART收发模块设计4.1 UART接收模块设计4.2 UART发送模块设计4.3 UART回环顶层模块4.4 UART回环上板验证 五、写在最后 一、写在前面 由于设计需要&…

UART通信协议

UART通信协议 UART ( universal asynchronous receiver-transmitter&#xff09;是一种采用异步串行通信方式的通用异步收发传输器;它在发送数据时将并行数据转换成串行数据来传输&#xff0c;在接收数据时将接收到的串行数据转换成并行数据。UART串口通信需要两根信号线来实现…

UART协议讲解

UART协议讲解 一、什么是UART协议二、通信方式的分类1、串行通信&#xff08;一&#xff09;、同步通信和异步通信&#xff08;二&#xff09;、单工和半双工以及全双工 2、并行通信 三、UART协议具体介绍1、UART数据传输的格式2、UART电平标准 三、UART的优缺点四、参考 一、什…

【数字IC】深入浅出理解UART协议

深入浅出理解UART协议 一、什么是UART&#xff1f;二、UART的帧格式2.1 为什么UART的传输需要起始位&#xff1f;2.2 UART基本的数据形式2.3 为什么UART的数据位可变&#xff1f; 三、UART的波特率3.1 什么是波特率3.2 如何换算波特率3.3 波特率和采样频率是一样的吗&#xff1…

最详细的 UART协议 分析在这里!

1. 协议基础 1.1. 协议简介 UART是“Universal Asynchronous Receiver/Transmitter”&#xff0c;通用异步收发器的缩写。在19世纪60年代&#xff0c;为了解决计算机和电传打字机通信&#xff0c;Bell发明了UART协议&#xff0c;将并行输入信号转换成串行输出信号。因为U…

UART协议

UART协议 简介 UART是通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;通常称作UART&#xff0c;是一种异步收发传输器,是设备间进行异步通信的关键模块。UART负责处理数据总线和串行口之间的串/并、并/串转换&#xff0c;并规定了帧…

【云域网络社区】云域网络社区APP

【软件名称】云域社区 【功能介绍】资源分享&#xff0c;游戏交流交友 【下载地址】http://sss.shmmec.com/apk.apk 图片

怎么划分领域、子域、核心域、通用域和支撑域

怎么划分领域、子域、核心域、通用域和支撑域 如何理解领域和子域&#xff1f;什么是领域什么是子域怎么划分领域和子域如何理解核心域、通用域和支撑域&#xff1f;为什么要划分核心域、通用域和支撑域 总结 DDD 的知识体系提出了很多的名词&#xff0c;像&#xff1a;领域、子…

网络安全-域服务器(二)

域服务器&#xff1a; 域服务器&#xff08;一&#xff09;&#xff1a;网络安全-域&#xff08;一&#xff09;_IT之一小佬的博客-CSDN博客域服务器&#xff08;二&#xff09;&#xff1a;网络安全-域服务器&#xff08;二&#xff09;_IT之一小佬的博客-CSDN博客 域 11.O…

网课管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 3. 功能简介 用户中心 1.1用户注册&#xff1a;用户需要注册才能登陆进入web 1.2用户登录&#xff1a;通过判断匹配来进…

如何搭建域服务器

实验名称&#xff1a;如何搭建域服务器 实验环境&#xff1a;wind2003*2 实验过程&#xff1a; 由于Windows Server 2003在默认的安装过程中DNS是不被安装的&#xff0c;所以我们需要手动去添加&#xff0c; 添加方法如下:“开始—设置—控制面板—添加删除程序”&#xff…