【FPGA实战篇一】按键消抖及流水点灯
一、FPGA简识
1、认识FPGA
FPGA是一种数字集成电路芯片,英文全称为Field Programmable Gate Array,中文名称为“现场可编程逻辑门阵列”。FPGA是数字电路的物理实现方式之一[1]。与数字电路的另一种重要实现方式ASIC(Application Specific Integrated Circuit,专用集成电路)芯片相比,FPGA的一项重要特点是其可编程特性,即用户可通过程序指定FPGA实现某一特定数字电路。
FPGA开发编程语言
设计FPGA的常用编程语言有Verilog HDL和VHDL,其中Verilog HDL为国内常用。此外,System Verilog,Xilinx HLS,Chisel等也可以作为FPGA的编程语言,但它们并未被广泛使用。与C/C++,Jave,Python等计算机编程语言不同的是,FPGA编程语言属于硬件编程语言,因此在使用FPGA编程语言时不能照搬软件设计思路。
FPGA开发设计流程
FPGA的典型设计流程,开发人员首先要根据设计说明(spec)制定电路方案,如果设计规模啊较大,开发人员还需要对整体电路方案进行划分。之后,根据电路方案,开发人员使用Verilog HDL或其他语言对电路进行描述和仿真。
2、按键消抖原理
按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。
二、创建工程
1、quartus创建
创建一个new project
①打开quartus主界面,点击new project wizard
②点击【File】→【New project Wizard】
进入工程创建界面,按照图示操作
点击next,直至跳至如下界面
按照图中提示进行相关选择
芯片:EP4CE6F17C8
点击next,直至出现如下界面,点击finish
创建工程完成,首界面如图
添加编写程序的文件(Verilog HDL
)
【File】→【New】→【Verilog HDL】
至此,用quartus的方式创建工程就完成了,下面我们介绍另外一种工程创建方式。
2、文件夹文本方式创建
新建一个文件夹,填写项目名称(鼠标右击可以选择新建文件夹以及文本文档)
在此文件夹下创建三个文件夹
在rtl文件下新建文本文档(你所需要编写的项目模块以及其名称)
注意:要修改文本扩展名,按图示勾选
打开quartus,点击New project Wizard,在路径选择上选择你创建的项目下prj文件
此后步骤同上,创建后界面如图
导入我们刚刚创建的文本文档
进入下示界面,选择创建的文本文档(选择当前需要编程的文档)
点击ok,可以看到如示界面
至此,有关quartus的工程创建部分就结束了。(我这里的文件都是为了演示步骤用,名字随便取的,下面的讲解是严格按照工程实践进行的)
三、Verilog 代码编写
顶层模块key_led
module key_led(
input clk , //系统时钟
input rst_n , //复位 低电平有效
input wire key_in , //顶层模块定义wire型
output wire [3:0] led
);
wire press;
//模块例化
key_debounce u_key_debounce(
.clk (clk ),
.rst_n (rst_n ),
.key (key_in ),
.press (press ) //按键按下标志
);
key_driver u_key_driver(
.clk (clk ),
.rst_n (rst_n ),
.en (press ),
.led_o (led )
);
endmodule
驱动模块key_driver
module key_driver(
input wire clk ,
input wire rst_n ,
input wire en ,
output reg [3:0] led_o
);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led_o <= 0;
end
else if(en) begin
led_o <= {
~led_o[0], led_o[3:1]};
end
end
endmodule
key_debounce
module key_debounce(
input wire clk , //50MHz 20ns
input wire rst_n ,
input wire key ,
output reg press //按键按下标志
);
//参数
parameter DELAY_TIME = 1000_000; //延时20ms
//信号定义
reg key_r0 ; //同步 当前时钟周期输入状态
reg key_r1 ; //打拍 前一个时钟周期输入的状态
wire key_nedge ; //下降沿
reg [19:0] delay_cnt ; //计数20ms,需要20ms/20ns = 1000_000
reg delay_flag ;
//同步
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
key_r0 <= 1'b1;
key_r1 <= 1'b1;
end
else begin
key_r0 <= key;
key_r1 <= key_r0;
end
end
assign key_nedge = ~key_r0 & key_r1; //检测下降沿
//assign key_pedge = key_r0 & ~key_r1; //检测上升沿
//delay_cnt
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
delay_cnt <= 0;
end
else if(delay_flag) begin
if(delay_cnt == DELAY_TIME - 1) begin
delay_cnt <= 0;
end
else begin
delay_cnt <= delay_cnt + 1;
end
end
end
//delay_flag
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
delay_flag <= 1'b0;
end
else if(key_nedge)begin
delay_flag <= 1'b1;
end
else if(delay_cnt == DELAY_TIME - 1) begin
delay_flag <= 1'b0;
end
end
//press
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
press <= 1'b0;
end
else if(delay_cnt == DELAY_TIME - 1)begin
press <= ~key_r0;
end
else begin
press <= 1'b0;
end
end
endmodule
key_led_tb
// 单位/精度
`timescale 1ns/1ns
module key_led_tb();
reg tb_clk ;
reg tb_rst_n ;
reg tb_key ;
wire [3:0] tb_led ;
reg [5:0] i ;
key_led u_key_led(
.clk (tb_clk), //系统时钟
.rst_n (tb_rst_n), //复位 低电平有效
.key_in (tb_key), //顶层模块定义wire型
.led (tb_led)
);
defparam u_key_led.u_key_debounce.DELAY_TIME = 20;
parameter CLOCK_PERIOD = 20;
initial tb_clk = 1'b0;
always #(CLOCK_PERIOD/2) tb_clk = ~tb_clk;
initial i = 0;
initial begin
tb_rst_n = 1'b0;
tb_key = 1'b1;
#(CLOCK_PERIOD * 20);
tb_rst_n = 1'b1;
#(CLOCK_PERIOD * 20);
for(i = 0; i < 100; i = i + 1) begin
tb_key = {
$random};
#(CLOCK_PERIOD * 30);
end
tb_key = 1'b1;
#(CLOCK_PERIOD * 30);
$stop; //停止仿真
end
endmodule
需要添加的文本如下
编译代码
编译结果
如果有报错,可以双击报错地方,回到错误地方进行修改。
四、模拟仿真
设置modelsim(安装可以参考我以前的博客)
【Tools】→【 op…】
按照图中提示操作
查看电路图
查看任意一个展开图
设置testbeach
开始仿真
仿真界面
这样就可以开始仿真了。
点击sim,添加仿真波形
鼠标右击添加波形
选择全部
点击run,查看波形
五、开发板验证
编辑引脚约束文件
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_E16 -to key_in
set_location_assignment PIN_G15 -to led[0]
set_location_assignment PIN_F16 -to led[1]
set_location_assignment PIN_F15 -to led[2]
set_location_assignment PIN_D16 -to led[3]
添加脚本
双击文件
点击快捷键查看引脚约束状况
或者按照图示选择
查看引脚约束
连接开发板并打开电源,点击编译
如果编译出现错误,按照下面提示进行设置再编译
【assessments】→【device】
再进行如下设置(点击OK)
编译成功,出现如下界面(这里没有连接到开发板)
如果这里没有驱动,则需要我们下载
连接开发板,烧录
结果演示(通过k2按键,实现灯的亮灭)
①
②
③
④