本文完成的实验为按键控制LED灯,通过对连接按键输入的IO电平进行检测,根据电平的状态来改变LED的亮灭。
实验要求和硬件概览
在【每周一练】小眼睛FPGA1K开发板硬件平台中使用的LED和按键硬件和管脚分布进行了介绍。把硬件的相关信息集中到一篇帖子中,便于读者查看。
实验的目的是:
- 设计8 种彩灯效果,可通过按键切换。
- 选择一个按键作为控制输入,按下一次换一种显示效果,在8 种效果中循环。
Verilog逻辑设计
FPGA编程可以分为两个主要部分,一部分是Verilog语言描述的硬件工作逻辑,用于描述模块的工作流程;另一部分是引脚约束文件,建立模块与硬件之间的映射关系。
本次实验的原理图如下:
从模块设计的角度,需要设计两个模块管理输入信号处理和控制输出信号。
首先是对按键输入信号进行处理。硬件上按键的输入的电路上添加了电容用于对输入的信号进行滤波,滤除了部分信号抖动,逻辑上需要对输入信号进行如下处理:
- 对按键信号做消抖处理,消抖的延时在20ms;
- 对检测到的按键信号进行计数,进而调整LED的状态。
基于上述要求设计按键消抖模块和按键计数模块
按键消抖模块的源码为:
`timescale 1ns / 1ps `define UD #1
module btn_deb#(
parameter BTN_WIDTH = 4'd8,
parameter MS_20 = 20'd800_000
)
(
input clk,//40MHz
input [BTN_WIDTH-1:0] btn_in,
output reg [BTN_WIDTH-1:0] btn_deb
);
//==================================================================================
reg [19:0] time_cnt= 20'd0;
always@(posedge clk)
begin
if(time_cnt == MS_20 - 1'b1)
time_cnt <= `UD 20'd0; else time_cnt <= `UD time_cnt + 1'd1;
end
always @(posedge clk)
begin
if(time_cnt == MS_20 - 1'b1)
btn_deb <= `UD btn_in;
end
endmodule
对消抖后的按键信号进行计数,设计按键计数控制模块为:
`timescale 1ns / 1ps
`define UD #1
module key_ctl(
input clk,
input key,
output [3:0] ctrl
);
wire btn_deb;
// 按键消抖
btn_deb#(
.BTN_WIDTH ( 4'd1 ), //parameter BTN_WIDTH = 4'd8
.MS_20 ( 20'd800_000 )
) U_btn_deb
(
.clk ( clk ),//input clk,
.btn_in ( key ),//input [BTN_WIDTH-1:0] btn_in,
.btn_deb ( btn_deb ) //output reg [BTN_WIDTH-1:0] btn_deb
);
reg btn_deb_1d;
always @(posedge clk)
begin
btn_deb_1d <= `UD btn_deb;
end
reg [2:0] key_push_cnt=3'd0;
always @(posedge clk)
begin
if(~btn_deb & btn_deb_1d)
begin
if(key_push_cnt == 3'd7)
key_push_cnt <= `UD 2'd0;
else
key_push_cnt <= `UD key_push_cnt + 3'd1;
end
end
assign ctrl = key_push_cnt;
endmodule
完成对按键输入信号的处理后,可以以按键模块的计数值调整rgb灯的颜色。设计状态机来实现不同按键计数值对应的led灯模式,设计模块如下:
`timescale 1ns / 1ps
`define UD #1
module led_rgb(
input clk,//40MHz
input [2:0] ctrl,
output reg [11:0] led_rgb
);
// led_rgb status change
always @(posedge clk)
begin
case(ctrl)
3'd0 : //R
led_rgb<=`UD 12'b0000_1111_1111;
3'd1 : //G
led_rgb<=`UD 12'b1111_0000_1111;
3'd2 : //B
led_rgb<=`UD 12'b1111_1111_0000;
3'd3 : //RG
led_rgb<=`UD 12'b0000_0000_1111;
3'd4 : //RB
led_rgb<=`UD 12'b0000_1111_0000;
3'd5 : //GB
led_rgb<=`UD 12'b1111_0000_0000;
3'd6 : //RGB
led_rgb<=`UD 12'b0000_0000_0000;
default:led_rgb<=`UD 12'b1111_1111_1111;
endcase
end
endmodule
在顶层模块中实例化上述模块
`timescale 1ns / 1ps
`define UD #1
module key_led_rgb_top(
input clk,
input key,
output [11:0] led_rgb
);
wire [2:0] ctrl;
key_ctl key_ctl(
.clk ( clk ),
.key ( key ),
.ctrl ( ctrl )
);
led_rgb u_led_rgb(
.clk ( clk ),
.ctrl ( ctrl ),
.led_rgb ( led_rgb )
);
endmodule
编译成功后,根据硬件原理图添加按键和RGB灯的引脚约束。
通过是适配器烧写相应的位流文件。实验的效果如本文开头的视频所示。