• 【FPGA零基础学习之旅#12】三线制数码管驱动(74HC595)串行移位寄存器驱动


    🎉欢迎来到FPGA专栏~三线制数码管驱动


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

    CSDN

    遇见未来

    一、效果演示

    🥝ISSP调试演示:
    程序配置完成:
    程序配置

    调试:
    测试结果

    🥝按键控制演示:
    按键控制

    二、电路结构

    三线制的数码管驱动中,使用74HC595芯片来减少FPGA的管脚数量使用。

    关于74HC595芯片的介绍和时序图,参考文章:74HC595介绍74HC595 驱动

    在AC620开发板上的数码管驱动电路:
    电路1
    AC620开发板使用的是74HC595芯片的级联来驱动显示:
    电路2

    三、代码详解

    3.1 总体结构设计

    先上RTL视图:
    RTL
    HEX8模块将segsel信号传给m74HC595_Driver模块,然后将接收到的信号转换为DS、SH_CP和ST_CP信号;ISSP模块用于调试。

    3.2 驱动74HC595芯片

    关于74HC595芯片的驱动,主要参考该时序图进行代码编写:
    时序图

    74HC595是8位串行移位寄存器,带有存储寄存器和三态寄存器,其中移位寄存器和存储寄存器分别采用不同的时钟。其可以把串行的信号转为并行的信号,因此常用做各种数码管以及点阵屏的驱动芯片。

    该芯片的主要IO:

    IO名称功能
    DS / SER串行数据输入端
    STCP / RCK存储寄存器的时钟输入。上升沿时移位寄存器中的数据进入存储寄存器,下降沿时存储寄存器中的数据保持不变。应用时通常将 ST_CP 置为低点平,移位结束后再在 ST_CP 端产生一个正脉冲更新显示数据。
    SHCP / SCK移位寄存器的时钟输入。上升沿时移位寄存器中的数据依次移动一位,即 Q0 中的数据移到 Q1 中,Q1 中的数据移到 Q2 中,依次类推;下降沿时移位寄存器中的数据保持不变。

    由于在AC620开发板中芯片采用3.3V供电,这样在设计74HC595工作频率时,直接使用50M晶振四分频后的时钟作为其工作时钟。

    74HC595的驱动代码,由于模块命名不能以数字开头,所以加了个m:

    m74HC595_Driver.v:

    module m74HC595_Driver(
    		Clk,
    		Rst_n,
    		Data,
    		S_EN,
    		SH_CP,	//SCK
    		ST_CP,	//RCK
    		DS		//Data
    	);
    
    	parameter DATA_WIDTH = 16;
    
    	input 		Clk;
    	input 		Rst_n;
    	input 		[DATA_WIDTH-1 : 0] Data;	//data to send
    	input 		S_EN;						//send en
    	output reg 	SH_CP;						//shift clock
    	output reg 	ST_CP;						//latch data clock
    	output reg 	DS;							//shift serial data
    	
    	parameter CNT_MAX = 4;
    		
    	reg [15:0] divider_cnt;//分频计数器
    	wire sck_pluse;
    	
    	reg [4:0]SHCP_EDGE_CNT;//SH_CP EDGE counter
    	
    	reg [15:0]r_data;
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			r_data <= 16'd0;
    		else if(S_EN)
    			r_data <= Data;
    		else
    			r_data <= r_data;
    	end
    		
    	//clock divide
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			divider_cnt <= 16'd0;
    		else if(divider_cnt == CNT_MAX)
    			divider_cnt <= 16'd0;
    		else
    			divider_cnt <= divider_cnt + 1'b1;
    	end
    	
    	assign sck_pluse = (divider_cnt == CNT_MAX);
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			SHCP_EDGE_CNT <= 5'd0;
    		else if(sck_pluse)begin
    			if(SHCP_EDGE_CNT ==  5'd31)
    				SHCP_EDGE_CNT <= 5'd0;
    			else
    				SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
    		end
    		else
    			SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
    	end	
    		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			SH_CP <= 1'b0;
    			ST_CP <= 1'b0;
    			DS <= 1'b0;	
    		end
    		else begin
    			case(SHCP_EDGE_CNT)
    				5'd0: begin SH_CP <= 1'b0; ST_CP <= 1'b1; DS <= r_data[15]; end
    				5'd1: begin SH_CP <= 1'b1; ST_CP <= 1'b0;end
    				5'd2: begin SH_CP <= 1'b0; DS <= r_data[14];end
    				5'd3: begin SH_CP <= 1'b1; end
    				5'd4: begin SH_CP <= 1'b0; DS <= r_data[13];end
    				5'd5: begin SH_CP <= 1'b1; end
    				5'd6: begin SH_CP <= 1'b0; DS <= r_data[12];end
    				5'd7: begin SH_CP <= 1'b1; end
    				5'd8: begin SH_CP <= 1'b0; DS <= r_data[11];end
    				5'd9: begin SH_CP <= 1'b1; end
    				5'd10:begin SH_CP <= 1'b0; DS <= r_data[10];end
    				5'd11:begin SH_CP <= 1'b1; end
    				5'd12:begin SH_CP <= 1'b0; DS <= r_data[9];end
    				5'd13:begin SH_CP <= 1'b1; end
    				5'd14:begin SH_CP <= 1'b0; DS <= r_data[8];end
    				5'd15:begin SH_CP <= 1'b1; end
    				5'd16:begin SH_CP <= 1'b0; DS <= r_data[7];end
    				5'd17:begin SH_CP <= 1'b1; end
    				5'd18:begin SH_CP <= 1'b0; DS <= r_data[6];end
    				5'd19:begin SH_CP <= 1'b1; end
    				5'd20:begin SH_CP <= 1'b0; DS <= r_data[5];end
    				5'd21:begin SH_CP <= 1'b1; end
    				5'd22:begin SH_CP <= 1'b0; DS <= r_data[4];end
    				5'd23:begin SH_CP <= 1'b1; end
    				5'd24:begin SH_CP <= 1'b0; DS <= r_data[3];end
    				5'd25:begin SH_CP <= 1'b1; end
    				5'd26:begin SH_CP <= 1'b0; DS <= r_data[2];end
    				5'd27:begin SH_CP <= 1'b1; end
    				5'd28:begin SH_CP <= 1'b0; DS <= r_data[1];end
    				5'd29:begin SH_CP <= 1'b1; end
    				5'd30:begin SH_CP <= 1'b0; DS <= r_data[0];end
    				5'd31:begin SH_CP <= 1'b1; end
    				default:begin SH_CP <= 1'b0;ST_CP <= 1'b0;DS <= 1'b0;	end
    			endcase	
    		end
    	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

    RTL视图:

    RTL74

    3.3 HEX8模块

    该模块的设计是在该文章的讲解基础之上进行修改:【FPGA零基础学习之旅#11】数码管动态扫描

    上述参考文章中的模块可以称为HEX6,驱动了6个数码管,在此我们需要驱动8个数码管,故可以将模块命名为HEX8。

    需要注意的是,在设计数码管位选的时候,一定要看清使用板子的电路结构,弄清楚是高电平位选还是低电平位选!

    HEX8.v:

    module HEX8(
    		input 					Clk,		//50M
    		input 					Rst_n,		//复位
    		input 					En,			//数码管显示使能
    		input 		[31:0]		disp_data,	//8 × 4 = 32(8个数码管,数据格式为hex,总共输32位)
    		output reg 	[7:0]		seg, 		//数码管段选
    		output 		[7:0]		sel	 		//数码管位选(数码管选择)
    );
    	reg [7:0]sel_r;
    
    //--------<分频器>--------
    	reg [14:0]divider_cnt;//25000-1
    	
    	reg clk_1K;
    	
    	reg [3:0]data_tmp;//待显示数据缓存
    	
    	//1KHz分频计数器
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			divider_cnt <= 15'd0;
    		else if(!En)
    			divider_cnt <= 15'd0;
    		else if(divider_cnt == 24999)
    			divider_cnt <= 15'd0;
    		else
    			divider_cnt <= divider_cnt + 1'b1;
    	end
    	
    	//1KHz扫描时钟
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			clk_1K <= 1'b0;
    		else if(divider_cnt == 24999)
    			clk_1K <= ~clk_1K;
    		else
    			clk_1K <= clk_1K;
    	end
    	
    //--------<6位循环移位寄存器>--------	
    	always@(posedge clk_1K or negedge Rst_n)begin
    		if(!Rst_n)
    			sel_r <= 8'b0000_0001;
    		else if(sel_r == 8'b1000_0000)
    			sel_r <= 8'b0000_0001;
    		else
    			sel_r <= sel_r << 1;
    	end	
    
    //--------<6选1多路器>--------		
    	always@(*)begin
    		case(sel_r)
    			8'b0000_0001:data_tmp = disp_data[3:0];
    			8'b0000_0010:data_tmp = disp_data[7:4];
    			8'b0000_0100:data_tmp = disp_data[11:8];
    			8'b0000_1000:data_tmp = disp_data[15:12];
    			8'b0001_0000:data_tmp = disp_data[19:16];
    			8'b0010_0000:data_tmp = disp_data[23:20];
    			8'b0100_0000:data_tmp = disp_data[27:24];
    			8'b1000_0000:data_tmp = disp_data[31:28];
    			default:data_tmp = 4'b0000;
    		endcase
    	end
    
    //----------------		
    	always@(*)begin
    		case(data_tmp)
    			4'h0:seg = 8'hc0;
    			4'h1:seg = 8'hf9;
    			4'h2:seg = 8'ha4;
    			4'h3:seg = 8'hb0;
    			4'h4:seg = 8'h99;
    			4'h5:seg = 8'h92;
    			4'h6:seg = 8'h82;
    			4'h7:seg = 8'hf8;
    			4'h8:seg = 8'h80;
    			4'h9:seg = 8'h90;
    			4'ha:seg = 8'h88;
    			4'hb:seg = 8'h83;
    			4'hc:seg = 8'hc6;
    			4'hd:seg = 8'ha1;
    			4'he:seg = 8'h86;
    			4'hf:seg = 8'h8e;
    		endcase
    	end
    		
    //--------<2选1多路器>--------		
    	assign sel = (En)?(sel_r):8'b1111_1111;
    		
    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

    3.4 顶层模块

    在顶层模块中需要调用ISSP这样的一个IP核,操作过程和调试方法参考:【FPGA零基础学习之旅#11】数码管动态扫描

    smg.v:

    module smg(
    	input 			Clk,		//50M
    	input 			Rst_n,
    	//input [31:0] 	disp_data,
    	output 			SH_CP,		//shift clock
    	output 			ST_CP,		//latch data clock
    	output 			DS			//shift serial data
    );
    	
    	wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
    	wire [7:0] seg;//数码管段选(当前要显示的内容)	
    
    	wire  [31:0] disp_data;
    	
    	ISSP UISSP(
    		.probe(),
    		.source(disp_data)
    	);
    		
    	HEX8 UHEX8(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.En(1'b1),
    		.disp_data(disp_data),
    		.sel(sel),
    		.seg(seg)
    	);
    		
    	m74HC595_Driver Um74HC595_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

    四、按键控制改变数据值

    项目要求: 通过控制按键,使得数码管显示不同的数据内容。

    实现效果:
    实现效果
    先看RTL视图来理解整体框架:
    RTL3
    按下按键1,数码管显示12345678;按下按键2,数码管显示89abcdef。

    按键消抖模块的设计参考该文章:【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)

    在此贴出按键消抖的代码:

    KeyFilter.v:

    //
    //模块:按键消抖模块
    //key_state:输出消抖之后按键的状态
    //key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
    //
    module KeyFilter(
    	input Clk,
    	input Rst_n,
    	input key_in,
    	output reg key_flag,
    	output reg key_state
    );
    
    	//按键的四个状态
    	localparam
    		IDLE 		= 4'b0001,
    		FILTER1 	= 4'b0010,
    		DOWN 		= 4'b0100,
    		FILTER2 	= 4'b1000;
    
    	//状态寄存器
    	reg [3:0] curr_st;
    	
    	//边沿检测输出上升沿或下降沿
    	wire pedge;
    	wire nedge;
    	
    	//计数寄存器
    	reg [19:0]cnt;
    	
    	//使能计数寄存器
    	reg en_cnt;
    	
    	//计数满标志信号
    	reg cnt_full;//计数满寄存器
    	
    //------<边沿检测电路的实现>------
    	//边沿检测电路寄存器
    	reg key_tmp0;
    	reg key_tmp1;
    	
    	//边沿检测
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			key_tmp0 <= 1'b0;
    			key_tmp1 <= 1'b0;
    		end
    		else begin
    			key_tmp0 <= key_in;
    			key_tmp1 <= key_tmp0;
    		end	
    	end
    		
    	assign nedge = (!key_tmp0) & (key_tmp1);
    	assign pedge = (key_tmp0)  & (!key_tmp1);
    
    //------<状态机主程序>------	
    	//状态机主程序
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			curr_st <= IDLE;
    			en_cnt <= 1'b0;
    			key_flag <= 1'b0;
    			key_state <= 1'b1;
    		end
    		else begin
    			case(curr_st)
    				IDLE:begin
    					key_flag <= 1'b0;
    					if(nedge)begin
    						curr_st <= FILTER1;
    						en_cnt <= 1'b1;
    					end
    					else
    						curr_st <= IDLE;
    				end
    				
    				FILTER1:begin
    					if(cnt_full)begin
    						key_flag <= 1'b1;
    						key_state <= 1'b0;
    						curr_st <= DOWN;
    						en_cnt <= 1'b0;
    					end	
    					else if(pedge)begin
    						curr_st <= IDLE;
    						en_cnt <= 1'b0;
    					end
    					else
    						curr_st <= FILTER1;
    				end
    				
    				DOWN:begin
    					key_flag <= 1'b0;
    					if(pedge)begin
    						curr_st <= FILTER2;
    						en_cnt <= 1'b1;
    					end
    					else
    						curr_st <= DOWN;
    				end
    				
    				FILTER2:begin
    					if(cnt_full)begin
    						key_flag <= 1'b1;
    						key_state <= 1'b1;
    						curr_st <= IDLE;
    						en_cnt <= 1'b0;
    					end	
    					else if(nedge)begin
    						curr_st <= DOWN;
    						en_cnt <= 1'b0;
    					end
    					else
    						curr_st <= FILTER2;
    				end
    				
    				default:begin
    					curr_st <= IDLE;
    					en_cnt <= 1'b0;
    					key_flag <= 1'b0;
    					key_state <= 1'b1;
    				end
    			endcase
    		end
    	end
    	
    //------<20ms计数器>------		
    	//20ms计数器
    	//Clk 50_000_000Hz
    	//一个时钟周期为20ns
    	//需要计数20_000_000 / 20 = 1_000_000次
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			cnt <= 20'd0;
    		else if(en_cnt)
    			cnt <= cnt + 1'b1;
    		else
    			cnt <= 20'd0;
    	end
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			cnt_full <= 1'b0;
    		else if(cnt == 999_999)
    			cnt_full <= 1'b1;
    		else
    			cnt_full <= 1'b0;
    	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

    简单编写了一个KeyData模块用于不同数据的输入

    KeyData.v:

    module KeyData(
    	input Clk,
    	input Rst_n,
    	input Key_state1,
    	input Key_flag1,
    	input Key_state2,
    	input Key_flag2,
    	output reg [31:0] dis_data
    );
    
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			dis_data <= 32'h00000000;
    		else if(Key_flag1 && !Key_state1)
    			dis_data <= 32'h12345678;
    		else if(Key_flag2 && !Key_state2)
    			dis_data <= 32'h89abcdef;
    		else 
    			dis_data <= dis_data;
    	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

    顶层模块KeyCtrlSmg.v:

    module KeyCtrlSmg(
    	input 	Clk,
    	input 	Rst_n,
    	input 	KeyIn1,
    	input 	KeyIn2,
    	output 	SH_CP,		//shift clock
    	output 	ST_CP,		//latch data clock
    	output 	DS			//shift serial data
    );
    
    	wire key_state1;
    	wire key_flag1;
    	wire key_state2;
    	wire key_flag2;
    	
    	wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
    	wire [7:0] seg;//数码管段选(当前要显示的内容)	
    
    	wire  [31:0] dis_data;
    	
    	KeyFilter KeyFilter1(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.key_in(KeyIn1),
    		.key_flag(key_flag1),
    		.key_state(key_state1)
    	);
    	
    	KeyFilter KeyFilter2(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.key_in(KeyIn2),
    		.key_flag(key_flag2),
    		.key_state(key_state2)
    	);
    
    	KeyData UKeyData(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.Key_state1(key_state1),
    		.Key_flag1(key_flag1),
    		.Key_state2(key_state2),
    		.Key_flag2(key_flag2),
    		.dis_data(dis_data)
    	);
    
    	HEX8 UHEX8(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.En(1'b1),
    		.disp_data(dis_data),
    		.sel(sel),
    		.seg(seg)
    	);
    		
    	m74HC595_Driver Um74HC595_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
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    测试激励文件:

    `timescale 1ns/1ns
    `define clock_period 20
    
    module KeyCtrlSmg_tb;
    
    	reg Clk;
    	reg Rst_n;
    	reg KeyIn1;
    	reg KeyIn2;
    	
    	wire SH_CP;
    	wire ST_CP;
    	wire DS;
    
    	KeyCtrlSmg UKeyCtrlSmg(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.KeyIn1(KeyIn1),
    		.KeyIn2(KeyIn2),
    		.SH_CP(SH_CP),		//shift clock
    		.ST_CP(ST_CP),		//latch data clock
    		.DS(DS)				//shift serial data
    	);
    
    	initial Clk = 1;
    	always#(`clock_period / 2) Clk = ~Clk;
    	
    	initial begin 
    		Rst_n = 0;
    		KeyIn1 = 1;
    		KeyIn2 = 1;
    		#200;
    		Rst_n = 1;
    		#200;
    		KeyIn1 = 0;
    		KeyIn2 = 1;
    		#(`clock_period*10000)
    		KeyIn1 = 1;
    		KeyIn2 = 1;
    		#(`clock_period*10000)
    		KeyIn1 = 1;
    		KeyIn2 = 0;
    		#(`clock_period*10000)
    		$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

    仿真结果:

    仿真结果

    csdn

    🧸结尾


  • 相关阅读:
    软考:中级软件设计师:2022年下半年上午软件设计师考题
    基于javaweb基于ssm框架学生信息管理(选课)系统设计与实现(项目源码)
    Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析
    Dubbo(十) dubbo服务超时时间设置以及优先级
    Java_数组
    java计算机毕业设计ssm+vue个人时间规划系统
    java基于springboot+vue的人才推荐求职招聘系统
    访客预约管理4大难点,帮你逐一破解
    11.16堆的一些性质与操作
    一文看懂云计算和大数据到底是什么!
  • 原文地址:https://blog.csdn.net/m0_56262476/article/details/132655030