FPGA的uart通信实验仿真


注:代码参考:https://blog.csdn.net/weixin_49780322/article/details/120216265?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2-120216265-blog-75201194.pc_relevant_multi_platform_whitelistv1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2-120216265-blog-75201194.pc_relevant_multi_platform_whitelistv1&utm_relevant_index=5


一、uart的基本知识(参考正点原子视频)

UART是一种采用异步串行通信方式的通用异步收发传输器。功能:在发送数据时将并行数据转换为串行数据来传输,在接收数据时将接收的串行数据转换成并行数据。

在这里插入图片描述
在这里插入图片描述
异步通信和同步通信最大的区别就是时候带有同步时钟信号
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述奇偶检验位是检验数据时候真实可靠,如果是奇数校验的话根据前面的有效数据1的个数在校验位补上0或者1,来保证1的个数是奇数;偶校验一样的原理,保证1的个数为偶数。有效数据的个数可以为5、6、7、8,常用的是8位。停止位可以是1、1.5、2个时钟周期的高电平

在这里插入图片描述

二、uart实验【参考:链接

1. 接收模块代码

uart串口通信是一种异步串行的全双工通信方式,tx端用于数据发送,rx端用于数据的接收。信号线空闲时为高电平。由于是异步通信,数据发送会包装成数据帧的形式发送,帧格式为:1个起始位(0)、8个数据位(用户数据)、1个奇偶校验位(用于简单纠错以保证传送的可靠性)、1或者2两个停止位(1个),其中奇偶校验位不是必须的。

  • 检测数据的发送:由图可知数据帧格式第一个bit是低电平,当FPGA的rx端检测到信号线上有下降沿产生时,表示有数据传送过来,根据预先设置的波特率对数据接收,由于数据是串行从低位到高位传输,接收到的数据暂时存储到寄存器中,等到接收完1个字节的数据,通过串并转换保存接收到的数据
  • 发送时通过tx信号线按照设置好的波特率将数据发送出去,数据按照数据帧的格式发送,即先发送1bit的低电平,再从低位到高位发送数据。

在这里插入图片描述

module uart_rx(
	input 			sys_clk,			//50M系统时钟
	input 			sys_rst_n,			//系统复位
	input 			uart_rxd,			//接收数据线
	output reg 		uart_rx_done,		//数据接收完成标志
	output reg [7:0]uart_rx_data		//接收到的数据
);
//常量化波特率,可更改
parameter	BPS=9600;					//波特率9600bps,可更改
parameter	SYS_CLK_FRE=50_000_000;		//50M系统时钟
localparam	BPS_CNT=SYS_CLK_FRE/BPS;	//传输一位数据所需要的时钟个数
 
reg 			uart_rx_d0;		//寄存1拍
reg 			uart_rx_d1;		//寄存2拍
reg [15:0]		clk_cnt;				//时钟计数器
reg [3:0]		rx_cnt;					//接收计数器
reg 			rx_flag;				//接收标志位
reg [7:0]		uart_rx_data_reg;		//数据寄存
	
wire 			neg_uart_rx_data;		//数据的下降沿
 
assign	neg_uart_rx_data=uart_rx_d1 & (~uart_rx_d0);  //捕获数据线的下降沿,用来标志数据传输开始
//将数据线打两拍,作用1:同步不同时钟域信号,防止亚稳态;作用2:用以捕获下降沿
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_rx_d0<=1'b0;
		uart_rx_d1<=1'b0;
	end
	else begin
		uart_rx_d0<=uart_rxd;
		uart_rx_d1<=uart_rx_d0;
	end		
end
//捕获到数据下降沿(起始位0)后,拉高传输开始标志位,并在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rx_flag<=1'b0;
	else begin 
		if(neg_uart_rx_data)
			rx_flag<=1'b1;
		else if((rx_cnt==4'd9)&&(clk_cnt==BPS_CNT/2))//在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
			rx_flag<=1'b0;
		else 
			rx_flag<=rx_flag;			
	end
end
//时钟每计数一个BPS_CNT(传输一位数据所需要的时钟个数),即将数据计数器加1,并清零时钟计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		rx_cnt<=4'd0;
		clk_cnt<=16'd0;
	end
	else if(rx_flag)begin
		if(clk_cnt<BPS_CNT-1'b1)begin
			clk_cnt<=clk_cnt+1'b1;
			rx_cnt<=rx_cnt;
		end
		else begin
			clk_cnt<=16'd0;
			rx_cnt<=rx_cnt+1'b1;
		end
	end
	else begin
		rx_cnt<=4'd0;
		clk_cnt<=16'd0;
	end		
end
//在每个数据的传输过程正中(数据比较稳定)将数据线上的数据赋值给数据寄存器
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_rx_data_reg<=8'd0;
	else if(rx_flag)
		if(clk_cnt==BPS_CNT/2) begin
			case(rx_cnt)			
				4'd1:uart_rx_data_reg[0]<=uart_rxd;
				4'd2:uart_rx_data_reg[1]<=uart_rxd;
				4'd3:uart_rx_data_reg[2]<=uart_rxd;
				4'd4:uart_rx_data_reg[3]<=uart_rxd;
				4'd5:uart_rx_data_reg[4]<=uart_rxd;
				4'd6:uart_rx_data_reg[5]<=uart_rxd;
				4'd7:uart_rx_data_reg[6]<=uart_rxd;
				4'd8:uart_rx_data_reg[7]<=uart_rxd;
				default:;
			endcase
		end
		else
			uart_rx_data_reg<=uart_rx_data_reg;
	else
		uart_rx_data_reg<=8'd0;
end	
//当数据传输到终止位时,拉高传输完成标志位,并将数据输出
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_rx_done<=1'b0;
		uart_rx_data<=8'd0;
	end	
	else if(rx_cnt==4'd9)begin
		uart_rx_done<=1'b1;
		uart_rx_data<=uart_rx_data_reg;
	end		
	else begin
		uart_rx_done<=1'b0;
		uart_rx_data<=8'd0;
	end
end
endmodule

2.接收模块测试代码

`timescale 1ns/1ns	//定义时间刻度
//模块、接口定义
module uart_rx_tb();
reg sys_clk;			
reg sys_rst_n;			
reg uart_rxd;
 
wire uart_rx_done;		
wire uart_rx_data;
//例化被测试的接收模块
uart_rx #(
	.BPS			(9600),		//波特率9600
	.SYS_CLK_FRE	(50_000_000)//时钟频率50M	
)
u_uart_rx(
	.sys_clk		(sys_clk),			
	.sys_rst_n		(sys_rst_n),			
	.uart_rxd		(uart_rxd),			
	.uart_rx_done	(uart_rx_done),		
	.uart_rx_data	(uart_rx_data)	
);
 
localparam	CNT=50_000_000/9600*20;	//计算出传输每个时钟所需要的时间
initial begin	//传输8位数据	8'b01010101
	//初始时刻定义
		sys_clk	<=1'b0;	
		sys_rst_n<=1'b0;		
		uart_rxd<=1'b1;//空闲时需要拉高
	#20 //系统开始工作
		sys_rst_n<=1'b1;
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b1;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b1;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
end
 
always begin
	#10	sys_clk=~sys_clk;	//时钟20ns,50M
end
 
endmodule 


3.接收模块仿真图

在这里插入图片描述

4.发送模块代码

module uart_tx(
	input 			sys_clk,	//50M系统时钟
	input 			sys_rst_n,	//系统复位
	input	[7:0] 	uart_data,	//发送的8位置数据
	input			uart_tx_en,	//发送使能信号
	output reg 		uart_txd	//串口发送数据线
 
);
 
parameter 	SYS_CLK_FRE=50_000_000;    //50M系统时钟 
parameter 	BPS=9_600;                 //波特率9600bps,可更改
localparam	BPS_CNT=SYS_CLK_FRE/BPS;   //传输一位数据所需要的时钟个数
 
reg	uart_tx_en_d0;			//寄存1拍
reg uart_tx_en_d1;			//寄存2拍
reg tx_flag;				//发送标志位
reg [7:0]  uart_data_reg;	//发送数据寄存器
reg [15:0] clk_cnt;			//时钟计数器
reg [3:0]  tx_cnt;			//发送个数计数器
 
wire pos_uart_en_txd;		//使能信号的上升沿
//捕捉使能端的上升沿信号,用来标志输出开始传输
assign pos_uart_en_txd= uart_tx_en_d0 && (~uart_tx_en_d1);
 
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_tx_en_d0<=1'b0;
		uart_tx_en_d1<=1'b0;		
	end
	else begin
		uart_tx_en_d0<=uart_tx_en;
		uart_tx_en_d1<=uart_tx_en_d0;
	end	
end
//捕获到使能端的上升沿信号,拉高传输开始标志位,并在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		tx_flag<=1'b0;
		uart_data_reg<=8'd0;
	end
	else if(pos_uart_en_txd)begin
		uart_data_reg<=uart_data;
		tx_flag<=1'b1;
	end
	else if((tx_cnt==4'd9) && (clk_cnt==BPS_CNT/2))begin//在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
		tx_flag<=1'b0;
		uart_data_reg<=8'd0;
	end
	else begin
		uart_data_reg<=uart_data_reg;
		tx_flag<=tx_flag;	
	end
end
//时钟每计数一个BPS_CNT(传输一位数据所需要的时钟个数),即将数据计数器加1,并清零时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		clk_cnt<=16'd0;
		tx_cnt <=4'd0;
	end
	else if(tx_flag) begin
		if(clk_cnt<BPS_CNT-1)begin
			clk_cnt<=clk_cnt+1'b1;
			tx_cnt <=tx_cnt;
		end
		else begin
			clk_cnt<=16'd0;
			tx_cnt <=tx_cnt+1'b1;
		end
	end
	else begin
		clk_cnt<=16'd0;
		tx_cnt<=4'd0;
	end
end
//在每个数据的传输过程正中(数据比较稳定)将数据寄存器的数据赋值给数据线
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_txd<=1'b1;
	else if(tx_flag)
		case(tx_cnt)
			4'd0:	uart_txd<=1'b0;
			4'd1:	uart_txd<=uart_data_reg[0];
			4'd2:	uart_txd<=uart_data_reg[1];
			4'd3:	uart_txd<=uart_data_reg[2];
			4'd4:	uart_txd<=uart_data_reg[3];
			4'd5:	uart_txd<=uart_data_reg[4];
			4'd6:	uart_txd<=uart_data_reg[5];
			4'd7:	uart_txd<=uart_data_reg[6];
			4'd8:	uart_txd<=uart_data_reg[7];
			4'd9:	uart_txd<=1'b1;
			default:;
		endcase
	else 	
		uart_txd<=1'b1;
end
endmodule

5.发送模块测试代码

`timescale 1ns/1ns	//定义时间刻度
//模块、接口定义
module uart_tx_tb();
reg 		sys_clk;			
reg 		sys_rst_n;			
reg [7:0]	uart_data;
reg 		uart_tx_en;
		
wire 	 	uart_txd;
//例化被测试的接收模块
uart_tx #(
	.BPS			(9600),		//波特率9600
	.SYS_CLK_FRE	(50_000_000)//时钟频率50M	
)
u_uart_tx(
	.sys_clk		(sys_clk),			
	.sys_rst_n		(sys_rst_n),			
	.uart_data		(uart_data),			
	.uart_tx_en		(uart_tx_en),		
	.uart_txd		(uart_txd)	
);
 
localparam	CNT=50_000_000/9600*20;	//计算出传输每个时钟所需要的时间
initial begin	//传输8位数据	8'b01010101
	//初始时刻定义
		sys_clk		<=1'b0;	
		sys_rst_n	<=1'b0;		
		uart_tx_en	<=1'b0;
		uart_data	<=8'b01010101;//发送数据 01010101	
	#20 //系统开始工作
		sys_rst_n   <=1'b1;
	#(CNT/2)
		uart_tx_en	<=1'b1;	
	#20
		uart_tx_en	<=1'b0;		
end
 
always begin
	#10	sys_clk=~sys_clk;	//时钟20ns,50M
end
 
endmodule 

6.发送模块仿真图

在这里插入图片描述

7.顶层文件代码

module uart_top(
	input 	sys_clk,	//系统时钟
	input 	sys_rst_n,	//系统复位
 
	input 	uart_rxd,	//接收端口
	output 	uart_txd	//发送端口
 
);
parameter	UART_BPS=9600;			//波特率
parameter	CLK_FREQ=50_000_000;	//系统频率50M	
 
wire uart_en_w;
wire [7:0] uart_data_w; 
 
//例化发送模块
uart_tx#(
	.BPS		    (UART_BPS),
	.SYS_CLK_FRE	(CLK_FREQ))
u_uart_tx(
	.sys_clk		(sys_clk),
	.sys_rst_n	    (sys_rst_n),
	.uart_tx_en		(uart_en_w),
	.uart_data	    (uart_data_w),	
	.uart_txd	    (uart_txd)
);
//例化接收模块
uart_rx #(
	.BPS				(UART_BPS),
	.SYS_CLK_FRE		(CLK_FREQ))
u_uart_rx(
	.sys_clk			(sys_clk),
	.sys_rst_n		    (sys_rst_n),
	
	.uart_rxd		    (uart_rxd),	
	.uart_rx_done	    (uart_en_w),
	.uart_rx_data	    (uart_data_w)
);
 
endmodule

8.顶层文件测试代码

`timescale 1ns/1ns	//定义时间刻度
//模块、接口定义
module uart_top_tb();
reg 		sys_clk;			
reg 		sys_rst_n;			
reg 		uart_rxd;
		
wire 	 	uart_txd;
 
//例化被测试的接收模块
uart_top #(
	.UART_BPS		(9600),		//波特率9600
	.CLK_FREQ		(50_000_000)//时钟频率50M	
)
u_uart_top(
	.sys_clk		(sys_clk),			
	.sys_rst_n		(sys_rst_n),			
	.uart_rxd		(uart_rxd),			
	.uart_txd		(uart_txd)	
);
 
localparam	CNT=50_000_000/9600*20;	//计算出传输每个时钟所需要的时间
initial begin	//传输8位数据	8'b01010101
//初始时刻定义
		sys_clk	<=1'b0;	
		sys_rst_n<=1'b0;		
		uart_rxd<=1'b1;
	#20 //系统开始工作
		sys_rst_n<=1'b1;
	#(CNT/2)	
		uart_rxd<=1'b0;//开始传输起始位
	#CNT		
		uart_rxd<=1'b1;//传输最低位,第1位
	#CNT		
		uart_rxd<=1'b0;//传输第2位
	#CNT		
		uart_rxd<=1'b1;//传输第3位
	#CNT		
		uart_rxd<=1'b0;	//传输第4位
	#CNT		
		uart_rxd<=1'b1;//传输第5位
	#CNT		
		uart_rxd<=1'b0;//传输第6位
	#CNT		
		uart_rxd<=1'b1;//传输第7位
	#CNT		
		uart_rxd<=1'b0;	//传输最高位,第8位
	#CNT		
		uart_rxd<=1'b1;	//传输终止位
end
 
always begin
	#10	sys_clk=~sys_clk;	//时钟20ns,50M
end
 
endmodule


9.顶层模块仿真代码

在这里插入图片描述

整个系统的框图
在这里插入图片描述

		---晓凡 20227月于桂林书
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值