15 UART回环

UART 串口简介

常用的通信方式可分为为串行通信(serial communication)和并行通信(parallel communication)两种。并行通信是多比特数据同时通过并行线进行传送(一般以字或字节为单位并行进行传输),这种传输方式用的线多、成本高、速率高、要考虑时延匹配,不宜进行长距离通信,因此并行通信一般用于板载外设的通信。串行通信是数据在一条线上一比特接一比特地按顺序进行传送,这种传输方式线少、成本低、速率低,事宜用于长距离通信,因此串行通信常用于不同设备之间的通信。
串行通信可分为同步串行通信(synchronized serial communication)和异步串行通信(asynchronous serial communication)。同步串行通信需要通信双方在同一时钟的控制下同步传输数据,如SPI、IIC等,而异步串行通信只需要通信双方约定好通信速率、起始标志、结束标志,然后在各自的时钟的控制下进行数据传输,如UART等。
UART 是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receivertransmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。UART 通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。
在这里插入图片描述

UART传输时序

UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位
在这里插入图片描述
起始位:不传输数据时 UART 数据传输线通常保持高电压电平。若要开始数据传输,发送方会将传输线从高电平拉到低电平并保持 1 个波特率周期。当接收方检测到高电平到低电平的跃迁时,便开始以波特率对应的频率读取数据帧中的位。
数据帧:数据帧包含所传输的实际数据。如果使用奇偶校验位,数据帧长度可以是 5 位到 8 位,如果不使用奇偶校验位,数据帧长度可以是 9 位。在大多数情况下,数据以最低有效位优先方式发送。
奇偶校验:接收方可以通过奇偶校验位来判断数据传输期间是否发生改变(奇校验就是确保数据位和校验位中 1 的个数为奇数,偶校验就是确保数据位和校验位中 1 的个数为偶数)。
停止位:表示数据包结束,发送方将数据传输线从拉到高并保持 1 到 2 位的时间。
在进行UART通信时双方需要约定好数据位数(可选择 5、6、7、8 位)、奇偶校验(可选择奇校验、偶校验、无校验)、停止位(可选择 1 、1.5 、 2 位)、波特率(每秒传输二进制数据的位数)

UART 硬件

在设置好数据格式及波特率之后,UART 只负责完成数据的串并转换,而信号的传输则由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有TTL、 RS232、RS422、RS485 等,它们定义了接口的电气特性,如 RS-232 是单端输入输出,而 RS-422/485 为差分输入输出等。其中RS232是最常见的标准之一,它一般采用DB9类型的接口,如下图所示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

硬件设计

在这里插入图片描述
在这里插入图片描述

原理图中的 CH340 就是转换芯片(用于将 TTL 标准的 UART 转换为 USB ),其中 UART1_RXD 连接到 FPGA 芯片的的 RX (E14)管脚,UART1_TXD 连接到 FPGA 芯片的 TX(D17) 管脚,CH340_P/N 分别接到开发板的 TYPE - C 插座 D+/-上。

系统框图

本次实验任务是实现串口数据的回环,以此整个设计包括两个子模块,分别用于串口的发送和接收,其系统框图如下:
在这里插入图片描述

代码编写

串口接收模块设计

串口接收模块的框图如下:
在这里插入图片描述
其时序图如下:
在这里插入图片描述
代码如下:

module uart_rx #(parameter CLK_FREQ = 50000000,		//系统时钟parameter UART_BPS = 115200			//波特率
)(input sys_clk,						//系统时钟input sys_rst_n,					//系统复位input uart_rxd,						//串口接收线output reg data_flag,				//数据有效标志,随数据一起输出,只保持一个时钟周期output reg [7:0] rx_data			//串口接收到的数据
);localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;	//波特率计数器的计数周期//串口输入延迟一拍,用于捕获上升沿
reg uart_rxd_d0;
//启动标志,在空闲状态检测到开始信号拉高
wire start_flag;
//接收忙标志,接收过程中拉高
reg rx_busy;
//波特率计数器
reg [31:0] baud_count;
//接收计数
reg [3:0] rx_count;
//串口接收移位寄存器
reg [7:0] uart_rx_data;//延迟一拍,用于捕获下降沿
always @(posedge sys_clk) beginif(!sys_rst_n)uart_rxd_d0 <= 1'b1;elseuart_rxd_d0 <= uart_rxd;
end//捕获下降沿,在下降沿时刻且接收空闲则拉高start_flag
assign start_flag = ((uart_rxd_d0 & ~uart_rxd & ~rx_busy)) ? 1 : 0;//接收忙标志控制
//检测到start_flag时设置为忙,当接收到8bit数据(不包括启动位)且波特率计数器溢出时设置为空闲
always @(posedge sys_clk) beginif(!sys_rst_n)rx_busy <= 1'b0;else if(start_flag == 1'b1)rx_busy <= 1'b1;else if((rx_count == 4'd8) && (baud_count == (BAUD_CNT_MAX - 1)))rx_busy <= 1'b0;
end//波特率计数器,当接收处于忙状态时进行周期计数
always @(posedge sys_clk) beginif(!sys_rst_n)baud_count <= 0;else if(rx_busy == 1'b1) beginif(baud_count < (BAUD_CNT_MAX - 1))baud_count <= baud_count + 1;elsebaud_count <= 0;endelsebaud_count <= 0;
end//接收计数器,当接收忙且波特率计数器溢出时进行递增计数
always @(posedge sys_clk) beginif(!sys_rst_n)rx_count <= 4'b0;else if(rx_busy == 1'b1) beginif(baud_count == (BAUD_CNT_MAX - 1))rx_count <= rx_count + 4'b1;endelserx_count <= 4'b0;
end//将数据采集到移位寄存器中
always @(posedge sys_clk) beginif(!sys_rst_n)uart_rx_data <= 8'b0;else if(rx_busy == 1'b1) beginif(baud_count == ((BAUD_CNT_MAX - 1) / 2)) begincase(rx_count)4'd1: uart_rx_data[0] <= uart_rxd_d0;	//bit04'd2: uart_rx_data[1] <= uart_rxd_d0;	//bit14'd3: uart_rx_data[2] <= uart_rxd_d0;	//bit24'd4: uart_rx_data[3] <= uart_rxd_d0;	//bit34'd5: uart_rx_data[4] <= uart_rxd_d0;	//bit44'd6: uart_rx_data[5] <= uart_rxd_d0;	//bit54'd7: uart_rx_data[6] <= uart_rxd_d0;	//bit64'd8: uart_rx_data[7] <= uart_rxd_d0;	//bit7default: ;endcaseendendelseuart_rx_data <= 8'b0;
end//数据有效标志输出
always @(posedge sys_clk) beginif(!sys_rst_n)data_flag <= 1'b0;else if((rx_count == 8) && (baud_count == (BAUD_CNT_MAX - 1)))data_flag <= 1'b1;elsedata_flag <= 1'b0;
end//数据输出
always @(posedge sys_clk) beginif(!sys_rst_n)rx_data <= 1'b0;else if((rx_count == 8) && (baud_count == (BAUD_CNT_MAX - 1)))rx_data <= uart_rx_data;
endendmodule

串口发送模块设计

串口发送模块框图如下:
在这里插入图片描述
对应的时序图如下:
在这里插入图片描述
程序代码如下:

module uart_tx #(parameter CLK_FREQ = 50000000,		//系统时钟parameter UART_BPS = 115200			//波特率
)(input sys_clk,						//系统时钟input sys_rst_n,					//系统复位output reg uart_txd,				//串口发送线input data_flag,					//数据有效标志,随数据一起输入,只保持一个时钟周期input [7:0] tx_data,				//串口需要发送的数据output reg tx_busy					//串口发送忙标志
);localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;	//波特率计数器的计数周期//波特率计数器
reg [31:0] baud_count;
//接收计数
reg [3:0] tx_count;
//串口接收移位寄存器
reg [7:0] uart_tx_data;//切换到忙状态控制
//当数据有效且处于空闲状态则转换到忙状态
//当完成发送后又转换到空闲状态,发送空闲周期时提前BAUD_CNT_MAX/32周期结束,确保回环测试时发送速度大于接收速度
always @(posedge sys_clk) beginif(!sys_rst_n)tx_busy <= 1'b0;else if((data_flag == 1'b1) && (tx_busy == 1'b0))tx_busy <= 1'b1;else if((tx_count == 10'd9) && (baud_count == (BAUD_CNT_MAX - BAUD_CNT_MAX / 32 - 1)))tx_busy <= 1'b0;
end//锁定需要发送的数据到移位寄存器
always @(posedge sys_clk) beginif(!sys_rst_n)uart_tx_data <= 8'b0;else if((data_flag == 1'b1) && (tx_busy == 1'b0))uart_tx_data <= tx_data;else if((tx_count == 4'd9) && (baud_count == (BAUD_CNT_MAX - BAUD_CNT_MAX / 32 - 1)))uart_tx_data <= 8'b0;
end//波特率计数器,当发送处于忙状态时进行周期计数
always @(posedge sys_clk) beginif(!sys_rst_n)baud_count <= 0;else if(tx_busy == 1'b1) beginif(baud_count < (BAUD_CNT_MAX - 1))baud_count <= baud_count + 1;elsebaud_count <= 0;endelsebaud_count <= 0;
end//发送计数器,当发送忙且波特率计数器溢出时进行递增计数
always @(posedge sys_clk) beginif(!sys_rst_n)tx_count <= 4'b0;else if(tx_busy == 1'b1) beginif(baud_count == (BAUD_CNT_MAX - 1))tx_count <= tx_count + 4'b1;endelsetx_count <= 4'b0;
end//将移位寄存器中的数据通过txd发送出去
always @(posedge sys_clk) beginif(!sys_rst_n)uart_txd <= 8'b1;else if(tx_busy == 1'b1) begincase(tx_count)4'd0: uart_txd <= 1'b0;				//起始位4'd1: uart_txd <= uart_tx_data[0];	//bit04'd2: uart_txd <= uart_tx_data[1];	//bit14'd3: uart_txd <= uart_tx_data[2];	//bit24'd4: uart_txd <= uart_tx_data[3];	//bit34'd5: uart_txd <= uart_tx_data[4];	//bit44'd6: uart_txd <= uart_tx_data[5];	//bit54'd7: uart_txd <= uart_tx_data[6];	//bit64'd8: uart_txd <= uart_tx_data[7];	//bit74'd9: uart_txd <= 1'b1;				//停止位default: uart_txd <= 1'b1;endcaseendelseuart_txd <= 8'b1;
endendmodule

顶层模块设计

顶层模块主要负责例化串口接收和发送模块,并将接收到的数据给发送模块进行发送,相应的代码如下:

module uart_loopback #(parameter CLK_FREQ = 50000000,		//系统时钟parameter UART_BPS = 115200			//波特率
)(input sys_clk,						//系统时钟input sys_rst_n,					//系统复位input uart_rxd,					//串口接收线output uart_txd					//串口发送线
);//串口输入延迟一拍
reg uart_rxd_d0;
//串口输入延迟二拍
reg uart_rxd_d1;//数据有效标志
wire data_flag;
//串口收到的数据
wire [7:0] rx_data;
//串口发送忙标志
wire tx_busy;//延迟两拍,用于消除亚稳态
always @(posedge sys_clk) beginif(!sys_rst_n) beginuart_rxd_d0 <= 1'b1;uart_rxd_d1 <= 1'b1;endelse beginuart_rxd_d0 <= uart_rxd;uart_rxd_d1 <= uart_rxd_d0;end
end//例化串口接收模块
uart_rx #(.CLK_FREQ(CLK_FREQ),			//系统时钟.UART_BPS(UART_BPS)				//波特率
)u_uart_rx_inst0(.sys_clk(sys_clk),				//系统时钟.sys_rst_n(sys_rst_n),			//系统复位.uart_rxd(uart_rxd_d1),			//串口接收线.data_flag(data_flag),			//数据有效标志,随数据一起输出,只保持一个时钟周期.rx_data(rx_data)				//串口接收到的数据
);//例化串口发送模块
uart_tx #(.CLK_FREQ(CLK_FREQ),			//系统时钟.UART_BPS(UART_BPS)				//波特率
)u_uart_tx_inst0(.sys_clk(sys_clk),				//系统时钟.sys_rst_n(sys_rst_n),			//系统复位.uart_txd(uart_txd),			//串口发送线.data_flag(data_flag),			//数据有效标志,随数据一起输入,只保持一个时钟周期.tx_data(rx_data),				//串口需要发送的数据.tx_busy(tx_busy)				//串口发送忙标志
);endmodule

仿真激励代码设计

仿真激励代码先对顶层模块进行复位,然后通过uart_rxd向顶层模块注入数据,同时还要产生50M的时钟,其代码如下:

`timescale 1ns / 1psmodule tb_uart_loopback();//parameter define
parameter  CLK_PERIOD = 20;		//时钟周期为20ns//reg define
reg sys_clk;					//时钟信号
reg sys_rst_n;					//复位信号
reg uart_rxd;					//UART接收端口
wire uart_txd;					//UART发送端口//发送8'h55(8'b0101_0101)
initial beginsys_clk <= 1'b0;sys_rst_n <= 1'b0;uart_rxd <= 1'b1;#200sys_rst_n <= 1'b1;#1000uart_rxd <= 1'b0;	//起始位#8680				//延时433个时钟周期uart_rxd <= 1'b1;	//D0#8680uart_rxd <= 1'b0;	//D1#8680uart_rxd <= 1'b1;	//D2#8680uart_rxd <= 1'b0;	//D3#8680uart_rxd <= 1'b1;	//D4#8680uart_rxd <= 1'b0;	//D5#8680uart_rxd <= 1'b1;	//D6#8680uart_rxd <= 1'b0;	//D7 #8680uart_rxd <= 1'b1;	//停止位#8680uart_rxd <= 1'b1;	//空闲状态   
end//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;//例化顶层模块
uart_loopback  u_tb_uart_loopback_inst0(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.uart_rxd(uart_rxd),.uart_txd(uart_txd)
);endmodule

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

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

相关文章

什么是Linux?它与其他操作系统有何区别?

什么是Linux&#xff1f;它与其他操作系统有何区别&#xff1f; 什么是Linux&#xff1f;它与其他操作系统有何区别&#xff1f;摘要引言正文内容了解LinuxLinux与其他操作系统的区别开放性多样性安全性 &#x1f914; QA环节小结 参考资料表格总结总结未来展望 博主 默语带您 …

DBO优化GRNN回归预测(matlab代码)

DBO-GRNN回归预测matlab代码 蜣螂优化算法(Dung Beetle Optimizer, DBO)是一种新型的群智能优化算法&#xff0c;在2022年底提出&#xff0c;主要是受蜣螂的的滚球、跳舞、觅食、偷窃和繁殖行为的启发。 数据为Excel股票预测数据。 数据集划分为训练集、验证集、测试集,比例…

如何使用OpenCV扫描图像、查找表和时间测量

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV4.9.0开源计算机视觉库核心功能&#xff08;核心模块&#xff09; ​ 编辑 目标 我们将寻求以下问题的答案&#xff1a; 如何浏览图像的每个像素&#xff1f;OpenCV 矩…

【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.哈希桶源码 2.哈希…

分割数组的两种解法:动态规划、二分法

410. 分割数组的最大值 1. 动态规划 「将数组分割为 m 段&#xff0c;求……」是动态规划题目常见的问法 理清状态转移方程比较难&#xff0c;因此不推荐用动态规划解题。 2. 贪心 二分法 「使……最大值尽可能小」是二分搜索题目常见的问法。 本题中&#xff0c;我们注意…

数据库管理开发工具Navicat for MySQL Mac版下载

Navicat for MySQL&#xff08;Mac版&#xff09;是一款强大的数据库管理开发工具&#xff0c;专为MySQL设计。它提供直观的用户界面&#xff0c;支持数据建模、查询构建、数据传输等功能&#xff0c;帮助用户轻松管理数据库。其特点包括高效的数据处理能力、安全的数据传输机制…

算法---前缀和练习-1(除自身以外数组的乘积)

除自身以外数组的乘积 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 首先&#xff0c;创建两个辅助数组 f 和 g&#xff0c;它们的长度与 nums 相同。数组 f 用于存储每个元素左侧所有元素的乘积&#xff0c;数组 g 用于存…

SpringCloud-Nacos注册中心

服务注册到nacos Nacos是SpringCloudAlibaba的组件&#xff0c;而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说&#xff0c;并没有太大区别。 主要差异在于&#xff1a; 依赖不同服务地址不同 1. 引入依赖…

XML Data – Semi-Structured Data XML 数据 - 半结构化数据

Outline • Structured, Semistructured, and Unstructured Data • XML Hierarchical (Tree) Data Model • Extracting XML Documents from Relational Databases • XML Documents, DTD, and XML Schema • XML Languages 结构化、半结构化和非结构化数据 - XML 层次&#x…

$.when.apply($, deferreds).done(function() {}) 用法

$.when.apply($, deferreds).done(function() {}) 这行代码是 jQuery 中用于处理多个异步操作的一种模式。让我们逐步解释其用法&#xff1a; $.when(): 这是 jQuery 中的一个方法&#xff0c;用于创建一个新的 Deferred&#xff08;延迟&#xff09;对象。Deferred 对象用于管…

如何设计循环队列(两种方法)

文章目录 前言一、方法一:数组法二、方法二.链表法总结 前言 前面有提到过队列的知识&#xff0c;这次来说一下怎么设计一个循环队列 一.循环队列&#xff08;力扣&#xff09; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资…

【算法每日一练]-动态规划(保姆级教程 篇17 状态压缩)#POJ1185:炮兵阵地 #互不侵犯

目录 今日知识点&#xff1a; 把状态压缩成j,dp每行i的布置状态&#xff0c;从i-1和i-2行进行不断转移 把状态压缩成j,dp每行i的布置状态&#xff0c;从i-1行进行状态匹配&#xff0c;然后枚举国王数转移 POJ1185&#xff1a;炮兵阵地 思路&#xff1a; 题目&#xff1a;互…

代码随想录算法训练营第二十八天|● 93.复原IP地址 ● 78.子集 ● 90.子集II (JS写法)

93 复原IP地址 题目链接/文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1XP4y1U73i/ 思路&#xff1a; /*** param {string} s* return {string[]}*/ var resto…

微信商家转账到零钱:实用指南,涵盖开通、使用与常见问题

商家转账到零钱是什么&#xff1f; 商家转账到零钱功能整合了企业付款到零钱和批量转账到零钱&#xff0c;支持批量对外转账&#xff0c;操作便捷。如果你的应用场景是单付款&#xff0c;体验感和企业付款到零钱基本没差别。 商家转账到零钱的使用场景有哪些&#xff1f; 这…

基于YOLOv8深度学习的橙子病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

【动态规划】Leetcode 746. 使用最小花费爬楼梯

【动态规划】Leetcode 746. 使用最小花费爬楼梯 解法 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[i] 表示跳跃到第 i 层&#x…

「Swift」AttributedString常见使用方法

前言&#xff1a;AttributedString是Apple推出的可以实现单个字符或字符范围带相应属性的字符串。属性提供了一些文本特性&#xff0c;可以让文本展示的样式更加丰富。在日常开发过程中&#xff0c;我通常用于同一个Label中包含不同的字体大小或字体颜色的样式编写中。 使用举…

11 Games101 - 笔记 - 几何(曲线与曲面)

11 几何&#xff08;曲线与曲面&#xff09; 贝塞尔曲线 定义 贝塞尔曲线&#xff1a;由控制点和线段组成的曲线&#xff0c;控制点是可拖动的支点。 如图&#xff0c;蓝色为贝塞尔曲线&#xff0c;p1, p2, p3为控制点&#xff0c;曲线和初始与终止端点相切&#xff0c;并且…

【原理图PCB专题】Cadence 17.4版本导出excel版本坐标文件

坐标文件记录了板卡上每个元件的坐标是生产的基础资料,在PCBA生产时,需要提坐标文件并且务必保证 准确无误。 Cadence导出坐标文件大部分网上的都是txt文件,其格式如下。导出的文件存在没有对应的Title,格式打开混乱等问题。 那么Cadence 17.4版本如何导出有标题、…

分布式系统的基本特性

一般&#xff0c;分布式系统需要支持以下特性&#xff1a; 资源共享 开放性 并发性 可伸缩性 容错性 透明性 下面分别讨论。 容易理解的 资源共享 一旦授权&#xff0c;可以访问环境中的任何资源。 资源&#xff1a;包括硬件(e.g. printer, scanner, camera)、软件&a…