FPGA实现UART协议的接收与发送

news2024/11/25 6:30:02

一、接收模块uart_rx.v

        UART协议,空闲时,TX和RX数据线都是通过上拉电阻拉高的状态,这样才能在起始位到来时检测到一个下降的边沿。 

 UART数据格式

uart_rx.v模块输入输出示意图

uart_rx.v模块输入输出示意图

 

  1. RX_start。首先,找到起始位的开始时刻RX_start,这是一个接收字节的最一开始。对RX打拍两次得到RX_1、RX_2。并找出传输每个字节的RX的起始时刻RX_start。

    (注意!:只有在cnt=0时RX出现的下降沿才是起始时刻,否则RX传输过程中也会有由高电平变低电平的情况,这种情况显然不能判定为起始时刻)

  2. cnt。最小是0,最大是传输(1+8+0.5)byte所用时间。cnt平时为0,RX_start出现时cnt立刻开始累加,一直累加到最大值才停止然后恢复到0,等待下一个RX_start到来从新触发cnt累加。cnt不为零的时候,就是说明RX线上正在传输一个字节。
  3. 在何时刻读取RX线上的数据位?定义一个8位的临时寄存器——RX_data_r。在cnt累加到bit1_OK时,到达LSB最低位的中间时刻,中间时刻是最稳定的所以选择在每一位的中间时刻保存该位。以此类推,当cnt累加到bit8_OK时即保存到第8位MSB,这样就保存完一个字节。
  4. 输出结果RX_data、RX_OK。每次都是在cnt累加到bitstop_OK处,把RX_data_r赋值给RX_data,同时产生一个与数据同步的单脉冲RX_OK告知外界本字节接收完且数据可用(注意!:cnt累加到bitstop_OK处时,其实不算完全接收完,因为刚刚接收完停止位的一半)
  5. uart_rx.v程序如下:
module uart_rx#(
	parameter	OSC 		= 50_000_000,
	parameter	BPS 		= 9600,
	parameter	bit_1_cnt 	= 5208,	//bit_1_cnt = OSC/BPS。代表接收1位UART数据时,需要的clk周期个数
	parameter	bit_0_5_cnt = 2604,	//bit_0_5_cnt = bit_1_cnt/2。代表接收0.5位UART数据时,需要的clk周期个数
	parameter	cnt_max 	= 52083	//cnt_max = bit_1_cnt*10。代表接收10位UART数据时,需要的clk周期个数
)(
	input				clk,
	input				RSTn,
	//
	input				RX,
	output reg [7:0]	RX_data,	//接收到的字节数据,在停止位的中间时刻被赋值生效
	output reg			RX_OK		//当一个字节传输完后,也就接收完毕,在停止位结束的最后一刻产生一个接收完成单脉冲信号。RX_data和RX_OK不同步
);

//******************************************************
//		parameter
//******************************************************
localparam	bit1_OK 	= bit_0_5_cnt+bit_1_cnt;
localparam	bit2_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit3_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit4_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit5_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit6_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit7_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit8_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bitstop_OK 	= bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;


//-------------------------RX_1,RX_2-----------------------------------
reg RX_1,RX_2;		//对输入进来的RX信号打两拍,方便检测出下降沿
wire RX_start;		//起始位到来那一时刻,产生一个单脉冲信号

//-------------------------cnt-----------------------------------
reg [31:0] cnt;

//-------------------------RX_data_r-----------------------------------
reg [7:0] RX_data_r;


//******************************************************
//		RX_1,RX_2
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)begin
		RX_1<=1'b0;
		RX_2<=1'b0;
	end
	else begin
		RX_1<=RX;
		RX_2<=RX_1;
	end
end

assign RX_start = ((~RX_1&RX_2)&&(cnt==32'd0))?1'b1:1'b0;
//******************************************************
//		cnt
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		cnt<=32'd0;
	else if(cnt==32'd0&&RX_start==1'b1)		//平常cnt等于0,当检测到RX_start脉冲,就开始计数,一直计10个UART比特位发送时长(根据波特率不同,cnt计数最大值也不同)
		cnt<=32'd1;
	else if(cnt!=32'd0&&cnt<cnt_max-bit_0_5_cnt)
		cnt<=cnt+1'b1;
	else if(cnt>=cnt_max-bit_0_5_cnt)
		cnt<=32'd0;
	else 
		cnt<=cnt;	
end
//******************************************************
//		RX_data_r
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		RX_data_r<=8'd0;
	else if(cnt==bit1_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit2_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit3_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit4_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit5_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit6_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit7_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else if(cnt==bit8_OK)
		RX_data_r<={RX,RX_data_r[7:1]};
	else
		RX_data_r<=RX_data_r;
end
//******************************************************
//		RX_data	RX_OK
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		RX_data<=8'd0;
	else if(cnt==bitstop_OK)
		RX_data<=RX_data_r;
	else
		RX_data<=RX_data;
end

always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		RX_OK<=1'b0;
	else if(cnt==bitstop_OK)
		RX_OK<=1'b1;
	else
		RX_OK<=1'b0;
end






endmodule

二、发送模块uart_tx.v

        发射模块的程序非常简单,就是在对应的时刻拉高或者拉低电平,达到模拟TX信号线的目的。  

 uart_rx.v模块输入输出示意图

        TX_start是个单周期脉冲,是外界给的,当有TX_start单脉冲时,发送TX_data寄存器中的数据。

  1. 进入模块,只有当TX_start=1时,才将TX_data寄存到TX_data_r。其余时刻保持刚刚寄存下来的值。
  2. cnt是时间轴,什么时刻发送第几位,都是根据cnt判断的。最小是0,最大是cnt_max。平时cnt是0,只有当TX_start到来时cnt才开始累加,一直累加到cnt_max(10个完整的数据位占用的等长的clk周期个数)
  3. TX。0~cnt_max平均分成10份,对应着10个数据位,以次发送(改变电平)。
  4. TX_end。cnt==cnt_max时,产生TX_end单脉冲,表示完成一个字节的传输。
  5. uart_tx.v程序如下:
module uart_tx#(
	parameter	OSC 		= 50_000_000,
	parameter	BPS 		= 9600,
	parameter	bit_1_cnt	= 5208,	//bit_1_cnt = OSC/BPS
	parameter 	bit_0_5_cnt	= 2604,
	parameter	cnt_max 	= 52083	//cnt_max = bit_1_cnt*10
)(
	input			clk,
	input			RSTn,
	//
	input [7:0]		TX_data,	//要发送的数据
	input			TX_start,	//发送开始单脉冲使能。TX_data和TX_start同步到来
	output reg		TX,
	output			TX_end
);

//******************************************************
//		parameter
//******************************************************
localparam	bit1_send = bit_1_cnt;
localparam	bit2_send = bit_1_cnt+bit_1_cnt;
localparam	bit3_send = bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit4_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit5_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit6_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit7_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bit8_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam	bitstop_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;

//------------------TX_data_r-----------------------------
reg [7:0] TX_data_r;	//每次在TX_start到来时,寄存一下要发送的数据TX_data给TX_data_r寄存器

//------------------cnt-----------------------------
reg [31:0] cnt;


//******************************************************
//		TX_data_r
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		TX_data_r<=8'd0;
	else if(TX_start==1'b1)
		TX_data_r<=TX_data;
	else
		TX_data_r<=TX_data_r;
end
//******************************************************
//		cnt
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		cnt<=32'd0;
	else if(cnt==32'd0&&TX_start==1'b1)		//平时cnt=0,当TX_start到来时cnt开始累加1,一直加到对应波特率下10个比特位全部发送完
		cnt<=32'd1;
	else if(cnt!=32'd0&&cnt<cnt_max)
		cnt<=cnt+1'b1;
	else if(cnt>=cnt_max)
		cnt<=32'd0;
	else
		cnt<=cnt;
end
//******************************************************
//		TX
//******************************************************
always@(posedge clk or negedge RSTn)begin
	if(!RSTn)
		TX<=1'b1;
	else if(cnt>=32'd1&&cnt<bit1_send)
		TX<=1'b0;		//起始位
	else if(cnt>=bit1_send&&cnt<bit2_send)
		TX<=TX_data_r[0];
	else if(cnt>=bit2_send&&cnt<bit3_send)
		TX<=TX_data_r[1];
	else if(cnt>=bit3_send&&cnt<bit4_send)
		TX<=TX_data_r[2];
	else if(cnt>=bit4_send&&cnt<bit5_send)
		TX<=TX_data_r[3];
	else if(cnt>=bit5_send&&cnt<bit6_send)
		TX<=TX_data_r[4];
	else if(cnt>=bit6_send&&cnt<bit7_send)
		TX<=TX_data_r[5];
	else if(cnt>=bit7_send&&cnt<bit8_send)
		TX<=TX_data_r[6];
	else if(cnt>=bit8_send&&cnt<bitstop_send)
		TX<=TX_data_r[7];
	else if(cnt>=bitstop_send&&cnt<cnt_max)
		TX<=1'b1;		//停止位
	else
		TX<=1'b1;
end
//******************************************************
//		TX_end
//******************************************************
assign TX_end = (cnt==cnt_max)?1'b1:1'b0;




endmodule

 

 

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

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

相关文章

在程序员从业生涯中,哪本书让你醍醐灌顶?

推荐《程序员的README》 [美] 克里斯里科米尼&#xff08;Chris Riccomini&#xff09; 著&#xff0c;付裕 译 每名新入行的工程师在开始工作之前要阅读的书&#xff01;10年大型公司初级工程师指导经验的行业大咖教你如何开启职业生涯、扩展工作技能、应对糟糕管理&#xff0…

信音电子在创业板IPO:募资约9亿元,预计上半年收入约4.3亿元

7月17日&#xff0c;信音电子&#xff08;中国&#xff09;股份有限公司&#xff08;下称“信音电子”&#xff0c;SZ:301329&#xff09;在深圳证券交易所创业板上市。本次上市&#xff0c;信音电子的发行价为21.00元/股&#xff0c;发行数量为为4300万股&#xff0c;募资总额…

Java 压缩多个文件为zip包(中间不生成临时文件,直接压缩为zip二进制流),以及解压zip包二进制流为文件

Java 压缩多个文件为zip包及解压zip包以及压缩多文件为zip文件流解压zip二进制流&#xff08;中间不生成临时文件&#xff0c;直接压缩为zip二进制流&#xff0c;并验证解压&#xff09; 1. 效果图2. 源码 这篇博客将提供俩种方法&#xff0c; 提前生成要压缩的多个文件&#…

vscode debug的方式

在.vscode文件夹下建立launch.json 例子1&#xff1a;调试python 来自 https://github.com/chunleili/tiPBD/tree/amg {"version": "0.2.0","configurations": [{"name": "hpbd 5 5","type": "python&quo…

港联证券|通胀和通缩的区别?通胀对股市有什么影响?

在市场经济上&#xff0c;通货紧缩和通货膨胀是两种比较常见的两种经济现象&#xff0c;那么&#xff0c;通胀和通缩的差异&#xff1f;通胀对股市有什么影响&#xff1f; 港联证证券为大家预备了相关内容&#xff0c;以供参考。 通胀和通缩存在以下差异&#xff1a; 1、定义…

初识操作系统

操作系统 文章目录 操作系统一、上次的问题二、什么是操作系统(Operator System&#xff09;设计操作系统的目的 三、操作系统上下层分别是什么四、先描述&#xff0c;后组织 一、上次的问题 为什么程序运行之前必须先加载到内存&#xff1f; 因为可执行程序&#xff08;文件…

数字化时代,如何做好用户体验与应用性能管理​

引言 随着数字化时代的到来&#xff0c;各个行业的应用系统从传统私有化部署逐渐转向公有云、行业云、微服务&#xff0c;这种变迁给运维部门和应用部门均带来了较大的挑战。基于当前企业 IT 运维均为多部门负责&#xff0c;且使用多种运维工具&#xff0c;因此&#xff0c;当…

【27】SCI易中期刊推荐——计算机科学机器人学(中科院2区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

arcgis建筑密度容积率覆盖率

大多数研究会把覆盖率当作建筑密度。 用覆盖率更恰当一些。 建筑覆盖率&#xff1a; 建筑物占据面积/街区面积 [Sum_area]/ ([area]*1000000) 排序检查数据&#xff0c;最大值0.75&#xff0c;最小值0. 建筑覆盖率&#xff0c;建筑密度的范围都应该在0-1之内&#xff0c;不是…

linux之Ubuntu系列(三)远程管理指令☞SSH 高级应用 RSA非对称加密 以及免密登录,配置别名

对称加密 、非对称加密 1、对称加密中加密和解密使用的秘钥是同一个&#xff1b;非对称加密中采用两个密钥&#xff0c;一般使用公钥进行加密&#xff0c;私钥进行解密。 2、对称加密解密的速度比较快&#xff0c;非对称加密和解密花费的时间长、速度相对较慢。 3、对称加密的…

【文末送书】AIGC时代的数据分析与可视化

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。搜…

C语言实现:offsetof(OFFSETOF)宏的实现

C语言实现&#xff1a;offsetof宏的实现 offsetof:求结构体成员的偏移量 offsetof:求结构体成员的偏移量 直接上代码&#xff1a; #define OFFSETOF(type,member) ((size_t)(&(((type*)0)->member))) 图解&#xff1a; 图中测试原码&#xff1a; #include<stdi…

Centos Stream9安装vim代码提示coc的详细过程

Centos Stream9安装vim代码提示coc的安装步骤&#xff1a; 1、安装字体 https://github.com/ryanoasis/nerd-fonts/releaseshttps://github.com/ryanoasis/nerd-fonts/releases wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/Hack.tar.xz 下载后&a…

短视频seo抖音矩阵源码开发搭建技术解析

一、 短视频seo抖音矩阵源码开发需要考虑以下几个方面&#xff1a; 技术选型&#xff1a;选择合适的开发语言、框架和数据库&#xff0c;常用的开发语言有Java、PHP等&#xff0c;常用的框架有Spring、Django等&#xff0c;常用的数据库有MySQL、MongoDB等。 服务器的选择&…

若依框架系列教程(RuoYi-Vue前后端分离版本)

视频教程见评论 相关文档&#xff1a; https://www.cnblogs.com/52mqq/p/16068330.html 若依 Ruo-Yi&#xff08;分离版&#xff09;学习笔记 若依框架RuoYi项目运行启动教程【傻瓜式教程】_若依前端怎么启动_紫陌~的博客-CSDN博客 一定要看上面那个傻瓜式教程&#xff0c;…

MediaType的常用类型-GPT问答

MediaType的常用类型-GPT问答 MediaType是一个枚举类&#xff0c;包含了常见的媒体类型。下面是一些常用的MediaType类型&#xff1a; APPLICATION_JSON&#xff1a;JSON格式的数据APPLICATION_XML&#xff1a;XML格式的数据APPLICATION_FORM_URLENCODED&#xff1a;表单格式的…

Docker 常用命令速览

Shawn的学习笔记Descriptionhttps://study.chenkequan.cn/#/Spring%E7%B3%BB%E5%88%97/Docker/Docker完整笔记查看我个人网站。 机缘巧合&#xff0c;我想把实验室的网重新配一遍&#xff0c;配置成自动登录校园网&#xff0c;之前配过了&#xff0c;我得到当时打了一个Docker…

盘点国内热门AI大模型

Chat-GPT问世以来&#xff0c;使得大模型一时间内火爆非凡。国内外各大科技公司也纷纷加入到大模型的研发行列中来&#xff0c;“百模之战”愈演愈烈&#xff0c;国内百度、阿里、华为等科技大厂陆续推出旗下大模型产品&#xff0c;新锐科技公司也不甘落后&#xff0c;目前不少…

【基于 GitLab 的 CI/CD 实践】01、GitLab CI/CD 基础概念

目录 一、为什么要做 CI/CD &#xff1f; 1.1 背景-传统的应用开发发布模式 问题 1.2 持续集成与持续交付 持续集成&#xff08;CI&#xff09; 持续交付&#xff08;CD&#xff09; 持续部署&#xff08;CD&#xff09; 1.3 CI/CD 的价值体现 1.4 推荐常用的 CI/CD 工…

人工智能系统将家用机器人的解决问题技能提高了80%

麻省理工学院的研究人员开发了PIGINet&#xff0c;这是一个新系统&#xff0c;旨在有效地提高家用机器人解决问题的能力&#xff0c;将规划时间缩短50-80%。 在正常情况下&#xff0c;家用机器人遵循预定义的执行任务的配方&#xff0c;这并不总是适合多样化或不断变化的环境。…