前言
独立按键控制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 | 复位按键 |
key | led控制按键 |
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变化方向