uart串口通信传输协议

uart串口通信传输协议uart 串口通信是一种异步串行全双工通信方式 tx 端用于数据发送 rx 端用于数据接收 信号线在空闲时为高电平 异步通信是按字符传输的 每传输一个字符就用起始位来收 发双方的同步 不会因收发双方的时钟频率的小的偏差导致错误 这种传输方式利用每一帧的起 止信号来建立发送与接收之间的同步 特点是 每帧内部各位均采用固定的时间间隔 而帧与帧之间的间隔时随即的 接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束 uart 也是异步通信方式 数据发送会包装成数据帧的形式发送 数据帧的格

一、Uart串口通信

        uart串口通信是一种异步串行全双工通信方式,tx端用于数据发送;rx端用于数据接收。信号线在空闲时为高电平。

        异步通信是按字符传输的。每传输一个字符就用起始位来收、发双方的同步。不会因收发双方的时钟频率的小的偏差导致错误。这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔时随即的。接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束。uart也是异步通信方式,数据发送会包装成数据帧的形式发送,数据帧的格式为:

uart串口通信传输协议

        空闲时间为高电平,故rx端接收到低电平时,表示有数据开始发送,再根据波特率对数据进行接收。波特率:每秒传输二进制数据的位数,单位bps。

                若波特率为bps,即代表一秒钟需要传输个bit数据。

                1s=10e9ns,50MHz时钟周期为20ns,则传输一个bit所需的时间为

                                10e9/=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)begin if(!rst_n) baud_temp <= 3'd0; else begin case(baud_set) 3'd0:baud_temp <= 16'd5207; //波特率9600bps 3'd1:baud_temp <= 16'd2603; //波特率19200bps 3'd2:baud_temp <= 16'd1301; //波特率38400bps 3'd3:baud_temp <= 16'd867; //波特率57600bps 3'd4:baud_temp <= 16'd433; //波特率bps default: baud_temp <= 16'd5207; endcase end end //波特率分频计数器 always @(posedge clk or negedge rst_n)begin if(!rst_n) baud_cnt <= 16'd0; else if(tx_state)begin //开始发送后波特率分频计数器开始工作 if(baud_cnt == baud_temp) baud_cnt <= 16'd0; else baud_cnt <= baud_cnt + 1'b1; end else baud_cnt <= 16'd0; end //生成波特率时钟 always @(posedge clk or negedge rst_n)begin if(!rst_n) baud_clk <= 1'b0; else if(baud_cnt == 1'b1) baud_clk <= 1'b1; else baud_clk <= 1'b0; end //对波特率时钟进行计数 always @(posedge clk or negedge rst_n)begin if(!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; else baud_clk_cnt <= baud_clk_cnt; end //发送完成标志 always @(posedge clk or negedge rst_n)begin if(!rst_n) tx_done <= 1'b0; else if(baud_clk_cnt == 4'd11) //波特率计数器记到4'd11时,发送完成 tx_done <= 1'd1; else tx_done <= 1'd0; end //正在发送标志 always @(posedge clk or negedge rst_n)begin if(!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; else tx_state <= tx_state; end //数据寄存 reg [7:0]data_byte_r; always @(posedge clk or negedge rst_n)begin if(!rst_n) data_byte_r <= 8'd0; else if(send_en) data_byte_r <= data_byte; else data_byte_r <= data_byte_r; end //并行数据转变成串行发送 always @(posedge clk or negedge rst_n)begin if(!rst_n) rs_Tx <= 1'b0; else begin case(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; endcase end end endmodule 
`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 begin rst_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

仿真图

uart串口通信传输协议

 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)begin if(!rst_n)begin rs_rx_r1 <= 1'b0; rs_rx_r2 <= 1'b0; end else begin rs_rx_r1 <= rs_rx; rs_rx_r2 <= rs_rx_r1; end end //数据寄存 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin rs_rx_temp1 <= 1'b0; rs_rx_temp2 <= 1'b0; end else begin rs_rx_temp1 <= rs_rx_r2; rs_rx_temp2 <= rs_rx_temp1; end end assign 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)begin if(!rst_n) baud_temp <= 16'd324; else begin case(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; endcase end end reg rx_state; //正在传输信号 //正在传输数据时baud_cnt开始计数 always @(posedge clk or negedge rst_n)begin if(!rst_n) baud_cnt <= 16'd0; else if(rx_state)begin if(baud_cnt == baud_temp) baud_cnt <= 16'd0; else baud_cnt <= baud_cnt + 1'b1; end else baud_cnt <= 16'd0; end // always @(posedge clk or negedge rst_n)begin if(!rst_n) baud_clk <= 1'b0; else if(baud_cnt == 16'd1) baud_clk <= 1'b1; else baud_clk <= 1'b0; end reg [2:0]data_byte_r [7:0]; reg [2:0]START_BIT,STOP_BIT; always @(posedge clk or negedge rst_n)begin if(!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; else baud_clk_cnt <= baud_clk_cnt; end //传输完成信号 always @(posedge clk or negedge rst_n)begin if(!rst_n) rx_done <= 1'b0; else if(baud_clk_cnt == 8'd159) rx_done <= 1'b1; else rx_done <= 1'b0; end //正在传输信号 always @(posedge clk or negedge rst_n)begin if(!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; else rx_state <= rx_state; end //计数完成时,串行数据转成并行数据 always @(posedge clk or negedge rst_n)begin if(!rst_n) r_data_byte <= 8'b0; else if(baud_clk_cnt == 8'd159)begin r_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]; end else begin r_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 end always @(posedge clk or negedge rst_n)begin if(!rst_n)begin START_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; end else if(baud_clk)begin case(baud_clk_cnt) 0:begin START_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; end 6,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 : begin START_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; end endcase end end endmodule
`timescale 1ns/1ps `define clk_period 20 module 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 begin rst_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串口通信传输协议

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/218401.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午7:21
下一篇 2026年3月18日 上午7:21


相关推荐

  • CentOS7 安装 Python 3.9.0[通俗易懂]

    CentOS7 安装 Python 3.9.0[通俗易懂]文章目录1.安装编译相关工具2.创建Python文件夹下载安装包3.编译安装4.创建软连接5.验证1.安装编译相关工具安装开发库yum-ygroupinstall”Developmenttools”安装依赖环境yum-yinstallzlibzlib-develbzip2-developenssl-develncurses-develsqlite-develreadline-develtk-develgdbm-develdb4-devel

    2026年3月7日
    6
  • 串口服务器调试助手使用教程,comassistant串口调试助手使用说明.pdf

    串口服务器调试助手使用教程,comassistant串口调试助手使用说明.pdf作者:温子祺wenziqi@wenziqi@单片机多功能调试助手简介单片机多功能调试助手简介单单片片机机多多功功能能调调试试助助手手简简介介1111简介图1单片机多功能调试助手单片机多功能调试助手一款集串口/USB/网络调试、进制转换、字模与数码管字型码制作、常用校验值计算、UNICODE码转换、位图输出C文件等众多功能于一身的综合型调试软件,最值得庆幸的是该软件会一直保持更新,并支持在…

    2022年6月12日
    34
  • pycharm如何安装python环境_pycharm怎么安装「建议收藏」

    pycharm如何安装python环境_pycharm怎么安装「建议收藏」安装方法:1、安装配置好Python环境;2、从官网下载pycharm安装程序;3、直接双击下载好的exe文件,进入安装向导界面,按照指示一步步操作;4、点击Install进行安装,等待安装完成后,点击Finish结束安装即可。本教程操作环境:windows7系统、Python3.5.2版本、DellG3电脑。首先我们来安装python1、首先进入网站下载:点击打开链接(或自己输入网址http…

    2022年8月27日
    12
  • cutoff激活成功教程版下载_cutout

    cutoff激活成功教程版下载_cutout000000013F67F64|E831680500         |callcutout.13F6D5E80                  |000000013F67F64|4C8D0DCAF33C00   |lear9,qwordptrds:[13FA4EA20]        |;13FA4EA20:L”menuicon.png”

    2025年12月3日
    8
  • Java8新特性学习之一:lambda表达式入门

    Java8新特性学习之一:lambda表达式入门

    2021年8月3日
    74
  • strsep的用法

    strsep的用法一 出现段错误看下面两种写法 1 nbsp include includestruc nbsp nbsp nbsp charname 32 nbsp nbsp nbsp char value nbsp nbsp nbsp structitem next intmain nbsp nbsp nbsp char channel start end nbsp nbsp nbsp structitemte

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号