UART串口通信(回环测试)

2023-05-16

一 UART串口通信简介

        UART(Universal Asynchronous Receiver-Transmitter)是采用异步串行通信方式的通用异步收发传输器,在发送数据时将并行数据转换为串行数据,在接收数据时将串行数据转换为并行数据。发送和接收的一帧数据由起始位、数据位、奇偶校验位和停止位组成,其数据帧格式如下:

        串口通信的速率用波特率表示,单位为bps(位/秒),常用的波特率由9600、19200、38400、57600、115200。

        针对异步串行通信接口的标准有RS232、RS422、RS485等,其电器特性如下表所示:

接口类型

RS232

RS422

RS485

工作方式

单端(全双工)

差分(全双工)

差分(半双工)

节点数

1发1收

1发10收

1发32收

逻辑特性

逻辑1:-5V~-15V

逻辑0:+5V~+15V

逻辑1:+2V~+6V

逻辑0:-2V~-6V

逻辑1:+2V~+6V

逻辑0:-2V~-6V

传输速率

20Kbps

10Mbps

10Mbps

传输距离

15m

1200m

3000m

二UART串口通信实验

1. 实验内容

        UART串口通信实验内容位:当上位机通过串口调试工具发送数据给FPGA,FPGA通过串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。当上位机未发送数据时候,FPGA每隔1s产生Hello World!发送给上位机并通过串口调试工具显示。

2.工程代码

2.1串口发送模块

串口发送模块将输入的8位并行数据进行串行输出。串口发送模块uart_tx代码如下:

1  `timescale	1ns/1ps
2  module uart_tx
3  #(
4  	parameter	CLK_FREQ	=	50_000_000,			//系统时钟频率
5  	parameter	UART_BPS	=	9600				//串口波特率
6  )
7  (
8  	input					clk,
9  	input					rst_n,
10 	
11 	input			[7:0]	tx_data,			//待发送数据
12 	input					tx_en,				//发送使能信号
13 	output					uart_tx_busy,		//发送忙碌标志信号
14 	output	reg				uart_txd			//UART数据发送端口
15 	
16 );
17 
18 localparam	BPS_CNT	=	CLK_FREQ/UART_BPS;//当前波特率需要系统时钟计数BPS_CNT次
19 
20 reg			tx_en_r1;
21 reg			tx_en_r2;
22 
23 reg  [15:0]	clk_cnt;			//系统时钟计数器
24 reg  [3:0]	tx_bit_cnt;		//发送数据位计数器
25 reg			tx_flag;			//发送过程标志位
26 reg			bit_flag;
27 
28 wire		tx_start;
29 
30 //uart_tx_busy:串口发送忙碌状态
31 assign	uart_tx_busy	=	tx_flag;
32 
33 //tx_start:串口数据开始发送信号,捕获tx_en上升沿
34 assign	tx_start	=	!tx_en_r2 && tx_en_r1;
35 always @(posedge clk	or negedge rst_n)
36 begin
37 	if(!rst_n)
38 	begin
39 		tx_en_r1	<=	1'b0;
40 		tx_en_r2	<=	1'b0;	
41 	end
42 	else
43 	begin
44 		tx_en_r1	<=	tx_en;
45 		tx_en_r2	<=	tx_en_r1;
46 	end
47 end
48 
49 //tx_flag:接收过程标志信号
50 always @(posedge clk or negedge rst_n)
51 begin
52 	if(!rst_n)
53 		tx_flag	<=	1'b0;
54 	else if(tx_start)
55 		tx_flag	<=	1'b1;
56 	else if(tx_bit_cnt == 4'd9 && bit_flag == 1'b1)
57 		tx_flag <=	1'b0;
58 	else
59 		tx_flag	<=	tx_flag;		
60 end
61 
62 //clk_cnt:系统时钟计数器
63 always @(posedge clk or negedge rst_n)
64 begin
65 	if(!rst_n)
66 		clk_cnt	<=	16'd0;
67 	else if(tx_flag == 1'b0 || clk_cnt == BPS_CNT - 1'b1)
68 		clk_cnt	<=	16'd0;
69 	else if(tx_flag)
70 		clk_cnt	<=	clk_cnt + 1'b1;
71 	else
72 		clk_cnt	<=	16'd0;
73 end
74 
75 //bit_flag:当clk_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
76 always @(posedge clk or negedge rst_n)
77 begin
78 	if(!rst_n)
79 		bit_flag	<=	1'b0;
80 	else if(clk_cnt == 16'd1)
81 		bit_flag 	<=	 1'b1;
82 	else 
83 		bit_flag	<=	1'b0;
84 end
85 
86 //tx_bit_cnt:发送数据位计数器
87 always @(posedge clk or negedge rst_n)
88 begin
89 	if(!rst_n)
90 		tx_bit_cnt	<=	4'd0;
91 	else if((tx_bit_cnt == 4'd9) && (bit_flag == 1'b1))
92 		tx_bit_cnt	<=	4'd0;
93 	else if(bit_flag && tx_flag)
94 		tx_bit_cnt	<=	tx_bit_cnt + 1'b1;
95 	else 
96 		tx_bit_cnt	<=	tx_bit_cnt;
97 end	
98 
99 //uart_txd:根据发送数据计数器来给uart发送端口赋值
100always @(posedge clk or negedge rst_n)
101begin
102	if(!rst_n)
103		uart_txd	<=	1'b1;
104	else	if(bit_flag == 1'b1)
105	begin
106		case(tx_bit_cnt)
107			4'd0:uart_txd	<=	1'b0;				//发送起始位
108			4'd1:uart_txd	<=	tx_data[0];		//发送数据低位
109			4'd2:uart_txd	<=	tx_data[1];
110			4'd3:uart_txd	<=	tx_data[2];
111			4'd4:uart_txd	<=	tx_data[3];
112			4'd5:uart_txd	<=	tx_data[4];
113			4'd6:uart_txd	<=	tx_data[5];
114			4'd7:uart_txd	<=	tx_data[6];
115			4'd8:uart_txd	<=	tx_data[7];
116			4'd9:uart_txd	<=	1'b1;				//发送数据高位
117			default:uart_txd <= 1'b1;
118		endcase
119	end
120	else
121		uart_txd	<=	uart_txd;
122end
123
124endmodule
125

仿真代码如下:

1 	`timescale  1ns/1ps
2 	module uart_tx_tb();
3 
4 	reg			clk;
5 	reg			rst_n;
6 	reg	[7:0]	tx_data;		
7 	reg			tx_en;
8 		
9 	wire		uart_tx_busy;
10	wire		uart_txd;
11
12	initial 
13	begin
14		clk	=	1'b1;
15		rst_n	=	1'b0;
16		#200
17		rst_n	=	1'b1;
18	end
19
20	always #10	clk = ~clk;
21
22	initial begin
23			tx_data <= 8'b0;
24			tx_en 	<= 1'b0;
25			#200
26			//发送数据0
27			tx_data	<= 8'd0;
28			tx_en 	<= 1'b1;
29			#20
30			tx_en 	<= 1'b0;
31	//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
32	//所以需要数据延时(5208*20*10)后再产生下一个数据
33			#(5208*20*10);
34			//发送数据1
35			tx_data<= 8'd1;
36			tx_en 	<= 1'b1;
37			#20
38			tx_en <= 1'b0;
39			#(5208*20*10);
40			//发送数据2
41			tx_data <= 8'd2;
42			tx_en <= 1'b1;
43			#20
44			tx_en <= 1'b0;
45			#(5208*20*10);
46			//发送数据3
47			tx_data<= 8'd3;
48			tx_en 	<= 1'b1;
49			#20
50			tx_en <= 1'b0;
51			#(5208*20*10);
52			//发送数据4
53			tx_data<= 8'd4;
54			tx_en 	<= 1'b1;
55			#20
56			tx_en 	<= 1'b0;
57			#(5208*20*10);
58			//发送数据5
59			tx_data<= 8'd5;
60			tx_en 	<= 1'b1;
61			#20
62			tx_en 	<= 1'b0;
63			#(5208*20*10);
64			//发送数据6
65			tx_data<= 8'd6;
66			tx_en 	<= 1'b1;
67			#20
68			tx_en <= 1'b0;
69			#(5208*20*10);
70			//发送数据7
71			tx_data<= 8'd7;
72			tx_en 	<= 1'b1;
73			#20
74			tx_en 	<= 1'b0;
75	end
76
77	uart_tx	u_uart_tx(
78		.clk(clk),
79		.rst_n(rst_n),
80		
81		.tx_data(tx_data),				//待发送数据
82		.tx_en(tx_en),					//发送使能信号
83		.uart_tx_busy(uart_tx_busy),	//发送忙碌标志信号
84		.uart_txd(uart_txd)				//UART数据发送端口
85		
86	);
87
88	endmodule
89

 仿真结果如下:

        仿真结果显示串口发送模块成功将数据0~7以UART数据帧格式进行发送。

2.2串口接收模块

        串口接收模块将串行的8位数据转化为并行的8位数据进行输出。串口接收模块uart_rx代码如下:

1  	`timescale 1ns/1ps
2  	module uart_rx
3  	#(
4  		parameter	CLK_FREQ	=	50_000_000,			//系统时钟频率
5  		parameter	UART_BPS	=	9600				//串口波特率
6  	)
7  	(
8  		input				clk,
9  		input				rst_n,
10 		
11 		input				uart_rxd,
12 		output				uart_rx_busy,
13 		output reg			rx_done,
14 		output reg [7:0]	rx_data
15 	);
16 
17 	localparam	BPS_CNT	=	CLK_FREQ/UART_BPS;//当前波特率需要系统时钟计数BPS_CNT次
18 
19 	reg			uart_rxd_r1;
20 	reg			uart_rxd_r2;
21 	reg [15:0]	clk_cnt;
22 	reg [3:0]	rx_bit_cnt;
23 	reg [7:0]	rx_data_r;
24 	reg			rx_flag;
25 	reg			bit_flag;
26 	reg			rx_done_r;
27 
28 
29 	assign	uart_rx_busy	=	rx_flag;
30 	wire	rx_start;
31 	//rx_start:接收开始信号
32 	assign	rx_start	=	uart_rxd_r2 && !uart_rxd_r1;
33 	always @(posedge clk or negedge rst_n)
34 	begin
35 		if(!rst_n)
36 		begin
37 			uart_rxd_r1	<=	1'b1;
38 			uart_rxd_r2	<=	1'b1;
39 		end
40 		else
41 		begin
42 			uart_rxd_r1	<=	uart_rxd;
43 			uart_rxd_r2	<=	uart_rxd_r1;
44 		end
45 	end
46 
47 	//rx_flag:接收过程标志位
48 	always @(posedge clk or negedge rst_n)
49 	begin
50 		if(!rst_n)
51 			rx_flag	<=	1'b0;
52 		else if(rx_start)
53 			rx_flag	<=	1'b1;
54 		else if(rx_bit_cnt == 4'd8 && bit_flag == 1'b1)
55 			rx_flag	<=	1'b0;
56 		else
57 			rx_flag	<=	rx_flag;
58 	end
59 
60 	//clk_cnt:系统时钟计数器
61 	always @(posedge clk or negedge rst_n)
62 	begin
63 		if(!rst_n)
64 			clk_cnt	<=	16'd0;
65 		else if(clk_cnt == BPS_CNT - 1'b1)
66 			clk_cnt	<=	16'd0;
67 		else if(rx_flag)
68 			clk_cnt	<=	clk_cnt + 1'b1;
69 		else
70 			clk_cnt	<=	16'd0;
71 	end
72 
73 	//bit_flag:接收过程标志位
74 	always @(posedge clk or negedge rst_n)
75 	begin
76 		if(!rst_n)
77 			bit_flag	<=	1'b0;
78 		else if(clk_cnt == BPS_CNT/2 - 1'b1)
79 			bit_flag	<=	1'b1;
80 		else
81 			bit_flag	<=	1'b0;
82 	end
83 
84 	//rx_bit_cnt:接收完成标志位
85 	always @(posedge clk or negedge rst_n)
86 	begin
87 		if(!rst_n)
88 			rx_bit_cnt	<=	4'd0;
89 		else if((rx_bit_cnt == 4'd8) && (bit_flag == 1'b1))
90 			rx_bit_cnt	<=	4'd0;
91 		else if(bit_flag == 1'b1)
92 			rx_bit_cnt	<=	rx_bit_cnt + 1'b1;
93 		else
94 			rx_bit_cnt	<=	rx_bit_cnt;
95 	end
96 
97 	//rx_data_r:输入数据进行移位
98 	always @(posedge clk or negedge rst_n)
99 	begin
100		if(!rst_n)
101			rx_data_r	<=	8'd0;
102		else if((rx_bit_cnt >= 4'd1) &&(rx_bit_cnt <= 4'd8) && (bit_flag == 1'b1))
103			rx_data_r	<=	{uart_rxd_r2 , rx_data_r[7:1]};
104		else
105			rx_data_r	<=	rx_data_r;
106	end
107
108	//rx_done_r:接收完成标志位
109	always @(posedge clk or negedge rst_n)
110	begin
111		if(!rst_n)
112			rx_done_r	<=	1'b0;
113		else if((rx_bit_cnt == 4'd8) && (bit_flag == 1'b1))
114			rx_done_r	<=	1'b1;
115		else
116			rx_done_r	<=	1'b0;
117	end
118
119	//rx_data:输出完整的8位有效数据
120	always @(posedge clk or negedge rst_n)
121	begin
122		if(!rst_n)
123			rx_data	<=	8'd0;
124		else if(rx_done_r)
125			rx_data	<=	rx_data_r;
126		else
127			rx_data	<=	rx_data;
128	end
129
130	//rx_done:接收完成标志位
131	always @(posedge clk or negedge rst_n)
132	begin
133		if(!rst_n)
134			rx_done	<=	1'b0;
135		else
136			rx_done	<=	rx_done_r;
137	end
138
139	endmodule

仿真代码如下:

1 	`timescale  1ns/1ns
2 	module uart_rx_tb();
3 
4 	reg				clk;
5 	reg				rst_n;
6 	reg				uart_rxd;
7 	wire			rx_done;
8 	wire [7:0]		rx_data;
9 
10	initial 
11	begin
12		clk			=	1'b1;
13		rst_n		=	1'b0;
14	 	uart_rxd  	= 	1'b1;
15		#200
16		rst_n		=	1'b1;
17	end
18
19	always #10	clk = ~clk;
20
21	initial 
22	begin
23		#200
24		rx_bit_data(8'd1);
25		rx_bit_data(8'd2);
26		rx_bit_data(8'd3);
27		rx_bit_data(8'd4);
28		rx_bit_data(8'd5);
29		rx_bit_data(8'd6);
30		rx_bit_data(8'd7);
31		rx_bit_data(8'd8);
32		rx_bit_data(8'd9);
33	end
34
35	task rx_bit_data(
36		input	[7:0]	data
37	);
38		integer	 i;
39		for(i = 0; i < 10; i = i + 1'b1)
40		begin
41			case(i)
42				0:uart_rxd	<=	1'b0;
43				1:uart_rxd	<=	data[0];
44				2:uart_rxd	<=	data[1];
45				3:uart_rxd	<=	data[2];
46				4:uart_rxd	<=	data[3];
47				5:uart_rxd	<=	data[4];
48				6:uart_rxd	<=	data[5];
49				7:uart_rxd	<=	data[6];
50				8:uart_rxd	<=	data[7];
51				9:uart_rxd 	<= 1'b1;
52			endcase
53			#(5208*20);
54		end	
55	endtask
56
57	uart_rx	u_uart_rx(
58		.clk(clk),
59		.rst_n(rst_n),
60		
61		.uart_rxd(uart_rxd),
62		.rx_done(rx_done),
63		.rx_data(rx_data)
64	);
65
66	endmodule

仿真结果如下: 

        仿真结果显示,rx_data成功将uart_rxd输入的串行数据转换为8位并行数据输出,rx_done为接收一帧数据完成信号。

2.3控制模块

       控制模块uart_ctrl用于控制串口通信发送使能和发送数据信号,当检测到s上位机串口调试工具助手发送数据给FPGA时,FPGA接收模块接收数据后并将发送到上位机完成数据回环。当上位机未发送数据时候,FPGA每隔1s产生Hello World!发送给上位机并通过串口调试工具显示。控制模块uart_ctrl代码如下:

1  	`timescale 1ns/1ps
2  	module uart_ctrl 
3  	#(
4  		parameter	CLK_FREQ	=	50_000_000,			//系统时钟频率
5  		parameter	UART_BPS	=	9600				//串口波特率
6  	)
7  	(
8  		input					clk,
9  		input					rst_n,
10 		
11 		input					rx_done,
12 		input		[7:0]	    rx_data,
13 		
14 		input					uart_tx_busy,
15 		output 				    uart_en,
16 		output 		[7:0]	    uart_data
17 
18 	);
19 
20 	localparam	BPS_CNT	=	CLK_FREQ/UART_BPS;//当前波特率需要系统时钟计数BPS_CNT次
21 
22 	reg			rx_done_r1;
23 	reg			rx_done_r2;
24 	reg			tx_start;
25 	reg			tx_en;
26 	reg	[7:0]	tx_data;
27 
28 	reg	[25:0]	uart_wait;			//发送等待时间计数器
29 	reg	[19:0]	uart_cnt;			//发送时间计数器
30 	reg			rx_data_valid;		//接收数据有效信号
31 	reg [7:0] 	store [14:0];		//存储待发送的8位数据  
32 	reg [2:0] 	uart_state;   		//状态机
33 	reg	[7:0]	bit_cnt;				//发送数据计数器
34 	reg			data_sel;			//发送数据选择信号
35 	reg			tx_en_r;
36 	reg	[7:0]	tx_data_r;
37 
38 	wire		rx_done_flag;
39 
40 	//rx_done_flag:接收完成标志信号
41 	assign	rx_done_flag	=	!rx_done_r2 && rx_done_r1;
42 	always @(posedge clk or negedge rst_n)
43 	begin
44 		if(!rst_n)
45 		begin
46 			rx_done_r1	<=	1'b0;
47 			rx_done_r2	<=	1'b0;
48 		end
49 		else
50 		begin
51 			rx_done_r1	<=	rx_done;
52 			rx_done_r2	<=	rx_done_r1;
53 		end
54 	end
55 
56 	//tx_en:发送使能信号
57 	//tx_data:发送数据
58 	always @(posedge clk or negedge rst_n)
59 	begin
60 		if(!rst_n)
61 		begin
62 			tx_start	<=	1'b0;
63 			tx_en		<=	1'b0;
64 			tx_data		<=	8'd0;
65 		end
66 		else 
67 		begin
68 			if(rx_done_flag)
69 			begin
70 				tx_start	<=	1'b1;
71 				tx_en		<=	1'b0;
72 				tx_data		<=	rx_data;
73 			end
74 			else if(tx_start && !uart_tx_busy)
75 			begin
76 				tx_start	<=	1'b0;
77 				tx_en		<=	1'b1;
78 				tx_data		<=	tx_data;
79 			end
80 			else
81 			begin
82 				tx_start	<=	1'b0;
83 				tx_en		<=	1'b0;
84 				tx_data		<=	tx_data;		
85 			end
86 		end
87 	end
88 
89 	//uart_en:串口发送使能信号
90 	//uart_data:串口发送数据
91 	//发送数据选择:data_sel高,选择存储的字符串,data_sel:低,选择接收的数据
92 	assign	uart_en		=	data_sel ? tx_en_r 	    :  tx_en	;
93 	assign	uart_data	=	data_sel ? tx_data_r	:  tx_data	;
94 
95 	//存储待发送的数据
96 	always @(*)
97 	begin
98 		if(!rst_n)
99 		begin
100			store[0]	=	8'd72; 		//存储字符H
101			store[1]	=  	8'd101;		//存储字符e	
102			store[2]	=	8'd108;		//存储字符l
103			store[3]	=	8'd108;		//存储字符l
104			store[4]	=	8'd111;		//存储字符o
105			store[5]	=	8'd32;    	//存储字符空格
106			store[6]	=	8'd87;    	//存储字符W
107			store[7]	=	8'd111;    	//存储字符o
108			store[8]	=	8'd114;    	//存储字符r
109			store[9]	=	8'd108;    	//存储字符l
110			store[10]	=	8'd100;    	//存储字符d
111			store[11]	=	8'd33;    	//存储字符!
112			store[12]	=	8'd32;    	//存储字符空格
113			store[13]	=	8'd10;      //换行符	
114			store[14]	=	8'd13;      //回车符	
115		end
116		else
117		begin
118			store[0]	=	store[0];
119			store[1]	=  	store[1];
120			store[2]	=	store[2];
121			store[3]	=	store[3];
122			store[4]	=	store[4];
123			store[5]	=	store[5];
124			store[6]	=	store[6];
125			store[7]	=	store[7];
126			store[8]	=  	store[8];
127			store[9]	=	store[9];
128			store[10]	=	store[10];
129			store[11]	=	store[11];
130			store[12]	=	store[12];
131			store[13]	=	store[13];
132			store[14]	=	store[14];
133		end
134	end
135
136	 //串口发送控制,每个一段时间发送字符串的命令
137	always @(posedge clk or negedge rst_n)
138	begin
139		if(!rst_n)
140		begin
141			uart_wait		<=	26'd0;
142			rx_data_valid	<=	1'b0;
143		end
144		else if(rx_done)
145		begin
146			uart_wait		<=	26'd0;
147			rx_data_valid	<=	1'b0;
148		end
149		else
150		begin
151			if(uart_wait == 26'd49_999_999)
152			begin
153				uart_wait		<=	26'd0;
154				rx_data_valid	<=	1'b1;
155			end
156			else
157			begin
158				uart_wait		<=	uart_wait + 1'b1;
159				rx_data_valid	<=	1'b0;
160			end
161		end
162	end
163
164	// //串口发送字符串数据,依次发送存储的字符串
165	always @(posedge clk or negedge rst_n)
166	begin
167		if(!rst_n)
168		begin
169			uart_cnt	<=	20'd0;
170			uart_state	<=	3'b001;
171			data_sel	<=	1'b0;
172			bit_cnt		<=	8'd0;
173			tx_data_r	<=	8'd0;
174			tx_en_r		<=	1'b0;
175		end
176		else
177		begin
178			if(rx_done)
179			begin
180				uart_cnt	<=	20'd0;
181				uart_state	<=	3'b001;
182				data_sel	<=	1'b0;
183				bit_cnt		<=	8'd0;
184				tx_data_r	<=	8'd0;
185				tx_en_r		<=	1'b0;	
186			end
187			else
188			begin
189				case(uart_state)
190				
191				3'b001:begin
192					if(rx_data_valid)
193					begin
194						uart_state	<=	3'b010;
195						data_sel	<=	1'b1;
196					end
197					else
198					begin
199						uart_state	<=	3'b001;
200						data_sel	<=	1'b0;
201					end
202				end
203				
204				3'b010:begin
205					if(bit_cnt == 8'd14)
206					begin
207						if(uart_cnt	== 20'd0)
208						begin
209							tx_data_r	<=	store[14];
210							tx_en_r		<=	1'b1;
211							uart_cnt	<=	uart_cnt + 1'b1;	
212						end
213						else if(uart_cnt == BPS_CNT*10 - 1'b1)
214						begin
215							uart_state	<=	3'b100;
216							tx_en_r		<=	1'b0;
217							uart_cnt	<=	20'd0;
218							bit_cnt		<=	8'd0;
219						end
220						else
221						begin
222							uart_cnt	<=	uart_cnt + 1'b1;
223							tx_en_r		<=	1'b0;
224						end
225					end
226					else
227					begin
228						if(uart_cnt	== 20'd0)
229						begin
230							tx_data_r	<=	store[bit_cnt];
231							tx_en_r		<=	1'b1;	
232							uart_cnt	<=	uart_cnt + 1'b1;
233						end
234						else if(uart_cnt == BPS_CNT*10 - 1'b1)
235						begin
236							tx_en_r		<=	1'b0;
237							uart_cnt	<=	20'd0;
238							bit_cnt		<=	bit_cnt + 1'b1;
239						end
240						else
241						begin
242							uart_cnt	<=	uart_cnt + 1'b1;
243							tx_en_r		<=	1'b0;
244						end
245					
246					end
247				end
248				
249				3'b100:begin	//发送完成
250					uart_state	<=	3'b001;
251					data_sel	<=	1'b0;
252					tx_en_r		<=	1'b0;
253				end
254				
255				default: begin
256					uart_state	<=	3'b001;
257					tx_en_r		<=	1'b0;
258					uart_cnt	<=	20'd0;	
259					data_sel	<=	1'b0;
260					bit_cnt		<=	8'd0;
261					tx_data_r	<=	8'd0;
262				end
263				
264			endcase			
265			end
266		end
267	end
268	 
269	endmodule 

2.4顶层模块

        顶层模块uart_top用于完成uart_tx、uart_rx、uart_ctrl模块的例化。

1 	`timescale 1ns/1ps
2 	module uart_top(
3 		input			sys_clk,
4 		input			rst_n,
5 		
6 		input			uart_rxd,
7 		output			uart_txd
8 	);
9 
10	parameter  	CLK_FREQ    =   50_000_000 ;   //时钟频率
11	parameter   UART_BPS    =   9600       ;   //比特率
12
13	wire [7:0]	uart_data;
14	wire [7:0]	tx_data;
15	wire		uart_en;
16	wire		uart_tx_busy;
17	wire		rx_done;
18
19	uart_tx
20	#(
21		.CLK_FREQ(CLK_FREQ),		//系统时钟频率
22		.UART_BPS(UART_BPS)			//串口波特率
23	)
24	u_uart_tx(
25		.clk(sys_clk),
26		.rst_n(rst_n),
27		
28		.tx_data(tx_data),			//待发送数据
29		.tx_en(uart_en),			//发送使能信号
30		.uart_tx_busy(uart_tx_busy),//发送忙碌标志信号
31		.uart_txd(uart_txd)			//UART数据发送端口
32	);
33
34	uart_rx
35	#(
36		.CLK_FREQ(CLK_FREQ),		//系统时钟频率
37		.UART_BPS(UART_BPS)			//串口波特率
38	)
39	u_uart_rx(
40		.clk(sys_clk),
41		.rst_n(rst_n),
42		
43		.uart_rxd(uart_rxd),
44		.uart_rx_busy(),
45		.rx_done(rx_done),
46		.rx_data(uart_data)
47	);
48
49	uart_ctrl 
50	#(
51		.CLK_FREQ(CLK_FREQ),		//系统时钟频率
52		.UART_BPS(UART_BPS)			//串口波特率
53	)
54	u_uart_ctrl(
55		.clk(sys_clk),
56		.rst_n(rst_n),
57		
58		.rx_done(rx_done),
59		.rx_data(uart_data),
60		
61		.uart_tx_busy(uart_tx_busy),
62		.uart_en(uart_en),
63		.uart_data(tx_data)
64	);
65
66	endmodule

 仿真代码如下:

1 	`timescale  1ns/1ps
2 	module uart_top_tb();
3 
4 	reg		sys_clk;
5 	reg		rst_n;
6 	reg		uart_rxd;
7 
8 	wire	uart_txd;
9 
10	initial 
11	begin
12		sys_clk		= 	1'b1;
13		rst_n		=	1'b0;
14		uart_rxd	=	1'b1;
15		#200
16		rst_n		=	1'b1;
17	end
18
19	always #10 sys_clk	=	~sys_clk;
20
21	initial 
22	begin
23		#400
24		rx_byte();
25	end
26
27	task rx_byte();
28		integer	j;
29		for(j = 1; j < 10; j = j + 1'b1)
30			rx_bit_data(j);
31	endtask
32
33	task rx_bit_data(
34		input	[7:0]	data
35	);
36		integer i;
37		for(i = 0; i < 10; i = i + 1'b1)
38		begin
39			case(i)
40				0:uart_rxd	<=	1'b0;
41				1:uart_rxd	<=	data[0];
42				2:uart_rxd	<=	data[1];
43				3:uart_rxd	<=	data[2];
44				4:uart_rxd	<=	data[3];
45				5:uart_rxd	<=	data[4];
46				6:uart_rxd	<=	data[5];
47				7:uart_rxd	<=	data[6];
48				8:uart_rxd	<=	data[7];
49				9:uart_rxd 	<= 1'b1;
50			endcase
51			#(5208*20);
52		end	
53	endtask
54
55	uart_top u_uart_top(
56		.sys_clk(sys_clk),
57		.rst_n(rst_n),
58		
59		.uart_rxd(uart_rxd),
60		.uart_txd(uart_txd)
61	);
62
63	endmodule

仿真结果如下所示: 

        首先验证串口通信回环测试,激励模块产生数据1~9,接收模块完成数据接收后,将并行的8位1~9数据传输到uart_tx模块,uart_tx将8位数据进行串行输出,仿真结果显示,回环测试成功。当未检测到rx_done接收信号时,uart_wait计数器每计数到1s,使能rx_data_valid信号,发送store寄存的Helllo World!。

3板级验证

下载代码到Alter Cyclone IV FPGA开发板中,设置波特率为9600,打开串口调试助手,接收区每隔1s接收一个Hello World!,当手动发送123456 数据时,串口接收区显示接收123456 字符。当停止发送123456 字符时,接收区每隔1s会接收到一个Hello World!。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UART串口通信(回环测试) 的相关文章

  • 字节序(byte order)和位序(bit order)

    一 网络字节序与主机字节序 在Linux网络编程中 xff0c 经常碰到网络字节序与主机字节序的相互转换 说到网络字节序与主机字节序需要清晰了解以下几个概念 1 字节序 字节序 xff0c 顾名思义 xff0c 指字节 Byte 在内存中存
  • 虚拟机和主机相互ping不通方法总结

    一 虚拟机和主机相互ping不通的解决方法 1 主要检查几个方面 xff1a 1 xff09 检查虚拟网卡有没有被禁用 2 xff09 检查虚拟机与物理机是否在一个VMNet中 3 xff09 检查虚拟机的IP地址与物理机对应的VMNet是
  • grafana告警规则设置

    Linux 内存告警配置 问题 xff1a Template variables are not supported in alert queries 解决办法 xff1a 单独配置个告警的视图 xff0c 用正则匹配出所有的主机 或者 每
  • 无法启动此程序因为计算机中丢失vcruntime140_1.dll

    安装mysql8的时候发现需要VCRUNTIME140 1 dll文件 该文件在C Windows System32下面 解决方法 推荐去微软官网下载最新的2015 2019vc 43 43 支持包 https support micros
  • Failed to start mysqld.service: Unit not found问题解决方案

    1 首先安装mariadb server yum install y mariadb server 2 启动服务 systemctl start mariadb service 3 添加到开机启动 systemctl enable mari
  • 【常见】CSS3进度条Loading动画(一)

    现在 xff0c GIF 格式的进度条已经越来越少 xff0c CSS 进度条如雨后春笋般涌现 CSS3的崛起 xff0c 更使得动态效果得以轻松实现 xff0c 未来 xff0c 必定是CSS3的天下 xff0c 所以今天我就来分享一下几
  • ssh免密报错:Could not resolve hostname : Name or service not known

    解决方法 xff1a 需要将对应的ip地址加到文件名 etc hosts中 如 xff1a 192 168 128 129 cdh1 192 168 128 130 cdh2 192 168 128 131 cdh3
  • Failed to execute operation: No such file or directory

    安装iptables服务 yum span class token function install span iptables services 然后再执行 xff1a systemctl enable iptables service
  • 为什么块的大小不能设置太小,也不能设置太大?

    xff08 1 xff09 HDFS的块设置太小 xff0c 会增加寻址时间 xff0c 程序一直在找块的开始位置 xff1b xff08 2 xff09 如果块设置的太大 xff0c 从磁盘传输数据的时间会明显大于定位这个块开始位置所需的
  • 深入理解混洗过程

    混洗过程 Map方法之后 xff0c Reduce方法之前的数据处理过程称之为Shuffle 混洗过程 map方法之后 首先进入getpartition方法 标记数据属于那个分区 并打上分区编号 因为后续的数据都是按分区处理 不同分区数据会
  • 大数据集群写数据流程原理分析

    写入数据流程 即客户端如何把数据写入hdfs集群 底层原理如下 右侧为hdfs集群 含有NameNode和DataNode节点 左侧为客户端准备把数据传送到集群 首先创建客户端 分布式的文件系统 创建好客户端后向NameNode请求 Nam
  • 集群资源管理基础架构和工作机制

    Yarn的基础架构 RM为整个yarn集群资源老大 NM为单个节点资源老大 AM为单个应用程序的资源老大 container容器 xff0c 集群的资源抽象 最小单位 xff1b Yarn工作机制 Job gt RM 在命令行上执行一个wc
  • OpenSSL 3.0 版本已经发布VS2019编译源码教程

    1 OpenSSL 3 0版本在2020年五月发布 OpenSSL3 0 在2020年5月发布了第一个版本 xff0c 随即在六月份又发布了一些alpha版本 xff0c 版本修正了一些bug xff0c 增加了一些协议支持 xff0c 继
  • FFmpeg 4.3 源码在windows上的编译

    FFmpeg开发环境准备 nbsp nbsp 学习目标 nbsp nbsp nbsp 学会配置vs2019 msys2 编译环境 nbsp nbsp nbsp 学会编译x264 x265 fdk aac sdl ffmpeg4 3 编译目的
  • cmake使用方法详解 - Windows Linux MacOS cmake安装教程

    一 什么是CMake xff0c 为什么要使用它 CMake 是用于构建 测试和软件打包的开源跨平台工具 xff0c 为什么要用cmake xff0c 是因为我们需要一个构建系统解决如下问题 xff1a 1 你想避免硬编码路径 2 您需要在
  • cmake是什么,为什么现在都用cmake,cmake编译原理和跨平台示例

    一 cmake是什么 xff1f CMake是一个开源 跨平台的工具系列 xff0c 是用来构建 测试和打包软件 CMake使用平台无关的配置文件来控制软件编译过程 xff0c 并生成可在您选择的编译器环境中使用项目文件 xff0c 比如可
  • Socket通信原理

    对TCP IP UDP Socket编程这些词你不会很陌生吧 xff1f 随着网络技术的发展 xff0c 这些词充斥着我们的耳朵 那么我想问 xff1a 1 什么是TCP IP UDP xff1f 2 Socket在哪里呢 xff1f 3
  • cmake add_library编译链接静态库cmakelists

    本篇文章我们来编写CMakeLists txt使用cmake的add library的构建静态库 xff0c 并使用target link libraries链接指定的静态库 cmake的linux windows 和linux环境的准备可
  • FFmpeg3最新的解码接口avcodec_send_packet和avcodec_receive_frame分析

    ffmpeg3版本的解码接口做了不少调整 xff0c 之前的视频解码接口avcodec decode video2和avcodec decode audio4音频解码被设置为deprecated xff0c 对这两个接口做了合并 xff0c
  • ffmpeg3.3新版本AVStream的封装流参数由codec替换codecpar(解码)

    ffmpeg新版本中 xff08 封装流 xff09 AVStream的codec参数要被codecpar参数所替代 xff0c 这样替代我们要注意什么 xff0c 为什么要替代 xff0c 我们先来看下ffmpeg的代码 代码分析和新参数

随机推荐

  • 为什么 qt 成为 c++ 界面编程的第一选择?

    为什么qt成为c 43 43 界面编程的第一选择 一 前言 为什么现在QT越来越成为界面编程的第一选择 xff0c 笔者从事qt界面编程已经有接近8年 xff0c 在这之前我做C 43 43 界面都是基于MFC xff0c 也做过5年左右
  • C++QT5跨平台界面编程原理和实战大全-夏曹俊-专题视频课程

    C 43 43 QT5跨平台界面编程原理和实战大全 4271人已学习 课程介绍 课程讲解基于QT5 9 SDK xff0c 包含QT界面编程的核心内容 xff0c 并提供全部源码 xff0c 课程讲解方式是接口讲解 分析 演示示例 xff0
  • Darknet问题:ERROR: Cannot load message class for [darknet_ros_msgs/BoundingBoxes].

    运行darknet ros进行目标检测时 xff0c 通过 rostopic list查看到 darknet ros bounding boxes这个话题 xff0c 因此想查看该话题的数据 xff0c 在使用 rostopic echo
  • ROS问题:ERROR: cannot launch node of type

    在运行launch文件时 xff0c 出现如下错误提示 xff1a 原因是没有source 所以解决方案就是在编译完成之后添加source devel setup bash xff0c 问题解决 参考网址 xff1a ERROR canno
  • 标注工具labelme的使用

    在做目标检测任务时 xff0c 少不了对图像进行标注 xff0c 标注工具有好几种 xff0c labelme是其中比较好用的一种 labelme可对图像进行标注 xff0c 包括多边形 矩形 线 点和图像级标注 它是用Python编写的
  • Linux小技巧之终端terminal全选

    当打开一个终端 xff0c 经过若干指令后 xff0c 终端上输出的内容较多 xff0c 直接框选这些内容进行选择比较费事 有没有全选的功能呢 xff1f 答案是有的 xff01 方法1 xff1a 终端菜单栏全选 当窗口比较小时 xff0
  • 如何使用set::key_comp 和 set::value_comp 标准模板库 (STL) 函数

    下面的代码示例演示如何使用 Visual C 43 43 set key comp 和 set value comp 的 STL 功能 所需要的头文件 xff1a lt set gt 原型 template lt class K class
  • Linux小技巧之终端快捷键大全

    在前面一篇博客中记录了终端全选的技巧 下面记录一下关于终端使用的其它一些小技巧 F1查看帮助F11全屏Shift 43 Ctrl 43 T 打开一个新的终端Shift 43 Ctrl 43 N新建一个窗口打开终端Shift 43 Ctrl
  • ROS问题:Yolo v4移植到ROS后检测结果/darknet_ros/detection_image在rviz中显示乱码

    在前面一篇博客 xff08 Yolo v4移植ROS xff09 中介绍了将Yolo v4移植到ROS中 由于Yolo v4的源码在Yolo v3源码的基础上有改动 xff0c 移植成功后会出现一个小bug xff0c 如下图所示 xff1
  • 吉洪诺夫正则化(Tikhonov regularization )

    最近看了看吉洪诺夫正则化方法 xff0c 对其基本内容作了一个简单的了解 现在总结如下 1 正则化 定义 xff1a 正则化 regularization xff0c 是指在线性代数理论中 xff0c 不适定问题通常是由一组线性代数方程定义
  • C++中getline()、gets()等函数的用法

    在学习C 43 43 的过程中 xff0c 经常会遇到输入输出的问题 xff0c 以下总结一下下面几个函数的用法 xff1a 1 cin 2 cin get 3 cin getline 4 getline 5 gets 1 cin gt g
  • C++字母大小写转换方法

    字母大小写这个问题相对比较简单 xff0c 总结了一些常用的大小写转换的方法 xff0c 欢迎指正补充 xff01 思路1 xff1a 根据字母的ASCII表进行转换 xff1a 由表格可以看出 xff0c 对应大小写字母之间相差32 xf
  • C++ 标准输出控制小数点后位数的方法

    在C 43 43 中 xff0c 要实现这个功能 xff0c 就要用到std命名空间中常用于流的控制符 xff0c 这里通常要用到setprecision 函数 xff0c 可以通过这个函数控制小数点后面位数 还要注意的是 xff0c 使用
  • C++中string::npos的一些用法总结

    一 关于npos的定义 在MSDN中有如下说明 xff1a basic string npos static const size type npos 61 1 定义 The constant is the largest represen
  • CMake:通过target_link_libraries链接第三方库

    sdbusplus 通过new method call同步调用service的method 风静如云的博客 CSDN博客 例子中需要在编译时链接 lsdbusplus lsystemd 这两个第三方库 那么通过cmake怎么指定呢 其实很简
  • 在ubuntu终端打开谷歌浏览器的命令

    安装好谷歌浏览器后 xff0c 用以下命令在终端打开谷歌浏览器 adb shell am start n com android chrome com google android apps chrome Main 之后便出现如下内容 xf
  • PELCO_D通信协议

    1 球机通信接口 xff08 EIA RS 485 xff09 数据传输方式 xff1a 异步半双工串行通讯 通信波特率 xff1a 9600Bps 数据格式 xff1a Start Bit xff1a 1 Bit xff1b Data B
  • C buffer

    这学期在Dartmouth上ENGS20 Introduction to Scientific Computing xff0c 好多东西不记下来就会忘 xff0c 所以开一个笔记 在C语言中 xff0c 输入和输出都是有buffer的 xf
  • 寄存器值的操作方法

    通过这段时间的工作和学习 xff0c 我感觉在嵌入式硬件编程中 xff0c 大多数情况下都是对相应硬件的功能寄存器进行设置和操作 一 寄存器的设置和操作特性 1 xff0c 一个寄存器的每个位有其不同的意义 xff0c 进行不同的设置会使硬
  • UART串口通信(回环测试)

    一 UART串口通信简介 UART xff08 Universal Asynchronous Receiver Transmitter xff09 是采用异步串行通信方式的通用异步收发传输器 xff0c 在发送数据时将并行数据转换为串行数据