1、知识点

基础部分参考:UART串口发送模块设计Verilog_发光中请勿扰的博客-CSDN博客_uart设计verilog

 (1)什么是串口(UART)?

串口作为常用的三大低速总线(UART、SPI、IIC)之一,在设计众多通信接口和调试时占有重要地位。串口(UART)全称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),主要用于数据间的串行传递,是一种全双工传输模式。它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

“异步”两个字即意味着在数据传递的两个模块之间使用的不是同步时钟。实际上在异步串口的传输中是不需要时钟的,而是通过特定的时序来标志传输的开始(起始位--由高到低)和结束(结束位,拉高)。

(2)串口的组成

UART 通信只有两根信号线,一根是发送数据端口线叫 tx(Transmitter),一根是接收数据端口线叫 rx(Receiver),如图所示,对于 PC 来说它的 tx 要和对于 FPGA来说的 rx 连接,同样 PC 的 rx 要和 FPGA 的 tx 连接,如果是两个 tx 或者两个 rx 连接那数据就不能正常被发送出去和接收到。

信号的传输由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有RS232、RS422、RS485等,它们定义了接口不同的电气特性,如RS-232是单端输入输 出,而RS-422/485为差分输入输出等。传输距离较短时(不超过15m),RS232是串行通信最常用的接口标准。RS-232标准的串口最常见的接口类型为DB9,样式如图所示,工业控制领域中用到的工控机一般都配备多个串口,很多老式台式机也都配有串口。但是笔记本电脑以及较新一点 的台式机都没有串口,它们一般通过USB转串口线来实现与外部设备的串口通信。

(3)RS232

 帧结构(10bit):

空闲状态保持高电平

UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。

校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。 奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时, 对1的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查 1的个数是否为偶数。

UART通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为5、6、7、8位,其中8位数据位是最常用的,在实际应用中 一般都选择8位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择1位(默认), 1.5或2位。

串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps(位 /秒),常用的波特率有9600、19200、38400、57600以及115200等。如波特率9600则代表每秒传输9600bit数据,以串口发送1个字节10bit算(起始位1bit+数据8bit+停止位1bit+NO校验位),则传输1个字节需要的时间是1*10/9600秒。

2、设计

(1)模块框图

顶层模块:

 子功能模块

串口数据接收模块:

串口数据发送模块:

整体框图:


( 2)功能实现

① 串口数据接收模块:

需要对rx数据进行打一拍,与系统时钟同步,但仍无法直接传送,因为该信号属于异步信号,会引出亚稳态。

亚稳态问题参考:亚稳态问题_发光中请勿扰的博客-CSDN博客

解决方法:对打拍后的数据再次打两拍

波形图:

波形:

 data输入

 打拍信号

使能信号 

 计数最大值

bit标志位

该模块支持任意波特率(理论上)的接收,但需要在使用该模块时使用参数将其例化,数据位8位,起始位和停止位各1位,无奇偶校验
        串口的传输是以起始位开始的,而起始位是将数据线拉低 ,所以我们需要捕捉数据线的下降沿,将接收数据线打拍两次,捕捉其下降沿。当捕捉到接收数据线的下降沿,拉高接收标志信号,标志模块进入接收过程;当接收完10个bit后,拉低接收标志信号,标志接收过程结束
        假设波特率为9600,则传输一个bit的时间为1s/9600,一个数据的传输共10bit(数据位8位,起始位和停止位各1位),则共需要1s/960;假设系统时钟为50MHz(参数化以便适应不同的系统频率),则其周期为20ns,那么传输一个bit所需要的系统周期数为(1s/960)/ 20ns ≈ 5208(个)。在接收过程中使用一个计数器计数,计数区间为(0,5208-1),这样的区间一共10个(一个字节需要传输10个bit);此外还需一个计数器对接收的bit数计数(每当上一个计数器计数到5207则表示接收完了一个bit),计数区间(0,9)。
        在接收过程,根据计数器的值(接收bit计数器),在每个bit计数器的中间接收数据,将其移位寄存(在电平中间数据最稳定)
        若接收bit计数器 = 0,则代表是起始位,不需要接收
        若接收bit计数器 = 1,则代表此时接收到数据的最低位LSB(数据的传输总是低位在前,高位在后),将其赋值给寄存数据的最低位;
        ······
        若接收bit计数器 = 8,则代表此时接收到数据的最高位MSB,将其赋值给寄存数据的最高位;
        若接收bit计数器 = 9,则代表是停止位,不需要接收

 代码:

module uart_rx
#(parameter uart_bps = 'd9600,//波特率parameter clk_fre  = 'd50_000_000//频率
)
(input sys_clk   ,input sys_rst_n       ,input rx       ,output reg [7:0]po_data    ,output reg po_flag
);parameter baud_cnt_max = clk_fre/uart_bps; //频率除以波特率reg rx_reg1                                      ;
reg rx_reg2                                     ;
reg rx_reg3                                     ;
reg start_flag                                  ;
reg work_en                                     ;
reg [15:0]baud_cnt                              ;
reg bit_flag                                    ;
reg [3:0]bit_cnt                                ;
reg [7:0]rx_data                                ;
reg rx_flag                                     ;//打一拍,同步到系统时钟下
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)rx_reg1 <= 1'b1;else rx_reg1 <= rx;//打两拍,消除亚稳态
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)rx_reg2 <= 1'b1;else rx_reg2 <= rx_reg1 ;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)rx_reg3 <= 1'b1;else rx_reg3 <= rx_reg2 ;//开始标志信号
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)start_flag <= 1'b0;else if (rx_reg2 == 1'b0 && rx_reg3 == 1'b1 && work_en == 1'b0)//引入使能信号是为了避免在数据传输时遇到1变为0,从而检验到下降沿start_flag <= 1'b1;elsestart_flag <= 1'b0;//使能信号
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)work_en <= 1'b0;else if(start_flag == 1'b1)work_en <= 1'b1;else if(bit_cnt == 4'd8 && bit_flag == 1'b1)work_en <= 1'b0;//bit计数器和bit标志信号elsework_en <= work_en;//波特率计数器
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)baud_cnt <= 16'd0;else if((baud_cnt == baud_cnt_max - 1 )  || (work_en == 1'b0))baud_cnt <= 16'd0;elsebaud_cnt <= baud_cnt + 1'b1;//bit标志信号
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)bit_flag <= 1'b0;else if(baud_cnt == baud_cnt_max / 2 - 1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;
//bit计数器
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)bit_cnt <= 4'd0;else if(bit_cnt == 4'd8 && bit_flag == 1'b1)bit_cnt <= 4'd0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;//数据拼接
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)rx_data <= 8'b0;else if(bit_cnt >= 4'd1 && bit_cnt <= 4'd8 && bit_flag == 1'b1)rx_data <= {rx_reg3,rx_data[7:1]};always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)rx_flag <= 1'b0;else if(bit_cnt == 4'd8 && bit_flag == 1'b1)rx_flag <= 1'b1;elserx_flag <= 1'b0;
//输出并行信号
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)po_data <= 8'b0;else if(rx_flag == 1'b1)po_data <= rx_data;
//对rx_flag进行打拍,保持与输出数据同步
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)po_flag <= 1'b0;else po_flag <= rx_flag;endmodule
`timescale 1ns/1ns  //定义时间刻度
//模块、接口定义
module uart_rx_tb();reg             sys_clk         ;
reg             sys_rst_n       ;
reg             rx              ;
wire    [7:0]   po_data         ;
wire            po_flag         ;initial begin  sys_clk <= 1'b0;   sys_rst_n <= 1'b0;rx <= 1'b1;#20sys_rst_n <= 1'b1;end
always #10 sys_clk <= ~sys_clk;initial begin #200rx_bit(8'd0);rx_bit(8'd1);rx_bit(8'd2);rx_bit(8'd3);rx_bit(8'd4);rx_bit(8'd5);rx_bit(8'd6);rx_bit(8'd7);end//例化发送模块
uart_rx
#(.uart_bps (9600),.clk_fre  (50_000_000)
)
uart_rx_inst
(.sys_clk(sys_clk)   ,.sys_rst_n(sys_rst_n),.rx(rx)     ,.po_data(po_data)  ,.po_flag(po_flag)
);task rx_bit (input [7:0] data
);integer i; //定义一个常量//用 for 循环产生一帧数据,for 括号中最后执行的内容只能写 i=i+1for(i=0; i<10; i=i+1) begincase(i)0: rx <= 1'b0;       //起始位1: rx <= data[0];      //LSB2: rx <= data[1];3: rx <= data[2];4: rx <= data[3];5: rx <= data[4];6: rx <= data[5];7: rx <= data[6];8: rx <= data[7];        //MSB9: rx <= 1'b1;        //停止位endcase#(5208*20);         //每发送 1 位数据延时end
endtask                             //任务结束endmodule 

 ②串口数据接收模块

波形图:

 波形:

模拟产生输入

 输入标志信号

 中间变量

 输出

module uart_tx
#(parameter uart_bps = 'd9600,parameter clk_fre  = 'd50_000_000
)
(input sys_clk          ,input sys_rst_n                ,input [7:0]pi_data     ,input pi_flag          ,output reg tx
);localparam baud_cnt_max = clk_fre/uart_bps;reg         work_en               ;
reg  [15:0] baud_cnt            ;
reg         bit_flag            ;
reg  [3:0]  bit_cnt             ;//使能信号
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)work_en <= 1'b0;else if(pi_flag == 1'b1)work_en <= 1'b1;else if(bit_cnt == 4'd09 &&  bit_flag == 1'b1)  work_en <= 1'b0;
//波特计数器
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)baud_cnt <= 16'd0;else if(work_en == 1'b0 || baud_cnt == baud_cnt_max - 1)baud_cnt <= 16'd0;else if(work_en == 1'b1)baud_cnt <= baud_cnt + 1'b1;
//bit标志位
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)bit_flag <= 1'b0;else if(baud_cnt == 16'd1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;
//bit计数器
always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)bit_cnt <= 4'd0;else if(bit_cnt == 4'd09 && bit_flag == 1'b1)bit_cnt <= 4'd0;else if(work_en == 1'b1 && bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)tx <= 1'b1;else if(bit_flag == 1'b1)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;endcaseendmodule    
`timescale 1ns/1ns  //定义时间刻度
//模块、接口定义
module uart_tx_tb();reg             sys_clk         ;
reg             sys_rst_n       ;
reg [7:0]       pi_data         ;
reg             pi_flag         ;wire           tx      ;initial begin  sys_clk <=1'b0;    sys_rst_n <=1'b0;                  #20                                         sys_rst_n <=1'b1;endalways #10 sys_clk = ~sys_clk;initial beginpi_data <= 8'd0;pi_flag <= 1'b0;#200//数据0pi_data <= 8'd0;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据1pi_data <= 8'd1;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据2pi_data <= 8'd2;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据3pi_data <= 8'd3;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据4pi_data <= 8'd4;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据5pi_data <= 8'd5;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据6pi_data <= 8'd6;pi_flag <= 1'b1;#20pi_flag <= 1'b0;#(5208*10*20)//数据7pi_data <= 8'd7;pi_flag <= 1'b1;#20pi_flag <= 1'b0;end//例化发送模块uart_tx
#(.uart_bps (9600),.clk_fre  (50_000_000)
)
uart_tx_inst
(.sys_clk   (sys_clk)       ,.sys_rst_n (sys_rst_n)     ,.pi_data   (pi_data)       ,.pi_flag   (pi_flag)       ,.tx            (tx)
);endmodule 

③顶层模块:

 波形:

rx数据接收(第一个拉高为数据得停止位)

 task验证

数据传递

//顶层
module rs232
(
//系统接口input                 sys_clk         ,           //50M系统时钟input              sys_rst_n       ,           //系统复位
//UART  input               rx,         //接收数据线output           tx                  //UART发送数据线
);//wire define
wire    [7:0]   rx_data ;                       //接收到的一个BYTE数据
wire            rx_flag ;                       //接收有效信号,可用作发送的使能信号//例化发送模块
uart_tx
#(.uart_bps (9600),.clk_fre  (50_000_000)
)
uart_tx_inst
(.sys_clk   (sys_clk)       ,.sys_rst_n (sys_rst_n)     ,.pi_data   (rx_data)       ,.pi_flag   (rx_flag)       ,.tx            (tx)
);
//例化接收模块
uart_rx
#(.uart_bps (9600),.clk_fre  (50_000_000)
)
uart_rx_inst
(.sys_clk(sys_clk)          ,.sys_rst_n(sys_rst_n)  ,.rx(rx)                    ,.po_data(rx_data)      ,.po_flag(rx_flag)
);
endmodule 
`timescale 1ns/1ns  //定义时间刻度
module rs232_tb();reg           sys_clk         ;
reg             sys_rst_n       ;
reg             rx              ;
wire            tx              ;initialbeginsys_clk <= 1'b0;  sys_rst_n <= 1'b0;rx <= 1'b1;#20sys_rst_n <= 1'b1;end
always #10 sys_clk <= ~sys_clk;initial begin#200rx_byte();  endtask rx_byte();integer j; //定义一个常量for(j=0; j<8; j = j +1) rx_bit(j);
endtasktask rx_bit (input [7:0] data
);integer i; //定义一个常量//用 for 循环产生一帧数据,for 括号中最后执行的内容只能写 i=i+1for(i=0; i<10; i=i+1) begincase(i)0: rx <= 1'b0;       //起始位1: rx <= data[0];      //LSB2: rx <= data[1];3: rx <= data[2];4: rx <= data[3];5: rx <= data[4];6: rx <= data[5];7: rx <= data[6];8: rx <= data[7];        //MSB9: rx <= 1'b1;        //停止位endcase#(5208*20);         //每发送 1 位数据延时end
endtask                             //任务结束rs232 rs232_inst
(
//系统接口.sys_clk(sys_clk)         ,           //50M系统时钟.sys_rst_n(sys_rst_n)      ,           //系统复位
//UART  .rx(rx)             ,           //接收数据线.tx(tx)                  //UART发送数据线
);endmodule

通信协议(一)——UART协议相关推荐

  1. 几种常用通信协议:IIC协议、SPI协议、UART协议

    通信可以形象的比喻成两个人讲话:1.你说的别人得能听懂:双方约定信号的协议.2.你的语速别人得能接受:双方满足时序要求. 一.IIC协议: 2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一 ...

  2. UART协议概述与实现

    UART协议概述(一) 协议描述 关键代码逻辑 写在最后 协议描述 常见的三大低速通信协议之一,UART,通用异步收发协议. 非常简单的协议,协议细节不需要多说,只说需要注意的点. 空闲位为高位,起始 ...

  3. UART项目验证(一) uart协议与uart ip的理解

    1.1 uart协议 uart(universal asynchronous reciver and transmitter):通用异步收发器,是一种通用串行数据总线,用于异步通信,将数据的二进制位一 ...

  4. UART协议驱动设计

    UART协议驱动设计 在不通信时,发送高电平. 发送信息时,应该首先发送起始位(1bit.低电平).可以理解为告诉接收方,应该接收信息了. 发送数据位,由于是串行通信,规定从低位开始发,最后到高位(协 ...

  5. 联网常见通信协议与通讯协议梳理- 通讯协议

    1  "通信"与"通讯"傻傻分得清 传统意义上的"通讯"主要指电话.电报.电传.通讯的"讯"指消息(Message),媒 ...

  6. 基础技术篇 10 ——物联网常见通信协议与通讯协议梳理【下】- 通信协议

    物联网常见通信协议与通讯协议梳理[下]- 通信协议 1  概述 在上一篇文章<物联网常见通信协议与通讯协议梳理[上]- 通讯协议>中,对物联网常用通信协议和通讯协议作了区分,并对通讯协议进 ...

  7. 通信协议之IIC协议(eeprom)_通俗易懂篇!

    1.IIC(Inter-Integrated Circuit),是IICBus简称,是一种串行通信总线. 2.IIC协议:遵循主机master和从机slave的主从关系机制,区别于SPI通信协议,II ...

  8. IOT(34 )---联网常见通信协议与通讯协议梳理- 通讯协议

    联网常见通信协议与通讯协议梳理[上]- 通讯协议 1  "通信"与"通讯"傻傻分得清 传统意义上的"通讯"主要指电话.电报.电传.通讯的&q ...

  9. Linux·UART协议

    目录 一.什么是UART? 二.UART的帧格式 2.1 为什么UART的传输需要起始位? 2.2 UART基本的数据形式 2.3 为什么UART的数据位可变? 三.UART的波特率 3.1 什么是波 ...

  10. verilog基础---uart协议解析

    UART协议详解 UART(Universal Asynchronous Receiver/Transmitter)是一种异步全双工串行通信协议,由Tx和Rx两根数据线组成,因为没有参考时钟信号,所以 ...

最新文章

  1. ajax post 参数说明
  2. (附下载地址)制作RPM包(星际译王词典包)
  3. 清除webbrowser cookie/session的6种方法
  4. Go的协程与Swoole的协程的区别:环境支持 线程
  5. 1732: 数花费(Kruscal)
  6. apache的es的原理_ElasticSearch原理
  7. Qt知识点汇总——来自网络
  8. 数字图像处理基础与应用 第五章
  9. linux添加硬盘永久挂载,linux新增硬盘如何挂载
  10. js多个(N)个数组的的元素组合排序算法,多维数组的排列组合或多个数组之间的排列组合...
  11. HDU 2176:取(m堆)石子游戏(Nim博弈)
  12. 重返opencv——视频编码和格式
  13. 高项_第六章项目进度管理
  14. 守护线程Deamon
  15. 陈桥五笔,我再也不会下载了
  16. 威联通TS-453Bmini NAS加装内存,轻松玩转虚拟机安装win10系统
  17. Zabbix - 微信报警
  18. 如何简单又快速的清理C盘内存
  19. 问题 J: 古罗马数字2
  20. matlab、python打字变红,格式错误问题

热门文章

  1. nextjs中阿里icon库的引入使用
  2. 0基础学RS(二十五)思科OSPF配置
  3. php爬虫模拟登陆爬取数据全过程
  4. 计算机网络实践报告--网络安全
  5. 传智五虎是真相?受影射最重PHP学科,这几月在忙什么..?
  6. os系统 测试网络软件,iStumbler For Mac v103.43 WiFi 信号强度检测工具 _ 黑苹果乐园...
  7. 如何将MAPGIS中的文件转换为SHP格式,及坐标系问题
  8. java iplimage 头文件_在javacv中将IplImage转换为Mat
  9. mysql +cobar_MySQL 中间件 cobar 初体验
  10. TFIDF的原理及实现