UART设计

一、UART通信简介

通用异步收发器,

特点:串行、异步、全双工通信

优点:通信线路简单,传输距离远

缺点:传输速度慢

数据传输速率:波特率(单位:baud,波特)

常见的波特率为:1200、2400、4800、19200、38400、57600、115200

最常用的:9600、115200                

数据通信格式:1个数据位+n个数据位+1个校验位+1个结束位

其中n个数据位:通常为8位,即1个字节

空闲位:当总线处于空闲状态时信号线的状态为1,表示当前线路没有进行数据传输。

起始位:每开始一次通信时发送方先发出一个逻辑0的信号,表示传输字符的开始。(因为总线空闲时为高电平,所以开始一次通信时先发送一个明显区别于空闲状态的信号,即低电平)

数据位:起始位之后就是我们所要传输的数据,数据位可以是5,6,7,8,9位等,构成一个字符。先发送最低位,最后发送最高位

奇偶校验位

数据位加上这一位后,使得1的位数应为偶数(偶校验)或奇数(奇校验)。

串口校验的几种方式:

  1. 无校验
  2. 奇校验:如果数据位中“1”的数目是偶数,则校验位为1,如果“1”的数目是奇数,则校验位为0
  3. 偶校验:如果数据位中1的数目是偶数,则校验位为0,如果是奇数,校验位为1
  4. Mark parity:校验位始终为1(不常用)
  5. Parity:校验位始终为0(不常用)

停止位:

它是一个字符数据的结束标志。可以是1位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢

传输时间:计数=时钟频率除以波特频率

二、Verilog实现:

1.UART_TX设计框图

信号

位宽

类型

功能描述

clk

1bit

input

工作时钟,频率50MHz

tx_data

8bit

input

发送数据

tx_flag

1bit

input

发送数据的有效标志信号

tx

1bit

output

串口发送信号

Verilog代码:

uart_tx:

module uart_tx
#(parameter  UART_BPS = 'd9600, //串口波特率parameter CLK_FREQ  = 'd50_000_000  //时钟频率)(input wire clk, //系统时钟input wire rstn,    //全局复位input wire [7:0] tx_data,   //发送8bit数据input wire tx_flag, //发送数据有效标志信号output reg tx //串转并后的1bit数据
);localparam cnt_max = CLK_FREQ / UART_BPS;reg [12:0] bd_cnt;reg bit_flag;reg [3:0] bit_cnt;reg work_en;    //接收数据工作使能信号//work_en:接收数据工作使能信号always @(posedge clk or negedge rstn) beginif (rstn == 1'b0) work_en <= 1'b0;else if (tx_flag == 1'b1) work_en <= 1'b1;else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))work_en <= 1'b0;end//bd_cnt:波特率计数器计数,从0计数到5207always @(posedge clk or negedge  rstn) beginif(rstn == 1'b0)bd_cnt <= 13'b0;else if((bd_cnt == cnt_max - 1) ||(work_en == 1'b0))bd_cnt <= 13'b0;else if(work_en == 1'b1)bd_cnt <= bd_cnt + 1'b1;   end//bit_flag : 当bd_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)bit_flag <= 1'b0;else if(bd_cnt == 13'd1)bit_flag <= 1'd1;else bit_flag <= 1'b0;end//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零always@(posedge clk or negedge rstn) beginif(rstn == 1'b0)bit_cnt <= 4'b0;else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))bit_cnt <= 4'b0;else if((bit_flag == 1'b1)&&(work_en == 1'b1))bit_cnt <= bit_cnt + 1'b1;end//tx:输出数据在满足uart协议(起始位为0,停止位为1)的情况下一位一位输出always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)tx <= 1'b1; //空闲状态为高电平else if(bit_flag == 1'b1)case(bit_cnt)0: tx <= 1'b0 ;1: tx <= tx_data[0];2: tx <= tx_data[1];3: tx <= tx_data[2];4: tx <= tx_data[3];5: tx <= tx_data[4];6: tx <= tx_data[5];7: tx <= tx_data[6];8: tx <= tx_data[7];9: tx <= 1'b1;default : tx <= 1'b1;endcaseendendmodule

testbench:

module tb_uart_tx();reg clk;reg rstn;reg [7:0] tx_data;reg tx_flag;wire tx;//初始化系统时钟,全局复位initial beginclk = 1'b1;rstn = 1'b0;#20;rstn <= 1'b1;end//模拟发送8次数据,分别为0~7initial begintx_data <= 8'b0;tx_flag <= 1'b0;#200//发送数据0tx_data <= 8'd0;tx_flag <= 1'b1;#20tx_flag <= 1'b0;// 每发送1bit数据需要5208个时钟周期,一帧数据为10bit//所以需要数据延时(5208*20*10)后再产生下一个数据#(5208*20*10);//发送数据1tx_data <= 8'd1;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据2tx_data <= 8'd2;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据3tx_data <= 8'd3;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据4tx_data <= 8'd4;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据5tx_data <= 8'd5;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据6tx_data <= 8'd6;tx_flag <= 1'b1;#20tx_flag <= 1'b0;#(5208*20*10);//发送数据7tx_data <= 8'd7;tx_flag <= 1'b1;#20tx_flag <= 1'b0;end//clk:每10ns电平翻转一次,产生一个50MHz的时钟信号always #10 clk = ~clk;uart_tx uart_tx_inst(.clk(clk),.rstn(rstn),.tx_data(tx_data),.tx_flag(tx_flag),.tx(tx));endmodule

仿真截图:

接收模块

uart_rx的设计框图:

信号

位宽

类型

功能描述

clk

1bit

Input

工作时钟,频率50MHz

rstn

1bit

Input

复位信号,低电平有效

rx

1bit

Input

串口接收信号

rx_data

8bit

output

串口接收后转成的8bit数据

rx_flag

1bit

output

串口接收后转成的8bit数据有效标志

Verilog代码:

module uart_rx
#(parameter UART_BPS = 'd9600,parameter CLK_FREQ = 'd50_000_000
)(input wire clk,input wire rstn,input wire rx,  //串口接收数据output reg[7:0] rx_data, //串转并后的8bit数据output reg rx_flag //串转并后的数据有效标志信号
);localparam cnt_max = CLK_FREQ / UART_BPS;reg rx_reg1;reg rx_reg2;reg rx_reg3;reg start_nedge;reg work_en;reg [12:0] bd_cnt;reg bit_flag;reg [3:0] bit_cnt;reg [7:0] data;reg flag;//插入两级寄存器进行数据同步,用来消除亚稳态//rx_reg1:第一级寄存器,寄存器空闲状态复位为1always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)rx_reg1 <= 1'b1;elserx_reg1 <= rx;end//rx_reg2:第二级寄存器,寄存器空闲状态复位为1always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)rx_reg2 <= 1'b1;elserx_reg2 <= rx_reg1;   end//reg3:第三级寄存器和第二级寄存器共同构成下降沿检测always @(posedge clk or rstn) beginif(rstn == 1'b0)rx_reg3 <= 1'b1;elserx_reg3 <= rx_reg2;end//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)start_nedge <= 1'b0;else if((~rx_reg2) && (rx_reg3))start_nedge <= 1'b1;else start_nedge <= 1'b0;end//work_en:接收数据工作使能信号always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)work_en <= 1'b0;else if(start_nedge == 1'b1)work_en <= 1'b1;else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))work_en <= 1'b0;end//bd_cnt:波特率计数器计数,从0计数到5207always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)bd_cnt <= 13'b0;else if((bd_cnt == cnt_max - 1)||(work_en == 1'b0))bd_cnt <= 13'b0;else if(work_en == 1'b1)bd_cnt <= bd_cnt + 1'b1;end//bit_flag : bd_cnt计数器计数到中间数时采样的数据最稳定//此时拉高一个标志信号表示数据可以被取走always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)bit_flag <= 1'b0;else if(bd_cnt == cnt_max/2 - 1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;end//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)//都接收完成后计数器清零always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)bit_cnt <= 4'b0;else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))bit_cnt <= 4'b0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;end//data:输入数据进行移位always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)data <= 8'b0;else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))data <= {rx_reg3,data[7:1]};end//flag:输入数据移位完成时flag拉高一个时钟的高电平always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)flag <= 1'b0;else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))flag <= 1'b1;elseflag <= 1'b0;end//rx_data:输出完整的8位有效数据always @(posedge clk or negedge rstn) beginif(rstn == 1'b0)rx_data <= 8'b0;else if(rx_flag == 1'b1)rx_data <= data;end//rx_flag:输出数据有效标志(比flag延后一个时钟周期,为了和rx_data同步)always @(posedge clk or rstn) beginif(rstn == 1'b0)rx_flag <= 1'b0;elserx_flag <= flag;endendmodule

tb_uart_rx:

module tb_uart_rx();reg clk;reg rstn;reg rx;wire [7:0] rx_data;wire rx_flag;//初始化系统时钟,全局复位和输入信号initial beginclk = 1'b1;rstn <= 1'b0;rx <= 1'b1;#20rstn <= 1'b1;end//模拟发送8次数据,分别为0~7initial 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);endalways #10 clk = ~clk;//定义一个名为rx_bit的任务,每次发送的数据有10位//data的值分别为0~7由i的值传递进来//任务以task开头, 后面紧跟任务名,调用时使用task rx_bit(input [7:0] data);integer  i;//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1;for(i=0;i<10;i=i+1) begincase(i)0: rx <= 1'b0;1: rx <= data[0];2: 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];9: rx <= 1'b1;endcase#(5208*20); // 每发送1位数据延时5208个时钟周期endendtask //任务以endtask结束uart_rx uart_rx_inst(.clk(clk),.rstn(rstn),.rx(rx),.rx_data(rx_data),.rx_flag(rx_flag));endmodule

仿真截图

参考资料:

5. 串口rs232 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档

FPGA协议篇:最简单且通用verilog实现UART协议 - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/596671.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4、双指针-移动零

首先不能复制&#xff0c;只能在原数组是哪个操作&#xff0c;那么很多集合的方式就不行了。当然在现实开发中肯定是可以的。目前按照题目来说是不可以的。所以我们可以思考下&#xff0c;是否可以通过交换来实现。 初始化一个变量 to 为 0。这个变量的目的是跟踪非零元素应该…

云计算存在的安全隐患

目录 一、概述 二、ENISA云安全漏洞分析 三、云计算相关系统漏洞 3.1 概述 3.2 漏洞分析 3.2.1 Hypervisor漏洞 3.2.1.1 CVE-2018-16882 3.2.1.2 CVE-2017-17563 3.2.1.3 CVE-2010-1225 3.2.2 虚拟机漏洞 3.2.2.1 CVE-2019-14835 3.2.2.2 CVE-2019-5514 3.2.2.3 CV…

观测线程的工具——jconsole

joconsole的简单使用 joncole位置在jdk/bin路径中&#xff0c;在进入路径后可以查找到jconsole.exe的应用程序。如图&#xff1a; 双击创建jconsole进程&#xff0c;可以在里面选择所要观测的java文件。 以我的代码为例&#xff1a; class MyThread extends Thread {Overrid…

(一)基于IDEA的JAVA基础11

为什么使用多重循环 有时单独一个循环不能满足我们的要求&#xff0c;我们就要使用多重循环&#xff0c;这不废话吗。 多重循环的使用: 一般我们使用多重循环都是双重for循环&#xff0c; 语法: for(循环条件){ 循环操作1&#xff1b; for(循环条件2){ 循环操作2&#x…

Struts2的入门:新建项目——》导入jar包——》jsp,action,struts.xml,web.xml——》在项目运行

文章目录 配置环境tomcat 新建项目导入jar包新建jsp界面新建action类新建struts.xml,用来配置action文件配置Struts2的核心过滤器&#xff1a;web.xml 启动测试给一个返回界面在struts.xml中配置以实现页面的跳转&#xff1a;result再写个success.jsp最后在项目运行 配置环境 …

post请求搜索功能爬虫

<!--爬虫仅支持1.8版本的jdk--> <!-- 爬虫需要的依赖--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency>…

HTTP 摘要认证

文章目录 一、什么是摘要认证二、工作流程三、实例演示 一、什么是摘要认证 摘要认证&#xff0c;即 Digest Access Authentication&#xff0c;是一种HTTP身份验证机制&#xff0c;用于验证用户的身份。相较于基本认证&#xff08;Basic Authentication&#xff09;使用用户名…

相机标定——四个坐标系介绍

世界坐标系(Xw,Yw,Zw) 世界坐标系是一个用于描述和定位三维空间中物体位置的坐标系&#xff0c;通常反映真实世界下物体的位置和方向。它是一个惯性坐标系&#xff0c;被用作整个场景或系统的参考框架。在很多情况下&#xff0c;世界坐标系被认为是固定不变的&#xff0c;即它…

解码mp4文件分别存储为pcm,yuv文件

// 解码分别写入对应文件 #include "myLog.h" #include <iostream>extern "C" { #include <libavformat\avformat.h> #include <libavutil\avutil.h> #include <libavcodec\avcodec.h> #include <libavutil\imgutils.h> #in…

华为ensp中高级acl (控制列表) 原理和配置命令 (详解)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月6日23点18分 高级acl&#xff08;Access Control List&#xff09;是一种访问控制列表&#xff0c;可以根据数据包的源IP地址、目标IP地址、源端口、目标端口、协议…

多路转接-epoll/Reactor(2)

epoll 上次说到了poll&#xff0c;它存在效率问题&#xff0c;因此出现了改进的poll----epoll。 目前epoll是公认的效率最高的多路转接的方案。 快速了解epoll接口 epoll_create&#xff1a; 这个参数其实已经被废弃了。 这个值只要大于0就可以了。 这是用来创建一个epoll模…

应用性能分析工具CPU Profiler

简介 本文档介绍应用性能分析工具CPU Profiler的使用方法&#xff0c;该工具为开发者提供性能采样分析手段&#xff0c;可在不插桩情况下获取调用栈上各层函数的执行时间&#xff0c;并展示在时间轴上。 开发者可通过该工具查看TS/JS代码及NAPI代码执行过程中的时序及耗时情况…