LED灯闪烁拓展实验——单按键控制LED灯不同闪烁方式

一、说明

实现平台:Vivado2018.3

二、内容

1. 使用按键KEY0和KEY_1分别控制LED0和LED1两个LED灯的闪烁方式;

2. 当没有按键按下时,两个LED灯均不亮;

3. 当按键KEY0按下后,LED0灯闪烁,当按键KEY0再次按下后则LED1灯闪烁,如此循环往复;

4. 当按键KEY1按下后,LED0和LED1交替闪烁,当按键KEY1再次按下后则LED0和LED1同时闪烁,如此循环往复;

5. 在内容3中若按键KEY1按下,则执行内容4操作;在内容4中若按键KEY0按下,则执行内容3操作。

三、步骤

(1)设计要求

  •    时钟频率为50MHZ;
  •    按键KEY0和KEY1;
  •    LED灯LED0和LED1。

(2)设计思路

本实验设计可分为四个模块:

  •    计数器模块:用于计数LED灯闪烁间隔;
  •    LED闪烁状态控制模块:用于改变LED灯状态;
  •   按键边缘检测模块:用于检测按键按下的状态;
  •   按键控制闪烁实现模块:使用两个状态机,分别实现KEY0和KEY1的LED灯闪烁控制。

(3)具体实现

0.相关信号声明:

module key_led(
	input  				sys_clk,
	input  				sys_rst_n,
	
	input      [1:0]	key,
	output reg [1:0] 	led
    );
	
reg  [24:0] cnt; 
reg  		led_ctrl;

reg  [1:0]	key_edg0;				//边缘检测
reg  [1:0]	key_edg1;

reg  [1:0]	state_key0;				//按键状态
reg  [1:0]  state_key1;

wire [1:0]	key_en;					//控制小灯闪烁

1. 计数器模块实现:

其中cnt计满为25_000_000,时钟频率为50MHZ,25_000_000/50_000_000 = 0.5s=500ms

//计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 25'd0;
	else if(cnt < 25'd25_000_000)	//计数500ms
//	else if(cnt < 25'd25)			//仿真用
		cnt <= cnt + 1'b1;
	else	
		cnt <= 0;
end

2. LED闪烁状态控制模块:

//每隔500ms就更改LED的闪烁状态
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		led_ctrl <= 1'b0;
	else if(cnt == 25'd25_000_000)
//	else if(cnt == 25'd25)			//仿真用
		led_ctrl <= ~led_ctrl;
end

3.按键边缘检测模块:

其中使用该模块的原因主要是实现按键仅按一下即实现相关操作的功能,而无需一直按住按键才能执行相关操作。代码中检测上升沿或下降沿均可。

assign key_en[0] = (~key_edg0[0]) & key_edg1[0]; //按键0下降沿检测
assign key_en[1] = (~key_edg0[1]) & key_edg1[1]; //按键1下降沿检测

//PL_KEY边缘检测
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		key_edg0 <= 0;
		key_edg1 <= 0;
	end
	else begin
		key_edg0 <= key;
		key_edg1 <= key_edg0;
	end
end

4.按键控制闪烁实现模块:

此处使用两个状态机,状态机1控制KEY0实现对应的LED灯闪烁;状态机2控制KEY1实现对应的LED灯闪烁。

//两个状态机,根据按键KEY0来控制LED的闪烁
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		state_key0 <= 2'd0;
		state_key1 <= 2'd0;
		led <= 0;
	end
	else begin
		case(state_key0)								//状态机1
			2'd0 : begin
				led <= led;								//默认保持原状态
				state_key0 <= (key_en[0]) ? 2'd1:2'd0;
			end
			2'd1 : begin
				led[0] <= (led_ctrl) ? 1'b1:1'b0; 		//LED0闪烁
				led[1] <= 0;							//LED1不亮	
				if (key_en[0])
					state_key0 <= 2'd2;					//切换到下一状态
				else if(key_en[1])  					//若按键1按下,则返回状态机2
					state_key1 <= 2'd1;
				else
					state_key0 <= 2'd1;
			end
			2'd2 : begin
				led[1] <= (led_ctrl) ? 1'b1:1'b0; 		//LED1闪烁
				led[0] <= 0;							//LED0不亮
				if (key_en[0])
					state_key0 <= 2'd1;					//返回上一状态
				else if(key_en[1])  					//若按键1按下,则返回状态机2
					state_key1 <= 2'd1;
				else
					state_key0 <= 2'd2;
			end
			default:state_key0 <= 2'd0;
		endcase
		case(state_key1)								//状态机2
			2'd0 : begin
				state_key1 <= (key_en[1]) ? 2'd1:2'd0;
			end
			2'd1 : begin
				led <= (led_ctrl) ? 2'b01:2'b10; 		//LED0和1交替闪烁
				state_key1 <= (key_en[1]) ? 2'd2:2'd1;	//切换到下一状态
				if (key_en[1])
					state_key1 <= 2'd2;					//切换到下一状态
				else if(key_en[0])begin  				//若按键0按下,则返回状态机1
					state_key0 <= 2'd1;
					state_key1 <= 2'd0;
				end
				else
					state_key1 <= 2'd1;	
			end
			2'd2 : begin
				led <= (led_ctrl) ? 2'b11:2'b00; 		//LED0和1同时闪烁
				if (key_en[1])
					state_key1 <= 2'd1;					//切换到上一状态
				else if(key_en[0])begin  				//若按键0按下,则返回状态机1
					state_key0 <= 2'd1;
					state_key1 <= 2'd0;
				end
				else
					state_key1 <= 2'd2;
			end
			default:state_key1 <= 2'd0;	
		endcase
	end
end	

5.testbenc仿真测试程序

此处我们模拟按键操作,分别按一下KEY0,再按一下KEY0,查看是否实现了LED0和LED1的切换闪烁操作;之后按KEY1,再按一下KEY1,查看时候实现了两个LED灯的交替以及同时闪烁;最后再仿真按下两次KEY0操作,查看是否实现了按键切换的操作。

其中为了防止仿真时间过长,主程序文件的cnt计数可以改的小一些,方便仿真。

`timescale 1ns / 1ns
module tb_key_led();

//输入
reg           sys_clk;
reg           sys_rst_n;
reg   [1:0]	  key;

//输出
wire  [1:0]   led;

//信号初始化
initial begin
    sys_clk = 1'b0;
    sys_rst_n = 1'b0;
	key = 2'b11;
    #200
    sys_rst_n = 1'b1;
	#200
	key = 2'b10;
	#100
	key = 2'b11;
	#2000
	key = 2'b10;
	#100
	key = 2'b11;
	#2000
	key = 2'b01;
	#100
	key = 2'b11;
	#2000
	key = 2'b01;
	#100
	key = 2'b11;
	#2000
	key = 2'b10;
	#100
	key = 2'b11;
	#2000
	key = 2'b10;
	#100
	key = 2'b11;
	#2000
	$finish;
end

//生成时钟
always #10 sys_clk = ~sys_clk;

//例化待测设计
key_led  u_key_led(
    .sys_clk         (sys_clk),
    .sys_rst_n       (sys_rst_n),
    .key             (key),
	.led			 (led)
    );

endmodule

四、仿真结果

 从仿真图可以看出,左边白框部分实现了LED0(01,00,01,00)和LED1(00,10,00,10)的闪烁功能;中间框部分则实现了LED等交替闪烁和同时闪烁的功能;最后右边蓝框部分,其实现功能和白框部分一样,实现了按键的切换实现LED灯闪烁功能,表明实验成功。

五、完整程序代码

//`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/20 08:28:10
// Design Name: 
// Module Name: key_led
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description:使用PL_KEY0和PL_KEY_1来控制底板上的PL_LED0和PL_LED1两个LED的闪烁方式。 
// 没有按键按下时,两个LED不亮,若按键0按下,则LED0闪烁,若再次按下则LED1闪烁,再按
// 下则LED0闪烁,如此交替;若1按下,则两个LED交替闪烁,再次按下则两个LED同时闪烁,如此反复。
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module key_led(
	input  				sys_clk,
	input  				sys_rst_n,
	
	input      [1:0]	key,
	output reg [1:0] 	led
    );
	
reg  [24:0] cnt; 
reg  		led_ctrl;

reg  [1:0]	key_edg0;				//边缘检测
reg  [1:0]	key_edg1;

reg  [1:0]	state_key0;				//按键状态
reg  [1:0]  state_key1;

wire [1:0]	key_en;					//控制小灯闪烁

//计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 25'd0;
	else if(cnt < 25'd25_000_000)	//计数500ms
//	else if(cnt < 25'd25)			//仿真用
		cnt <= cnt + 1'b1;
	else	
		cnt <= 0;
end

//每隔500ms就更改LED的闪烁状态
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		led_ctrl <= 1'b0;
	else if(cnt == 25'd25_000_000)
//	else if(cnt == 25'd25)			//仿真用
		led_ctrl <= ~led_ctrl;
end

assign key_en[0] = (~key_edg0[0]) & key_edg1[0]; //按键0下降沿检测
assign key_en[1] = (~key_edg0[1]) & key_edg1[1]; //按键1下降沿检测

//PL_KEY边缘检测
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		key_edg0 <= 0;
		key_edg1 <= 0;
	end
	else begin
		key_edg0 <= key;
		key_edg1 <= key_edg0;
	end
end

//两个状态机,根据按键KEY0来控制LED的闪烁
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		state_key0 <= 2'd0;
		state_key1 <= 2'd0;
		led <= 0;
	end
	else begin
		case(state_key0)								//状态机1
			2'd0 : begin
				led <= led;								//默认保持原状态
				state_key0 <= (key_en[0]) ? 2'd1:2'd0;
			end
			2'd1 : begin
				led[0] <= (led_ctrl) ? 1'b1:1'b0; 		//LED0闪烁
				led[1] <= 0;							//LED1不亮	
				if (key_en[0])
					state_key0 <= 2'd2;					//切换到下一状态
				else if(key_en[1])  					//若按键1按下,则返回状态机2
					state_key1 <= 2'd1;
				else
					state_key0 <= 2'd1;
			end
			2'd2 : begin
				led[1] <= (led_ctrl) ? 1'b1:1'b0; 		//LED1闪烁
				led[0] <= 0;							//LED0不亮
				if (key_en[0])
					state_key0 <= 2'd1;					//返回上一状态
				else if(key_en[1])  					//若按键1按下,则返回状态机2
					state_key1 <= 2'd1;
				else
					state_key0 <= 2'd2;
			end
			default:state_key0 <= 2'd0;
		endcase
		case(state_key1)								//状态机2
			2'd0 : begin
				state_key1 <= (key_en[1]) ? 2'd1:2'd0;
			end
			2'd1 : begin
				led <= (led_ctrl) ? 2'b01:2'b10; 		//LED0和1交替闪烁
				state_key1 <= (key_en[1]) ? 2'd2:2'd1;	//切换到下一状态
				if (key_en[1])
					state_key1 <= 2'd2;					//切换到下一状态
				else if(key_en[0])begin  				//若按键0按下,则返回状态机1
					state_key0 <= 2'd1;
					state_key1 <= 2'd0;
				end
				else
					state_key1 <= 2'd1;	
			end
			2'd2 : begin
				led <= (led_ctrl) ? 2'b11:2'b00; 		//LED0和1同时闪烁
				if (key_en[1])
					state_key1 <= 2'd1;					//切换到上一状态
				else if(key_en[0])begin  				//若按键0按下,则返回状态机1
					state_key0 <= 2'd1;
					state_key1 <= 2'd0;
				end
				else
					state_key1 <= 2'd2;
			end
			default:state_key1 <= 2'd0;	
		endcase
	end
end			

endmodule

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值