uart串口通信传输协议
一、Uart串口通信
uart串口通信是一种异步串行全双工通信方式,tx端用于数据发送;rx端用于数据接收。信号线在空闲时为高电平。
异步通信是按字符传输的。每传输一个字符就用起始位来收、发双方的同步。不会因收发双方的时钟频率的小的偏差导致错误。这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔时随即的。接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束。uart也是异步通信方式,数据发送会包装成数据帧的形式发送,数据帧的格式为:
空闲时间为高电平,故rx端接收到低电平时,表示有数据开始发送,再根据波特率对数据进行接收。波特率:每秒传输二进制数据的位数,单位bps。
若波特率为115200bps,即代表一秒钟需要传输115200个bit数据。
1s=10e9ns,50MHz时钟周期为20ns,则传输一个bit所需的时间为
10e9/115200=8681ns
则传输一个bit需要的时钟周期为8681/20=434个时钟周期。
数据位的传输是串行从低位到高位传输,接收到的数据暂时存储在寄存器中,待接收完1字节的数据,通过串并转换保存接收到的数据。发送时通过tx信号线按照设置好的比特率将数据发送出去,数据发送仍要按照数据帧发送,先发送起始位,再从低位到高位发送数据。
奇偶校验位:数据位加上校验位后,使得“1”的位数为偶数(偶校验)或者奇数(奇校验)。(一般都是无奇偶校验位的。)
停止位:数据位传输完成后都会发送停止位,标志一个传输已经完成。(默认1位,可选择1.5位、2位。)
二、Verilog 代码
1.发送模块
module uart_tx(input clk, //系统时钟input rst_n, //系统复位信号input [2:0]baud_set, //波特率选择信号input [7:0]data_byte, //并行数据input send_en, //发送使能信号output reg rs_Tx, //发送串行数据output reg tx_state, //uart正在发送信号 output reg tx_done //发送完成信号
);reg [15:0]baud_temp; //波特率reg [15:0]baud_cnt; //波特率分频器reg baud_clk; //波特率时钟reg [3:0]baud_clk_cnt; //波特率时钟计数器//波特率选择
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_temp <= 3'd0;else begincase(baud_set)3'd0:baud_temp <= 16'd5207; //波特率9600bps3'd1:baud_temp <= 16'd2603; //波特率19200bps3'd2:baud_temp <= 16'd1301; //波特率38400bps3'd3:baud_temp <= 16'd867; //波特率57600bps3'd4:baud_temp <= 16'd433; //波特率115200bpsdefault: baud_temp <= 16'd5207;endcaseend
end//波特率分频计数器
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt <= 16'd0;else if(tx_state)begin //开始发送后波特率分频计数器开始工作if(baud_cnt == baud_temp)baud_cnt <= 16'd0;elsebaud_cnt <= baud_cnt + 1'b1;endelsebaud_cnt <= 16'd0;
end//生成波特率时钟
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_clk <= 1'b0;else if(baud_cnt == 1'b1)baud_clk <= 1'b1;elsebaud_clk <= 1'b0;
end//对波特率时钟进行计数
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_clk_cnt <= 4'd0;else if(baud_clk_cnt == 4'd11)baud_clk_cnt <= 4'd0;else if(baud_clk)baud_clk_cnt <= baud_clk_cnt + 1'b1;elsebaud_clk_cnt <= baud_clk_cnt;
end//发送完成标志
always @(posedge clk or negedge rst_n)beginif(!rst_n)tx_done <= 1'b0;else if(baud_clk_cnt == 4'd11) //波特率计数器记到4'd11时,发送完成tx_done <= 1'd1;elsetx_done <= 1'd0;
end//正在发送标志
always @(posedge clk or negedge rst_n)beginif(!rst_n)tx_state <= 1'b0;else if(send_en) //发送使能拉高时,发送开始tx_state <= 1'b1;else if(baud_clk_cnt == 4'd11)tx_state <= 1'd0;elsetx_state <= tx_state;
end//数据寄存reg [7:0]data_byte_r;
always @(posedge clk or negedge rst_n)beginif(!rst_n)data_byte_r <= 8'd0;else if(send_en)data_byte_r <= data_byte;elsedata_byte_r <= data_byte_r;
end//并行数据转变成串行发送
always @(posedge clk or negedge rst_n)beginif(!rst_n)rs_Tx <= 1'b0;else begincase(baud_clk_cnt)0:rs_Tx <= 1'b1;1:rs_Tx <= 1'b0;2:rs_Tx <= data_byte_r[0];3:rs_Tx <= data_byte_r[1];4:rs_Tx <= data_byte_r[2];5:rs_Tx <= data_byte_r[3];6:rs_Tx <= data_byte_r[4];7:rs_Tx <= data_byte_r[5];8:rs_Tx <= data_byte_r[6];9:rs_Tx <= data_byte_r[7];10:rs_Tx <= 1'b1;endcaseend
endendmodule
`timescale 1ns/1ps
`define clk_period 20
module uart_tx_tb();reg clk;reg rst_n;reg [2:0]baud_set;reg [7:0]data_byte;reg send_en;wire rs_Tx;wire tx_state;wire tx_done;uart_tx u1(.clk(clk),.rst_n(rst_n),.baud_set(baud_set),.data_byte(data_byte),.send_en(send_en),.rs_Tx(rs_Tx),.tx_state(tx_state),.tx_done(tx_done));initial clk = 0;always #(`clk_period/2)clk = ~clk;initial beginrst_n = 1'b0;data_byte = 8'd0;send_en = 1'b0;baud_set = 3'd4;#(`clk_period*20+1)rst_n = 1'b1;#(`clk_period*50);data_byte = 8'hae;send_en = 1'b1;#`clk_period;send_en = 0;@(posedge tx_done)#(`clk_period*5000);data_byte = 8'hbc;send_en = 1'b1;#`clk_period;send_en = 1'b0;@(posedge tx_done)#(`clk_period*5000);$stop;
end
endmodule
仿真图
2.接收模块
module uart_rx(input clk, //系统时钟input rst_n, //系统复位信号input rs_rx, //输入接收到的串行数据input reg[2:0]baud_set, //波特率选择信号output reg[7:0]r_data_byte, //输出并行数据output reg rx_done //接收完成信号);reg rs_rx_r1,rs_rx_r2; //同步寄存器reg rs_rx_temp1,rs_rx_temp2; //数据寄存器wire nedege; //判断起始信号//此时输入信号相对与系统时钟是异步信号,需要对其进行同步处理//同步寄存器,消除亚稳态
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrs_rx_r1 <= 1'b0;rs_rx_r2 <= 1'b0;endelse beginrs_rx_r1 <= rs_rx;rs_rx_r2 <= rs_rx_r1;end
end
//数据寄存
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrs_rx_temp1 <= 1'b0;rs_rx_temp2 <= 1'b0;endelse beginrs_rx_temp1 <= rs_rx_r2;rs_rx_temp2 <= rs_rx_temp1;end
endassign nedege = (!rs_rx_temp1) && rs_rx_temp2; //若为1,则输入了起始位/*实际传输中,会有许多干扰,只采样一次的数据是很不可靠的。这里将每个数据平均分为16段,采样中间6段较为平稳的数据,进行累加,1-3'b001,2-3'b010,3-3'b011,4-3'b100,5-3'b101,6-3'b110.可见当采样数据有一半的状态为1时,最高位都为1,故以最高位来判断此时传输的数据
*/reg [15:0]baud_temp; //波特率reg [15:0]baud_cnt; //波特率分频计数器reg baud_clk; //波特率时钟reg [7:0]baud_clk_cnt; //波特率时钟计数器//波特率选择
//相比较发送模式的采样频率,接收模式的采样频率是其的16倍
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_temp <= 16'd324;else begincase(baud_set)0:baud_temp <= 16'd324;1:baud_temp <= 16'd162;2:baud_temp <= 16'd80;3:baud_temp <= 16'd53;4:baud_temp <= 16'd26;default : baud_temp <= 16'd324;endcaseend
endreg rx_state; //正在传输信号//正在传输数据时baud_cnt开始计数
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt <= 16'd0;else if(rx_state)beginif(baud_cnt == baud_temp)baud_cnt <= 16'd0;elsebaud_cnt <= baud_cnt + 1'b1;endelsebaud_cnt <= 16'd0;
end//
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_clk <= 1'b0;else if(baud_cnt == 16'd1)baud_clk <= 1'b1;elsebaud_clk <= 1'b0;
endreg [2:0]data_byte_r [7:0];reg [2:0]START_BIT,STOP_BIT;always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_clk_cnt <= 8'd0;else if(baud_clk_cnt == 8'd159 || ((baud_clk_cnt == 8'd12) && (START_BIT > 2)))//baud_clk_cnt计满时、或者是起始信号不为1时清零;baud_clk_cnt <= 8'd0;else if(baud_clk)baud_clk_cnt <= baud_clk_cnt + 1'b1;elsebaud_clk_cnt <= baud_clk_cnt;
end//传输完成信号
always @(posedge clk or negedge rst_n)beginif(!rst_n)rx_done <= 1'b0;else if(baud_clk_cnt == 8'd159)rx_done <= 1'b1;elserx_done <= 1'b0;
end//正在传输信号
always @(posedge clk or negedge rst_n)beginif(!rst_n)rx_state <= 1'b0;else if(nedege)rx_state <= 1'b1;else if(rx_done || (baud_clk_cnt == 8'd12 &&(START_BIT>2)))rx_state <= 1'b0;elserx_state <= rx_state;
end//计数完成时,串行数据转成并行数据always @(posedge clk or negedge rst_n)beginif(!rst_n)r_data_byte <= 8'b0;else if(baud_clk_cnt == 8'd159)beginr_data_byte[0] <= data_byte_r[0][2];r_data_byte[1] <= data_byte_r[1][2];r_data_byte[2] <= data_byte_r[2][2];r_data_byte[3] <= data_byte_r[3][2];r_data_byte[4] <= data_byte_r[4][2];r_data_byte[5] <= data_byte_r[5][2];r_data_byte[6] <= data_byte_r[6][2];r_data_byte[7] <= data_byte_r[7][2];endelse beginr_data_byte[0] <= data_byte_r[0];r_data_byte[1] <= data_byte_r[1];r_data_byte[2] <= data_byte_r[2];r_data_byte[3] <= data_byte_r[3];r_data_byte[4] <= data_byte_r[4];r_data_byte[5] <= data_byte_r[5];r_data_byte[6] <= data_byte_r[6];r_data_byte[7] <= data_byte_r[7];end
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginSTART_BIT <= 3'd0;data_byte_r[0] <= 3'd0;data_byte_r[1] <= 3'd0;data_byte_r[2] <= 3'd0;data_byte_r[3] <= 3'd0;data_byte_r[4] <= 3'd0;data_byte_r[5] <= 3'd0;data_byte_r[6] <= 3'd0;data_byte_r[7] <= 3'd0;endelse if(baud_clk)begincase(baud_clk_cnt)0:beginSTART_BIT <= 3'd0;data_byte_r[0] <= 3'd0;data_byte_r[1] <= 3'd0;data_byte_r[2] <= 3'd0;data_byte_r[3] <= 3'd0;data_byte_r[4] <= 3'd0;data_byte_r[5] <= 3'd0;data_byte_r[6] <= 3'd0;data_byte_r[7] <= 3'd0;STOP_BIT <= 3'd0;end6,7,8,9,10,11:START_BIT <= START_BIT + rs_rx_r2;22,23,24,25,26,27:data_byte_r[0] <= data_byte_r[0] + rs_rx_r2;38,39,40,41,42,43:data_byte_r[1] <= data_byte_r[1] + rs_rx_r2;54,55,56,57,58,59:data_byte_r[2] <= data_byte_r[2] + rs_rx_r2;70,71,72,73,74,75:data_byte_r[3] <= data_byte_r[3] + rs_rx_r2;86,87,88,89,90,91:data_byte_r[4] <= data_byte_r[4] + rs_rx_r2;102,103,104,105,106,107:data_byte_r[5] <= data_byte_r[5] + rs_rx_r2;118,119,120,121,122,123:data_byte_r[6] <= data_byte_r[6] + rs_rx_r2;134,135,136,137,138,139:data_byte_r[7] <= data_byte_r[7] + rs_rx_r2;150,151,152,153,154,155:STOP_BIT <= STOP_BIT + rs_rx_r2;default : beginSTART_BIT <= START_BIT;data_byte_r[0] <= data_byte_r[0];data_byte_r[1] <= data_byte_r[1];data_byte_r[2] <= data_byte_r[2];data_byte_r[3] <= data_byte_r[3];data_byte_r[4] <= data_byte_r[4];data_byte_r[5] <= data_byte_r[5];data_byte_r[6] <= data_byte_r[6];data_byte_r[7] <= data_byte_r[7];STOP_BIT <= STOP_BIT;endendcaseend
endendmodule
`timescale 1ns/1ps
`define clk_period 20module uart_rx_tb();reg clk;reg rst_n;reg send_en;reg [2:0]baud_set;reg rs_rx;wire rs_Tx;wire tx_state;wire tx_done;wire rx_done;reg [7:0]data_byte; wire [7:0]r_data_byte;uart_tx u1(.clk(clk),.rst_n(rst_n),.baud_set(baud_set),.data_byte(data_byte),.send_en(send_en),.rs_Tx(rs_Tx),.tx_state(tx_state),.tx_done(tx_done));uart_rx u2(.clk(clk),.rst_n(rst_n),.rs_rx(rs_Tx),.baud_set(baud_set),.r_data_byte(r_data_byte),.rx_done(rx_done));initial clk = 1;always #(`clk_period/2)clk = ~clk;initial beginrst_n = 1'b0;data_byte <= 8'd0;send_en = 1'b0;baud_set = 4'd4;#(`clk_period*20+1);rst_n = 1'b1;#(`clk_period*500);data_byte = 8'haa;send_en = 1;#(`clk_period);send_en = 0;@(posedge tx_done)#(`clk_period*500);data_byte = 8'h55;send_en = 1;#(`clk_period);send_en = 0;@(posedge tx_done)#(`clk_period*5000);$stop;end
endmodule
仿真图
uart串口通信传输协议相关推荐
- UART串口通信常用协议对比——rs232、485的区别
一.RS232:uart常用的一个协议,uart其实只是一个设备,而协议则是规定在传输时他的传输规则,232协议简单常用的一种协议,接受端和发送端的rxd与txd相连,这样就可以完成传输.因为每端都有 ...
- UART串口通信浅谈之(一)--基础概述
通信按照传统的理解就是信息的传输与交换.UART(Universal Asynchronous Receiver/Transmitter,即通用异步收发器)串行通信是单片机最常用的一种通信技术,通常用 ...
- 单片机:11.UART串口通信
原文地址:https://blog.csdn.net/Qingzhusshuiyun/article/details/78236798 通信按照传统的理解就是信息的传输与交换.对于单片机来说,通信则与 ...
- RS232与UART串口通信
通信,按照传统的理解就是信息的传输与交换.对于单片机来说,通信则与传感器.存储芯片.外围控制芯片等技术紧密结合,成为整个单片机系统的"神经中枢".没有通信,单片机所实现的功能仅仅局 ...
- 【FPGA】八、UART串口通信
文章目录 前言 一.UART简介 1.基本概念 2.UART协议 3.波特率简介 二.UART串口回环实验 1.设计思路 2.程序代码 ① 串口接收模块 ② 串口发送模块 ③ 串口顶层模块 ④ 串口仿 ...
- 基于FPGA Uart串口通信实验
基于FPGA Uart串口通信实验 首先需要了解uart串口通信协议,根据个人专业需求不同,了解的层面可以不同. UART简介 通用异步收发传输器(Universal Asynchronous Rec ...
- 51单片机入门——UART串口通信
文章目录 前言 1.什么是串行通信 2. USB转串口通信 3. IO 口模拟 UART 串口通信 4 UART串口通信的基本应用 4.1 通信的三种类型 4.2 UART模块 4.3 UART 串口 ...
- 【FPGA】UART串口通信
目录 前言 一丶通信方式 1.串行通信 2.并行通信 二丶UART 串口通信 三丶模块设计 四丶发送模块 1.代码 2.仿真 五丶接收模块 1.代码 2.仿真 六丶顶层模块 1.代码 2.模块原理图 ...
- NXP(I.MX6uLL) UART串口通信原理————这个未复习
参考:Linux NXP (I.MX6uLL) UART串口通信原理 作者:一只青木呀 发布时间: 2020-09-20 16:48:33 网址:https://blog.csdn.net/weixi ...
最新文章
- jquery判断方法是否存在_判断图中是否有环的三种方法
- 做图形处理Linux小型主机,8个优秀的linux图形图像工具
- 250g硬盘linux分区,linux分区格式化命令使用方法
- 支持向量机(SVM)的约束和无约束优化、理论和实现
- mysql dba系统学习(21)mysql存储引擎InnoDB
- 2018年学员信息系统项目管理师备考经验
- 安装一直初始化_3D max 软件安装问题大全
- 高并发处理方案_高并发系统下的缓存解决方案
- ai电磁组属于什么组_飞思卡尔智能车电磁组分区算法介绍
- oracle trigger 延迟执行_springboot中定时任务执行Quartz的使用
- WEB前端性能优化基本套路
- java new char 初始化_java考试复习
- springboot框架(2):整合junit4
- 巴菲特致股东的一封信:1991年
- 项目中git的运用及命令明细
- [每日一氵] 正则表达式以x开头,以x结尾的字符串
- itext修改pdf文字
- ONFI ZQ Calibration
- 百家企业短信网关(背景及核心代码)-1-同时对接多家短信公司的开源免费代码
- iCop-Ratel执行run_local.sh文件报错