基于ZYNQ-7000系列的FPGA学习笔记7——按键控制蜂鸣器(模块化编写)
- 1. 实验要求
- 2. 功能分析
- 3. 模块设计
- 4. 波形图
- 4.1 按键消抖模块
- 4.2 按键控制蜂鸣器模块
- 5.代码编写
- 5.1 rtl代码
- 5.2 测试代码
- 6. 代码仿真
- 7. 添加约束文件并分析综合
在上期的内容中,我们通过模拟按键控制led,这一期学习的内容是:按键控制蜂鸣器
1. 实验要求
通过按键控制领航者底板上的蜂鸣器,要求如下:
- 初始状态蜂鸣器响起
- 按下一次按键,改变一次按键的状态
- 需要对输入的按键信号进行消抖,消抖的时间为20ms
- 按下复位按键,蜂鸣器恢复默认状态
2. 功能分析
首先我们需要看一下蜂鸣器相关的原理图:
可以看到蜂鸣器通过一个8050的三极管驱动,连接到FPGA芯片的Bank35 ,引脚为L23。需要让蜂鸣器叫的话,给高电平就可以。
关于按键部分的内容,可以参考本系列的前面几篇,这里不做介绍。
3. 模块设计
由于本次的实验内容中需要我们设计多个模块,所以我设计了上述的功能框图,可以分为下面三个子模块:
- 按键消抖模块:作用是对输入的按键信号进行一个20ms的消抖
- 按键控制蜂鸣器模块:作用是对消抖之后的数据边沿采集,通过采集到的信号沿控制蜂鸣器
- 按键控制蜂鸣器顶层模块:分别例化了按键消抖模块和按键控制蜂鸣器模块,完成整个实验要求。
4. 波形图
4.1 按键消抖模块
为了防止按键的输入信号在采集的过程中出现亚稳态,我这里进行了打两拍的操作。同时为了实现按键的消抖,我也定义了一个cnt,用来记录电平保持稳定的时间,当这个时间大于20ms时才任务是按键按下了,这样就实现了按键的消抖
4.2 按键控制蜂鸣器模块
由于按键控制蜂鸣器模块的按键输入是消抖的后的按键信息(已经打过两拍了),所以我这里只打一个拍,用来计算是否产生了下降沿,从而判断按键是否按下
【注】:由于顶层按键控制蜂鸣器只是例化了上述两个模块,因此这里就不绘制波形图了,之间看后面仿真结果就可以。
5.代码编写
5.1 rtl代码
- 按键消抖模块:key_filter.v
//模块的端口定义
module key_filter(input sys_clk,input sys_rst_n,input key,output reg key_filter);reg key_d0; // 打两拍的变量定义
reg key_d1;reg change; //这个是用来确定的是否有电平变化的,为1表示有电平变化,为0表示没有//定义计数的变量,用来判断电平是否持续了20ms
reg [19:0] cnt;
//CNT_MAX用来确定按键消抖的时间, CNT_MAX = 20ms / (1s / 50Mhz ) = 1000_000 ,其中20ms是消抖的时间,50Mhz是系统时钟频率
parameter CNT_MAX = 20'd1000_000; //对输入的key信号,打两拍操作
always @(posedge sys_clk or negedge sys_rst_n) beginif(sys_rst_n == 1'b0) begin //初始状态下,d0和d1都是高电平,和按键未按下时一致key_d0 <= 1'b1;key_d1 <= 1'b1;endelse begin //运行状态下,开始打拍key_d0 <= key;key_d1 <= key_d0;end
end//检测是否有电平变化,如果有的话,把change置1,否则保持0
always @(posedge sys_clk or negedge sys_rst_n) beginif(sys_rst_n == 1'b0)change <= 1'b0;else begin if ( key_d0 != key_d1 )change <= 1'b1;else change <= 1'b0;end
end//如果change为1,说明电平有变化,重新开始计数,否则cnt减一,减到0停止
always @(posedge sys_clk or negedge sys_rst_n) beginif(sys_rst_n == 1'b0) cnt <= 20'd0;else beginif(change == 1'b1)cnt <= CNT_MAX;else beginif(cnt >20'd0)cnt <= cnt - 20'd1;elsecnt <= 20'd0;endend
end//如果cnt顺利的减到了1,说明电平保持了20ms,可以输出
always @(posedge sys_clk or negedge sys_rst_n) beginif(sys_rst_n == 1'b0) key_filter <= 1'b1;else beginif(cnt == 20'd1)key_filter <= key_d1; elsekey_filter <= key_filter;end
endendmodule
大致流程如下:首先对输入的按键信号大量拍,判断是否按键的电平是否变化,如果变化就把cnt置为最大值,如果没有变化,cnt每个系统时钟减1,直到减到1,表示抖动结束,输出当前的按键信息,否则输出上一次的按键信息。
- 按键控制蜂鸣器模块:key_beep.v
module key_beep(input sys_clk,input sys_rst_n,input key_filter, //消抖后按键值output reg beep //蜂鸣器
);reg key_filter_d0; //将消抖后的按键值延迟一个时钟周期wire neg_key_filter; //按键有效脉信号//捕获按键端口的下降沿,得到一个时钟周期的脉冲信号
assign neg_key_filter = (~key_filter) & key_filter_d0;//对按键端口的数据延迟一个时钟周期
always @ (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)key_filter_d0 <= 1'b1;elsekey_filter_d0 <= key_filter;
end//每次按键按下时,就翻转蜂鸣器的状态
always @ (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)beep <= 1'b1;else if(neg_key_filter) //有效的一次按键被按下beep <= ~beep;elsebeep <= beep;
end
endmodule
大致流程如下:对消抖之后的按键输入打一个拍,用来判断是否产生下降沿,如果产生下降沿,说明有按键按下,改变一次蜂鸣器的状态。
- 顶层按键控制蜂鸣器模块:top_key_beep.v
module top_key_beep(input sys_clk , //系统时钟input sys_rst_n , //系统复位,低电平有效input key , //按键output beep //蜂鸣器
);parameter CNT_MAX = 20'd100_0000; //消抖时间 20mswire key_filter ; //按键消抖后的值//例化按键消抖模块
key_filter #( .CNT_MAX (CNT_MAX) ) u_key_filter(.sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.key (key),.key_filter (key_filter)
);//例化蜂鸣器控制模块
key_beep u_key_beep (.sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.key_filter (key_filter),.beep (beep)
);
endmodule
5.2 测试代码
`timescale 1ns/1ns //仿真单位/精度module tb_top_key_beep();parameter CLK_PERIOD = 20; //时钟周期20ms
parameter CNT_MAX = 20'd10; //消抖时间 200ns//定义输入和输出的变量
reg sys_clk;
reg sys_rst_n;
reg key;wire beep;//这里用来产生按键按下的信息
initial beginsys_clk <=1'b0;sys_rst_n <=1'b0;key <= 1'b0; #190sys_rst_n <=1'b1;key <= 1'b1; //抖动开始#20key <= 1'b0;#30key <= 1'b1;#40key <= 1'b0;#30key <= 1'b1;#40key <= 1'b0; //抖动结束#300key <= 1'b1; //松开按键,抖动开始#40key <= 1'b0;#30key <= 1'b1; //抖动结束 end//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;//例化测试设计
top_key_beep #(.CNT_MAX (CNT_MAX) ) u_top_key_beep(.sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.key (key),.beep (beep)
);endmodule
【注】:这里的测试代码只针对顶层文件,底层的模块测试代码,暂时不需要。
6. 代码仿真
如下是仿真的结果:
可以看到仿真的结果和我们话的波形图是基本一致的,并且小于200ns的抖动都已经被滤除了,很不错。
7. 添加约束文件并分析综合
首先我们添加对应的约束文件
#时序约束
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
#IO 管脚约束
set_property -dict {PACKAGE_PIN M14 IOSTANDARD LVCMOS33} [get_ports beep]
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports key]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
完成之后,分析综合,得到如下结果:
-
顶层模块:
可以看到里面分别由两个子模块连接,下面是子模块的内部结构。 -
按键消抖模块
- 按键控制蜂鸣器模块
下一步就是生成比特流文件,然后上板调试了,这里结果我就还是不展示了。
以上就是本期的所有内容,创造不易,点个关注再走呗。