• 【FPGA零基础学习之旅#15】串口接收模块设计与验证(工业环境)


    🎉欢迎来到FPGA专栏~串口接收模块设计与验证(工业环境)


    • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
    • 博客主页:小夏与酒的博客
    • 🎈该系列文章专栏:FPGA学习之旅
    • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
    • 📜 欢迎大家关注! ❤️
      FPGQ2

    CSDN

    遇见未来

    一、效果演示

    🥝效果展示:
    效果展示

    🥝接收展示:
    PC机的串口助手发送数据,FPGA接收数据并将数据显示到数码管上(hex格式数据):
    数码管显示

    🥝代码展示:

    串口接收模块:uart_byte_rx.v

    module uart_byte_rx(
    	input 				Clk,//50M
    	input 				Rst_n,
    	input 		[2:0]	baud_set,
    	input 				data_rx,
    	output 	reg [7:0]	data_byte,
    	output 	reg			Rx_Done
    );
    
    	reg s0_Rx,s1_Rx;//同步寄存器
    	
    	reg tmp0_Rx,tmp1_Rx;//数据寄存器
    	
    	reg [15:0]bps_DR;//分频计数器计数最大值
    	reg [15:0]div_cnt;//分频计数器
    	reg bps_clk;//波特率时钟
    	reg [7:0]bps_cnt;
    	
    	reg uart_state;
    	
    	reg [2:0] r_data_byte [7:0];
    	
    	reg [2:0]START_BIT;
    	reg [2:0]STOP_BIT;
    	
    	wire nedge;
    	
    //--------<同步寄存器处理>--------		
    //用于消除亚稳态
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			s0_Rx <= 1'b0;
    			s1_Rx <= 1'b0;
    		end
    		else begin
    			s0_Rx <= data_rx;
    			s1_Rx <= s0_Rx;
    		end
    	end
    	
    //--------<数据寄存器处理>--------		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			tmp0_Rx <= 1'b0;
    			tmp1_Rx <= 1'b0;
    		end
    		else begin
    			tmp0_Rx <= s1_Rx;
    			tmp1_Rx <= tmp0_Rx;
    		end
    	end
    	
    //--------<下降沿检测>--------	
    	assign nedge = !tmp0_Rx & tmp1_Rx;
    	
    //----------------	
    //得到不同计数周期的计数器
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			div_cnt <= 16'd0;
    		else if(uart_state)begin
    			if(div_cnt == bps_DR)
    				div_cnt <= 16'd0;
    			else
    				div_cnt <= div_cnt + 1'b1;
    		end
    		else
    			div_cnt <= 16'd0;
    	end
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_clk <= 1'b0;
    		else if(div_cnt == 16'd1)
    			bps_clk <= 1'b1;
    		else
    			bps_clk <= 1'b0;
    	end
    	
    //----------------		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_cnt <= 8'd0;
    		else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
    			bps_cnt <= 8'd0;
    		else if(bps_clk)
    			bps_cnt <= bps_cnt + 1'b1;
    		else
    			bps_cnt <= bps_cnt;
    	end
    	
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			Rx_Done <= 1'b0;
    		else if(bps_cnt == 8'd159)
    			Rx_Done <= 1'b1;
    		else
    			Rx_Done <= 1'b0;
    	end	
    	
    //--------<波特率查找表>--------		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_DR <= 16'd324;
    		else begin
    			case(baud_set)
    				0:bps_DR <= 16'd324;
    				1:bps_DR <= 16'd162;
    				2:bps_DR <= 16'd80;
    				3:bps_DR <= 16'd53;
    				4:bps_DR <= 16'd26;
    				default:bps_DR <= 16'd324;
    			endcase
    		end	
    	end
    
    //--------<采样数据接收模块>--------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			START_BIT <= 3'd0;
    			r_data_byte[0] <= 3'd0; 
    			r_data_byte[1] <= 3'd0;
    			r_data_byte[2] <= 3'd0; 
    			r_data_byte[3] <= 3'd0;
    			r_data_byte[4] <= 3'd0; 
    			r_data_byte[5] <= 3'd0;
    			r_data_byte[6] <= 3'd0; 
    			r_data_byte[7] <= 3'd0;
    			STOP_BIT <= 3'd0;
    		end
    		else if(bps_clk)begin
    			case(bps_cnt)
    				0:begin
    					START_BIT <= 3'd0;
    					r_data_byte[0] <= 3'd0;
    					r_data_byte[1] <= 3'd0;
    					r_data_byte[2] <= 3'd0;
    					r_data_byte[3] <= 3'd0;
    					r_data_byte[4] <= 3'd0;
    					r_data_byte[5] <= 3'd0;
    					r_data_byte[6] <= 3'd0;
    					r_data_byte[7] <= 3'd0;
    					STOP_BIT <= 3'd0; 
    				end
    				6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;
    				22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;
    				38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;
    				54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;
    				70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;
    				86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;
    				102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;
    				118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;
    				134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;
    				150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;
    				default:begin
    					START_BIT <= START_BIT;
    					r_data_byte[0] <= r_data_byte[0];
    					r_data_byte[1] <= r_data_byte[1];
    					r_data_byte[2] <= r_data_byte[2];
    					r_data_byte[3] <= r_data_byte[3];
    					r_data_byte[4] <= r_data_byte[4];
    					r_data_byte[5] <= r_data_byte[5];
    					r_data_byte[6] <= r_data_byte[6];
    					r_data_byte[7] <= r_data_byte[7];
    					STOP_BIT <= STOP_BIT;
    				end
    			endcase
    		end
    	end
    
    //--------<数据状态判定模块>--------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			data_byte <= 8'd0;
    		else if(bps_cnt == 8'd159)begin
    			data_byte[0] <= r_data_byte[0][2];
    			data_byte[1] <= r_data_byte[1][2];
    			data_byte[2] <= r_data_byte[2][2];
    			data_byte[3] <= r_data_byte[3][2];
    			data_byte[4] <= r_data_byte[4][2];
    			data_byte[5] <= r_data_byte[5][2];
    			data_byte[6] <= r_data_byte[6][2];
    			data_byte[7] <= r_data_byte[7][2];
    		end
    		else
    			;
    	end
    
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			uart_state <= 1'b0;
    		else if(nedge)
    			uart_state <= 1'b1;
    		else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
    			uart_state <= 1'b0;
    		else
    			uart_state <= uart_state;
    	end
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203

    二、模块设计思路

    🔸有关于串口发送模块设计的相关文章如下:
    【FPGA零基础学习之旅#13】串口发送模块设计与验证
    【FPGA零基础学习之旅#14】串口发送字符串

    在串口通信的一般应用中,采集每一位数据中间时刻的电平即认为是此位数据的电平。

    但是在实际工业应用中,现场往往有非常强的电磁干扰,只采样一次就作为该数据的电平状态是不可靠的。很有可能恰好采集到被干扰的信号而导致结果出错,因此提出以下改进型的单 bit 数据接收方式示意图,使用多次采样求概率的方式进行状态判定:
    采样方式
    将每一位数据再平均分成了 16 小段。对于 Bit_x 这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。也就是前面提到的中间测量方式,但是也不排除该段数据受强电磁干扰而出现错误的电平脉冲。因此对这一段电平,进行多次采样,并求高低电平发生的概率,6 次采集结果中,取出现次数多的电平作为采样结果。例如,采样 6 次的结果分别为 1/1/1/1/0/1/,则取电平结果为 1,若为 0/0/1/0/0/0,,则取电平结果为 0,当 6 次采样结果中 1 和 0 各占一半(各 3 次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进行处理。

    🥝串口接收模块的设计如下:
    模块设计
    🥝端口作用:

    信号名称功能描述
    Clk系统时钟 50Mhz
    Rst_n系统复位信号
    baud_set波特率选择信号
    data_rx串行数据输入
    data_byte并行数据输出
    Rx_Done接收结束信号

    三、代码详解

    Verilog HDL的一维数组讲解参考:verilog数组的定义、转换和加法器的实现

    在代码的编写过程中,可以参考串口发送数据时的设计框架:
    设计框架
    其中,采样时钟的计算变为如下:
    采样时钟的计算

    代码中较难理解的部分如下:

    //--------<采样数据接收模块>--------	
    always@(posedge Clk or negedge Rst_n)begin
    	if(!Rst_n)begin
    		START_BIT <= 3'd0;
    		r_data_byte[0] <= 3'd0; 
    		r_data_byte[1] <= 3'd0;
    		r_data_byte[2] <= 3'd0; 
    		r_data_byte[3] <= 3'd0;
    		r_data_byte[4] <= 3'd0; 
    		r_data_byte[5] <= 3'd0;
    		r_data_byte[6] <= 3'd0; 
    		r_data_byte[7] <= 3'd0;
    		STOP_BIT <= 3'd0;
    	end
    	else if(bps_clk)begin
    		case(bps_cnt)
    			0:begin
    				START_BIT <= 3'd0;
    				r_data_byte[0] <= 3'd0;
    				r_data_byte[1] <= 3'd0;
    				r_data_byte[2] <= 3'd0;
    				r_data_byte[3] <= 3'd0;
    				r_data_byte[4] <= 3'd0;
    				r_data_byte[5] <= 3'd0;
    				r_data_byte[6] <= 3'd0;
    				r_data_byte[7] <= 3'd0;
    				STOP_BIT <= 3'd0; 
    			end
    			6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;
    			22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;
    			38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;
    			54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;
    			70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;
    			86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;
    			102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;
    			118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;
    			134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;
    			150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;
    			default:begin
    				START_BIT <= START_BIT;
    				r_data_byte[0] <= r_data_byte[0];
    				r_data_byte[1] <= r_data_byte[1];
    				r_data_byte[2] <= r_data_byte[2];
    				r_data_byte[3] <= r_data_byte[3];
    				r_data_byte[4] <= r_data_byte[4];
    				r_data_byte[5] <= r_data_byte[5];
    				r_data_byte[6] <= r_data_byte[6];
    				r_data_byte[7] <= r_data_byte[7];
    				STOP_BIT <= STOP_BIT;
    			end
    		endcase
    	end
    end
    
    //--------<数据状态判定模块>--------	
    always@(posedge Clk or negedge Rst_n)begin
    	if(!Rst_n)
    		data_byte <= 8'd0;
    	else if(bps_cnt == 8'd159)begin
    		data_byte[0] <= r_data_byte[0][2];
    		data_byte[1] <= r_data_byte[1][2];
    		data_byte[2] <= r_data_byte[2][2];
    		data_byte[3] <= r_data_byte[3][2];
    		data_byte[4] <= r_data_byte[4][2];
    		data_byte[5] <= r_data_byte[5][2];
    		data_byte[6] <= r_data_byte[6][2];
    		data_byte[7] <= r_data_byte[7][2];
    	end
    	else
    		;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    上述部分的代码需要紧密结合模块设计思路和verilog一维数组的语法来理解。

    特别是在进行数据状态的判定中一直选择读取一维数组中每位数据的第三位:

    data_byte[0] <= r_data_byte[0][2];
    data_byte[1] <= r_data_byte[1][2];
    data_byte[2] <= r_data_byte[2][2];
    data_byte[3] <= r_data_byte[3][2];
    data_byte[4] <= r_data_byte[4][2];
    data_byte[5] <= r_data_byte[5][2];
    data_byte[6] <= r_data_byte[6][2];
    data_byte[7] <= r_data_byte[7][2];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    编写思想举例如下:

    对一位数据需进行 6 次采样,然后取出现次数较多的数据作为采样结果,也就是说,6 次采样中出现次数多于 3 次的数据才能作为最终的有效数据。对此,可以用接收到数据 r_data_byte[n]结合数值比较器来判断,也可以直接令其等于当前位的最高位数据。
    以下面例子说明:当 r_data_byte[n]分别为二进制的 011B/010B/100B/101B时,这几个数据十进制格式分别为 3d/2d/4d/5d,可以发现大于等 4d 的为 100B/101B。当最高位是 1 即此时的数据累加值大于等于 4d,可以说明数据真实值为 1;当最高位是 0 即此时的数据累加值小于等于 3d,可以说明数据真实值为 0,因此只需判断最高位即可。

    我们将该模块与串口发送模块(串口发送模块讲解)放到一起来进行仿真测试:

    uart_byte_rx_tb.v:

    `timescale 1ns/1ns
    `define clock_period 20
    
    module uart_byte_rx_tb;
    
    	reg Clk;
    	reg Rst_n;
    	reg [2:0]baud_set;
    	
    	reg [7:0]data_byte;
    	reg send_en;
    	
    	wire uart_tx;
    	wire [7:0]data_byte_r;
    	
    	wire uart_state;
    	
    	wire Tx_Done;
    	wire Rx_Done;
    	
    	uart_byte_tx uart_byte_tx0(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.data_byte(data_byte),
    		.send_en(send_en),
    		.baud_set(baud_set),
    		.uart_tx(uart_tx),
    		.Tx_Done(Tx_Done),
    		.uart_state(uart_state)
    	);
    	
    	uart_byte_rx uart_byte_rx0(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.baud_set(baud_set),
    		.data_rx(uart_tx),
    		.data_byte(data_byte_r),
    		.Rx_Done(Rx_Done)
    	);
    
    	initial Clk = 1;
    	always#(`clock_period / 2) Clk = ~Clk;
    
    	initial begin
    		Rst_n = 1'b0;
    		data_byte = 8'd0;
    		send_en = 1'd0;
    		baud_set = 3'd4;
    		#(`clock_period*20 + 1 );
    		Rst_n = 1'b1;
    		#(`clock_period*50);
    		data_byte = 8'haa;
    		send_en = 1'd1;
    		#`clock_period;
    		send_en = 1'd0;
    		
    		@(posedge Tx_Done)
    		
    		#(`clock_period*5000);
    		data_byte = 8'h55;
    		send_en = 1'd1;
    		#`clock_period;
    		send_en = 1'd0;
    		@(posedge Tx_Done)
    		#(`clock_period*5000);
    	
    		$stop;
    	end
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    仿真结果:

    仿真结果

    板级验证:

    关于issp调试工具ip核的创建和使用参考:【FPGA零基础学习之旅#11】数码管动态扫描注意在本次项目中使用的是issp的探针(probe) 功能,而非源;

    编写板级验证代码:

    module uart_byte_rx_top(
    	input Clk,
    	input Rst_n,
    	input data_rx
    );
    
    	reg [7:0]data_byte_r;
    	wire [7:0]data_byte;
    	wire Rx_Done;
    	
    	uart_byte_rx uart_byte_rx0(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.baud_set(3'd0),
    		.data_rx(data_rx),
    		.data_byte(data_byte),
    		.Rx_Done(Rx_Done)
    	);
    
    	issp issp(
    		.probe(data_byte_r),
    		.source()
    	);
    
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			data_byte_r <= 8'd0;
    		else if(Rx_Done)
    			data_byte_r <= data_byte;
    		else
    			data_byte_r <= data_byte_r;
    	end
    	
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    其中,如下部分代码是对接收到的数据进行简单的缓存操作避免出现数据读取出错的情况

    always@(posedge Clk or negedge Rst_n)begin
    	if(!Rst_n)
    		data_byte_r <= 8'd0;
    	else if(Rx_Done)
    		data_byte_r <= data_byte;
    	else
    		data_byte_r <= data_byte_r;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    jic文件配置完成:
    配置1
    在issp调试工具的界面中选择连续读取数据
    连续读取数据

    并将数据格式为hex格式
    hex格式
    验证效果:
    效果

    四、扩展项目练习

    4.1 项目要求

    FPGA通过串口通信,将接收到的数据(hex格式)显示到数码管和issp调试工具上。

    4.2 实现效果

    电脑串口助手发送hex数据,issp接收到数据并将数据显示到数码管上:
    数码管显示

    4.3 实现过程

    先通过RTL视图来理清思路:
    RTL
    FPGA通过串口通信(uart_byte_rx),先将接收到的数据进行简单的缓存操作(Data_r),再将数据依次传递给HEX8模块issp调试模块,其中HEX8模块将数据处理好之后再传给移位寄存器模块(m74HC595_Drive)。

    🔸关于HEX8模块的讲解:【FPGA零基础学习之旅#11】数码管动态扫描
    🔸关于移位寄存器模块的讲解:【FPGA零基础学习之旅#12】三线制数码管驱动(74HC595)串行移位寄存器驱动

    先将数据缓存部分的代码封装为一个模块:

    Data_r.v:

    module Data_r(
    	input 				Clk,
    	input 				Rst_n,
    	input 				Rx_Done,
    	input 		[7:0]	data_byte,
    	output reg 	[7:0]	data_byte_r
    );
    
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			data_byte_r <= 8'd0;
    		else if(Rx_Done)
    			data_byte_r <= data_byte;
    		else
    			data_byte_r <= data_byte_r;
    	end
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    展示顶层模块:

    uart_byte_rx_hex8_top.v:

    module uart_byte_rx_hex8_top(
    	input 			Clk,
    	input 			Rst_n,
    	input 			data_rx,
    	output 			SH_CP,		//shift clock---------------SCK
    	output 			ST_CP,		//latch data clock----------RCK
    	output 			DS			//shift serial data---------Data
    );
    
    	wire 	[7:0]	data_byte_r;
    	wire 	[7:0]	data_byte;
    	wire 			Rx_Done;
    	
    	wire 	[7:0] sel;//数码管位选(选择当前要显示的数码管)
    	wire 	[7:0] seg;//数码管段选(当前要显示的内容)	
    	
    	uart_byte_rx uart_byte_rx(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.baud_set(3'd0),
    		.data_rx(data_rx),
    		.data_byte(data_byte),
    		.Rx_Done(Rx_Done)
    	);
    	
    	Data_r Data_r(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.Rx_Done(Rx_Done),
    		.data_byte(data_byte),
    		.data_byte_r(data_byte_r)
    	);
    	
    	issp issp(
    		.probe(data_byte_r),
    		.source()
    	);
    	
    	//由于串口目前只接收一个字节数据,只占用2个数码管,所以其余数码管显示0
    	HEX8 HEX8(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.En(1'b1),
    		.disp_data({24'h0,data_byte_r}),
    		.sel(sel),
    		.seg(seg)
    	);
    
    	m74HC595_Driver m74HC595_Driver(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.Data({seg,sel}),
    		.S_EN(1'b1),
    		.SH_CP(SH_CP),
    		.ST_CP(ST_CP),
    		.DS(DS)
    	);
    	
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    (顶层模块中其余模块的设计代码直接从上述参考文章中复制过来即可)

    jic文件配置完成:
    配置2
    最终实现效果:
    实现效果

    csdn

    🧸结尾


  • 相关阅读:
    数据库注入提权总结(三)
    Vue2自定义插件的写法-Vue.use()
    【docker】搭建xxl-job
    什么是电缆故障测试仪 核心功能是什么
    Oracle Linux ISO 全系列下载地址
    2023年9月19日
    perl:BigInt 计算 斐波那契数列
    ADDS:卸载/降级域控制器
    天猫复购预测训练赛技术报告
    58、RbbitMQ(结构和概念以及mq界面:15672)
  • 原文地址:https://blog.csdn.net/m0_56262476/article/details/133917115