按键延迟时间测试的Verilog实现(FPGA实验)

本次实验目的在于熟悉状态机的控制,需要实现的功能如下:
按下按键并保持然后松开
当保持时间小于1s时,一个led灯闪烁2次。
当保持时间小于2s大于1s时,一个led灯闪烁4次。
当保持时间大于2s时,四个led灯闪烁4次。
本次实验采用了状态机的思想控制,主要的细节包括:1.输入信号的缓冲;2输入信号的上升沿和下降沿的检测;3.不同延迟时间的跳转;4.按下计时和led闪烁计时器的各自设计;5,led闪烁的控制。
RTL代码如下:

module keykeeper(
	clk,
	rst,
	key_in,
	led,
	state_out,
	cnt,
	cnt_led,
	led_finish,
	time_flag_1,
	time_flag_2,
	key_pos,
	key_neg
	);
	
	input clk;
	input rst;
	input wire key_in;//输入信号
	output reg [3:0]led;//led信号
	output wire [1:0]state_out;//状态跳转输出
	output reg [15:0]cnt; //按下计时
	output reg [15:0]cnt_led;//led闪烁计时
	output reg led_finish;//led完成闪烁返回信号
	reg en_cnt;//按下计时使能信号
	
	reg key_temp_1;//信号缓冲同步
	reg key_temp_2;
	reg key_temp_3;//边沿寄存
	reg key_temp_4;
	
	reg en_cnt_led;
	
	output wire key_pos;//检测边沿信号
	output wire key_neg;
	
	output reg time_flag_1;
	output reg time_flag_2;
	
	//设置状态跳转
	reg [1:0]state;
	
	assign state_out = state;
	
	localparam
		s0 = 2'b00,
		s1 = 2'b01,
		s2 = 2'b10,
		s3 = 2'b11;
	
	//两级缓冲器对输入信号进行同步
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			key_temp_1 <= 1'b1;
			key_temp_2 <= 1'b1;
		end
		else	
		begin
			key_temp_1 <= key_in;
			key_temp_2 <= key_temp_1;
		end		
	
	//检测key_in的下降沿和上升沿
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			key_temp_3 <= 1'b1;
			key_temp_4 <= 1'b1;
		end
		else	
		begin
			key_temp_3 <= key_temp_2;
			key_temp_4 <= key_temp_3;
		end
	assign key_pos = (key_temp_3)&&(!key_temp_4);
	assign key_neg = (!key_temp_3)&&(key_temp_4);
	
	//设置计数使能信号
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			en_cnt = 1'b0;
		end
		else if(key_neg&&(!key_pos))
		begin
			en_cnt = 1'b1;
		end
		else if(key_pos&&(!key_neg))
		begin
			en_cnt <= 1'b0;
		end
		else
		begin
			en_cnt <= en_cnt;
		end
		
	
	//检测en_cnt,并决定是否开始计时
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			cnt <= 27'b0;
		end
		else if(en_cnt)
		begin
			cnt <= cnt + 1;
		end
		else
		begin
			cnt <= 0;
		end
		
	//设置time_flag
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			time_flag_1 <= 1'b0;
			time_flag_2 <= 1'b0;
		end
		else if(cnt >= 12'b0100_0000_0000)
		begin
			time_flag_2 <= 1'b1;
			time_flag_1 <= 1'b1;
		end
		else if((cnt >= 12'b0010_0000_0000)&&(cnt < 12'b0100_0000_0000))
		begin
			time_flag_1 <= 1'b1;
			time_flag_2 <= 1'b0;
		end
		else
		begin
			time_flag_1 <= 1'b0;
			time_flag_2 <= 1'b0;
		end
	//设置控制led的电路

	parameter CNT_LED_CYCLE = 12'b0001_0000_0000;
	
	//设置led计数控制逻辑
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			en_cnt_led = 1'b0;
		end
		else if(key_pos&&(!led_finish))
		begin
			en_cnt_led = 1'b1;
		end
		else if(led_finish)
		begin
			en_cnt_led = 1'b0;
		end
		else
		begin
			en_cnt_led <= en_cnt_led;
		end
			
	always@(posedge clk or negedge rst)
		if(!rst)
			cnt_led <= 0;
		else if(en_cnt_led)
			cnt_led <= cnt_led + 1;
		else
			cnt_led <= 0;
			
	//设置led亮灭的逻辑

	//s1
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			led <= 4'b1111;
			led_finish <= 1'b0;
		end
		else if(state == s1)
		begin
		led = 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end
		endcase
		end
		
		else if(state == s2)
		begin
		led <= 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*6: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*7:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*8:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*9:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*10:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end
		endcase
		end
		else if(state == s3)
		begin
		led <= 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*6: 
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*7:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*8:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*9:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*10:
		begin
			led <= ~led;
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end		
		endcase
		end
		else
		begin
			led <= 4'b1111;
			led_finish <= 1'b0;
		end	

	//检测time_flag并控制状态跳转以及led
	
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			state <= s0;
		end
		else
		begin
			case(state)
			s0:
				begin
				if(key_neg)
				state <= s1;
				else
				state <= s0;
				end
			s1:
				begin
				if((time_flag_1)&&(!time_flag_2))
				state <= s2;
				else
				state <= s1;
				end
			s2:
				begin
				if((time_flag_1)&&(time_flag_2))
				state <= s3;
				else
				state <= s2;
				end
			s3:
				begin
				if(led_finish)
				state <= s0;
				else
				state <= s3;
				end
			endcase
		end
		
		
endmodule 

测试代码如下:

`timescale 1ns/1ns
`define clock_period 20
module keykeeper_tb;
	reg key_in;
	reg clk;
	reg rst;
	wire [3:0] led;
	wire [1:0]state_out;
	wire [15:0] cnt; //101_1111_0101_1110_0001_0000_0000
	wire [15:0]cnt_led;
	wire led_finish;
	wire time_flag_1;
	wire time_flag_2;
	wire key_pos;
	wire key_neg;
	
	keykeeper keykeeper0(
								.clk(clk),
								.rst(rst),
								.key_in(key_in),
								.led(led),
								.state_out(state_out),
								.cnt(cnt),
								.cnt_led(cnt_led),
								.led_finish(led_finish),
								.time_flag_1(time_flag_1),
								.time_flag_2(time_flag_2),
								.key_pos(key_pos),
								.key_neg(key_neg)
								);
	initial
	clk = 1'b0;
	always#(`clock_period/2)
		clk = ~clk;
	
	initial
	begin
		key_in = 1'b1;
		rst = 1'b1;
		#20;
		rst = 1'b0;
		#40;
		rst = 1'b1;
		#20;
		key_in = 1'b1;
		#100;
		key_in = 1'b0;
		#(`clock_period*200);
		key_in = 1'b1;
		#(`clock_period*5_000);
		key_in = 1'b0;
		#(`clock_period*700)
		key_in = 1'b1;
		#(`clock_period*5_000);
		key_in = 1'b0;
		#(`clock_period*2000)
		key_in = 1'b1;
		#(`clock_period*5_000);
		$stop;		
	end
endmodule 

结果如下图
按键延迟时间测试的Verilog实现(FPGA实验)_第1张图片

你可能感兴趣的:(Verilog)