独立按键控制LED灯

前言

        独立按键控制LED灯项目旨在通过简单的电路设计和基础编程,实现按键对LED灯的开关控制。这个项目帮助你理解LED灯的工作原理和基本电路连接,同时提高你对电子元件的认识和实践技能。通过搭建电路并进行调试,你将掌握如何独立控制LED灯,从而为进一步学习电子技术打下坚实基础。

正文

一、 独立按键控制LED灯

        1.项目需求

        通过开发板外设独立按键和led,利用独立按键控制led依次循环点亮,Led的变化方式为:1234321,对独立按键操作有一次Led灯点亮往左或者往右移动。

        2.技术介绍

        本项目结合前两期工程进行设计,Led的变化方式为:1234321,共有两个设计:

        项目一

初始状态:led全部熄灭,跳转到第一个led灯点亮状态的条件为:对独立按键进行一次操作。

第一个led灯点亮状态:对独立按键进行第一次操作后点亮第一个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由左到右。

第2个led灯点亮状态:对独立按键进行第2次操作后点亮第2个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由左到右。

第3个led灯点亮状态:对独立按键进行第3次操作后点亮第3个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由左到右

第4个led灯点亮状态:对独立按键进行第4次操作后点亮第4个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由右到左

第3个led灯点亮状态:对独立按键进行第5次操作后点亮第一个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由右到左

第2个led灯点亮状态:对独立按键进行第6次操作后点亮第一个led灯,跳转到下一个状态的条件为:对独立按键进行一次操作,移动方式为:由右到左

状态转移图为:

        项目二

在没有对独立按键进行操作时,led灯是依次循环点亮的,即1234321,对按键进行操作后,led灯点亮的顺序翻转。

        如果不对独立按键进行消抖处理,会导致对独立按键进行一次操作后,点亮的led是随机的。为了解决这个问题,需要对独立按键进行消抖处理,利用其产生的标志信号flag来控制状态的跳转。

        3.顶层架构

        该图为本项目RTL电路图:cnt_freq为计数器分频模块,产生合适的时钟信号。jitter_ctrl_v2为按键消抖模块,产生有效的flag信号控制led。led_ctrl_v1为LED驱动模块,两个项目RTL电路图相同。

        4.端口描述

clk时钟信号
rst_n复位按键
keyled控制按键
led[3:0]四个led灯

二、代码验证

两个项目只有led控制模块不同,其他模块相同:

jitter_ctrl_v2代码:三段式状态机

//三段式
module jitter_ctrl_v2(

	input clk,
	input rst_n,
	input key,
	
	output reg flag,
	output reg key_en
);

reg [3:0] n_state;
reg [3:0] c_state;

parameter s0 = 4'b0001;//空闲状态
parameter s1 = 4'b0010;//下抖动状态
parameter s2 = 4'b0100;//稳定状态
parameter s3 = 4'b1000;//上抖动状态

reg [3:0] cnt;
//FSM1状态传递
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		c_state <= s0;
	else
		c_state <= n_state;
end 

//FSM2描述状态转移
always @(*)
begin
	if(rst_n == 0)
		n_state <= s0;
	else
		case(c_state)
			s0	:	begin
						if(key == 0)
							n_state <= s1;
						else
							n_state <= s0;
					end 
			s1	:	begin
						if(cnt == 9)
							n_state <= s2;
						else
							n_state <= s1;
					end 
			s2	:	begin
						if(key == 1)
							n_state <= s3;
						else
							n_state <= s2;
					end 					
			s3	:	begin
						if(cnt == 9)
							n_state <= s0;
						else
							n_state <= s3;
					end 		
			default	:	n_state <= s0;
		endcase
end 

//FSM3状态输出
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		begin
			cnt <= 4'd0;
			flag <= 1'b0;
			key_en <= 1'b0;
		end
	else
		case(c_state)
			s0	:	begin 
						cnt <= 4'd0;
						flag <= 1'b0;
						key_en <= 1'b0; 
					end 
			s1	:	begin
						if(cnt < 9)
							cnt <= cnt + 4'd1;
						else
							cnt <= 4'd0;
							
						if(cnt == 9)//到预定延时后,产生flag信号
							begin
								flag <= 1'b1;
								key_en <= 1'b1; 
							end 
						else
							begin
								key_en <= 1'b0; 
								flag <= 1'b0;
							end 
					end 
			s2	:	begin
						flag <= 1'b0;
						key_en <= 1'b1;
						cnt <= 4'd0;
					end 
			s3	:	begin//所有信号状态初始化
						flag <= 1'b0;
						key_en <= 1'b0;
						if(cnt < 9)
							cnt <= cnt + 4'd1;
						else
							cnt <= 4'd0;
					end 
			default	:	begin
								cnt <= 4'd0;
								flag <= 1'b0;
								key_en <= 1'b0;
							end 
		endcase 	
end 

endmodule 

cnt_freq代码:

module cnt_freq(

	input clk,
	input rst_n,
	
	output reg clk_1khz
);
//1khz目标时钟
//计数目标信号半个周期0.5ms
reg [14:0] cnt;
parameter MAX = 15'd25_000;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		cnt <= 15'd0;
	else
		if(cnt < MAX - 1)
			cnt <= cnt + 15'd1;
		else
			cnt <= 15'd0;
end 

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		clk_1khz <= 1'b0;
	else
		if(cnt == MAX - 1)
			clk_1khz <= ~clk_1khz;
		else
			clk_1khz <= clk_1khz;	
end 

endmodule 

led_ctrl_v2项目一led控制模块代码:

//循环点亮和按键控制移动方向
module led_ctrl_v2(

	input clk,
	input rst_n,
	input key,
	
	output reg [3:0] led
);

reg [2:0] n_state;
reg [2:0] c_state;
parameter s0 = 3'd0;
parameter s1 = 3'd1;
parameter s2 = 3'd2;
parameter s3 = 3'd3;
parameter s4 = 3'd4;
parameter s5 = 3'd5;

//FSM1状态传递
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		c_state <= s0;
	else
		c_state <= n_state;
end 

//FSM2描述状态转移
always@(*) begin
	if(rst_n == 0)
		n_state <= s0;
	else
		case(c_state)
			s0	:	begin
						if(key == 1)//控制按键按下
							n_state <= s1;//led循环点亮
						else
							n_state <= s0;
					end 
			s1	:	begin
						if(key == 1)//控制按键按下
							n_state <= s2;//led循环点亮
						else
							n_state <= s1;
					end 		
			s2	:	begin
						if(key == 1)//控制按键按下
							n_state <= s3;//led循环点亮
						else
							n_state <= s2;
					end 		
			s3	:	begin
						if(key == 1)//控制按键按下
							n_state <= s4;//led循环点亮
						else
							n_state <= s3;
					end
			s4	:	begin
						if(key == 1)//控制按键按下
							n_state <= s5;//led循环点亮
						else
							n_state <= s4;
					end
			s5	:	begin
						if(key == 1)//控制按键按下
							n_state <= s0;//led循环点亮
						else
							n_state <= s5;
					end					
			default	:	n_state <= s0;
		endcase
end  

//FSM3描述状态输出
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
			led <= 4'hf;
	else
		case(c_state)
			s0	: led <= 4'b0111; 
			s1	: led <= 4'b1011;
			s2	: led <= 4'b1101;
			s3	: led <= 4'b1110;
			s4	: led <= 4'b1101;
			s5	: led <= 4'b1011;
			default	:	led <= 4'hf;
		endcase
end 


endmodule 

led_ctrl_v1项目二led控制模块代码:

//循环点亮和按键控制移动方向
module led_ctrl_v1(

	input clk,
	input rst_n,
	input key,
	
	output reg [3:0] led
);

reg [2:0] n_state;
reg [2:0] c_state;
parameter s0 = 3'd0;
parameter s1 = 3'd1;
parameter s2 = 3'd2;
parameter s3 = 3'd3;
parameter s4 = 3'd4;
parameter s5 = 3'd5;

reg [9:0] cnt;
parameter MAX = 10'd1000;

//FSM1//状态传递
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		c_state <= s0;
	else
		c_state <= n_state;
end 

//FSM2//描述状态转移
always@(*) begin
	if(rst_n == 0)
		n_state <= s0;
	else
		case(c_state)
			s0	:	begin
						if(key == 1)
							n_state <= s3;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s1;
							else
								n_state <= s0;
					end 
			s1	:	begin
						if(key == 1)
							n_state <= s5;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s2;
							else
								n_state <= s1;
					end 		
			s2	:	begin
						if(key == 1)
							n_state <= s4;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s3;
							else
								n_state <= s2;
					end 		
			s3	:	begin
						if(key == 1)
							n_state <= s0;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s4;
							else
								n_state <= s3;
					end
			s4	:	begin
						if(key == 1)
							n_state <= s2;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s5;
							else
								n_state <= s4;
					end
			s5	:	begin
						if(key == 1)
							n_state <= s1;//控制按键改变变换方向
						else
							if(cnt == MAX - 1)//延时计数器记满自动循环
								n_state <= s0;
							else
								n_state <= s5;
					end 				
			default	:	n_state <= s0;
		endcase
end  

//FSM3//描述状态输出
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
			led <= 4'hf;
	else
		case(c_state)
			s0	: led <= 4'b1110; 
			s1	: led <= 4'b1101;
			s2	: led <= 4'b1011;
			s3	: led <= 4'b0111;
			s4	: led <= 4'b1011; 
			s5	: led <= 4'b1101; 
			default	:	led <= 4'hf;
		endcase
end 
//延时计数器
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		cnt <= 10'd0;
	else
		if(cnt < MAX - 1)
			cnt <= cnt + 10'd1;
		else
			cnt <= 10'd0;	
end 

endmodule 

顶层代码

module key_led(

	input clk,
	input rst_n,
	input key,
	
	output [3:0] led
);

wire clk_1khz;
wire flag;

cnt_freq #(.MAX(5)) cnt_freq_inst(//直接改变计数器的计数值#(.MAX(5))方便仿真

			.clk(clk),
			.rst_n(rst_n),
			
			.clk_1khz(clk_1khz)
);

jitter_ctrl_v2 jitter_ctrl_v2_inst(

			.clk(clk_1khz),
			.rst_n(rst_n),
			.key(key),
			
			.flag(flag),
			.key_en()
);

led_ctrl_v2 /*#(.MAX(5))*/ led_ctrl_inst(

			.clk(clk_1khz),
			.rst_n(rst_n),
			.key(flag),
			
			.led(led)
);

endmodule 

仿真代码:

`timescale 1ns/1ps
module key_led_tb;

	reg clk;
	reg rst_n;
	reg key;
	
	wire [3:0] led;

key_led key_led_inst(

			.clk(clk),
			.rst_n(rst_n),
			.key(key),
			
			.led(led)
);

initial clk = 1;
always #10 clk = ~clk;

initial begin
	rst_n = 0;
	key = 1;
	#200
	rst_n = 1;
	#5000
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下一次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下两次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下三次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下四次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下五次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下六次
	
	key = 1;
	#200
	rst_n = 1;
	#200
	key = 0;
	#240
	key = 1;
	#230
	key = 0;
	#213
	key = 1;
	#123
	key = 0;
	#3000
	key = 1;
	#213
	key = 0;
	#123
	key = 1;
	#234
	key = 0;
	#231
	key = 1;
	#3000//按下七次
	$stop;
end 

endmodule 

三、仿真验证

        项目一

        调出中间信号,进行系统分析

        模拟按键按下7次,产生7次flag信号,因为状态机这里使用Mealy 状态机:尽管输出是根据当前状态和输入信号计算的,实际的状态更新还是需要一个完整的时钟周期。因此,输入变化与输出响应之间通常会有一个时钟周期的延迟。led状态会在接收到flag后延时一个时钟周期后改变。

        项目二

        调出中间信号,进行系统分析

        观察输出信号,flag到来时了的处于由1110->1101变化,根据led控制程序,其在不接收flag信号条件下下一种状态为1011,在接收到flag后,1101->1110,可知,控制按键改变了led变化方向

参考资料

状态机

米利型有限状态机

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值