FPGA实战篇——【3】按键控制蜂鸣器
FPGA实战——按键控制蜂鸣器
目录
- FPGA实战——按键控制蜂鸣器
- 实验任务:
- 蜂鸣器
- 硬件设计
- 程序设计
- rtl文件
- 按键消抖
- ucf文件
- 编译
- RTL图
- 补充——例化模块 的软件操作:
- 下载及debug
- 仿真
实验任务:
复位后蜂鸣器发声,按下按键后停止发声,再次按下继续发声。
蜂鸣器
有源无源的判断:
1.将蜂鸣器引脚朝上,可以看出有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的一种是有源蜂鸣器。
2.万用表电阻档Rxl档: 用黑表笔接蜂鸣器 "+"引脚,红表笔在另一引脚上来回碰触,如果触发出咔、咔声的且电阻只有8Ω(或16Ω)的是无源蜂鸣器;如果能发出持续声音的,且电阻在几百欧以上的,是有源蜂鸣器。
本次实验使用有源蜂鸣器
硬件设计
当BUZZER引脚输出低电平时,PNP导通,蜂鸣器发声工作。
程序设计
rtl文件
这次我们用到了按键消抖,需要单独写一个模块比较方便,所以这节开始,我们就开始用多个.V文件,然后写顶层模块调用例化的底层模块这种写法了。回顾一下例化的知识(伪代码):
比如我已经定义了
<底层led模块>
module pled(input sys_clk,output led_value
);
那么我在顶层模块例化他的时候:
<底层模块>
首先定义顶层模块:
module top_flow_led(input sys_clk,output led
);
例化led模块时:
pled pled_u(.sysclk(sys_clk),.led_value(led)
);在例化模块中,.sysclk后的()表示的是输入,也就是说,因为在原来的pled模块中,sysclk是input,所以()中的变量为一个输入,现在,只需要将开发板上的晶振连接到顶层文件的input sysclk,然后这个顶层文件的sysclk再输入到led 的sysclk就实现了对底层模块的时钟的赋值。***********************而底层模块pled中 led_value是个output,所以这里.led_value(led)中led是作为led_value的输出接收信号,接收底层模块的输出,然后通过顶层模块,连接到开发板的LED灯上。***********************这就是通过例化连接到顶层模块的写法。具体后面的例子变量多,看起来可能更容易理解。
按键消抖
思想:当检测到按键值发生变化,也就是被按下时,将按键值保存,保存之后,每隔一段时间取按键值与原来值进行比较,如果持续20ms不变,就认为按键确实按下了。
(实际上,只需要检测到按键按下之后,启动计数器(不管怎么抖,都以变化后为计时起点重新计时),只要计数器在计数到20ms之前 ,按键值没有发生变化,就认为按键被按下)
那么我们写一个按键消抖模块,输入key(只用一个按键)输出一个key_flag表示按键被按下的标志,再输出一个key_value来存储key的值。
(一些疑问在最后的 下载与debug解决部分解决)
**************按键消抖模块**************
module key_debounce( //消抖模块input sys_clk,input sys_rst_n,input key,output reg key_value,output reg key_flag);reg key_reg; //存储key的值,相当于temp的作用
reg [19:0] delay_cnt; //计数器,计数20ms内key未变即按键被按下 20ms / 20ns = 100_0000,20位//按键延时计数器
always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginkey_reg <= 1'b1; //1为未按下delay_cnt <= 20'd0; //计数器值清零endelse beginkey_reg <= key; //把key的值存储到寄存器中去if(key_reg != key) //说明按键值有变化!!!!!(不管怎么抖,都以变化后为计时起点重新计时) delay_cnt <= 20'd100_0000; //一旦检测到key值变化,就把计数器设到倒计时最大值。else if(key_reg == key)begin //说明此时按键还是按下的状态if(delay_cnt > 20'd0)delay_cnt <= delay_cnt - 1'b1;else //else 它要等于自己!!!!!delay_cnt <= delay_cnt;endend
end//按键抖动判断
always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginkey_value <= 1'b1;key_flag <= 1'b0;endelse if(delay_cnt == 20'd1) begin //从100_0000到了1,说明持续了20mskey_flag <= 1'b1;key_value <= key; //寄存此时的按键值endelse begin //计数器减到0key_flag <= 1'b0; //这是个flag 标志位,不需要一直为1,只需要一个周期即可。key_value <= key_value;end
end
endmodule
这里有一点需要讲解一下:
我们在key_reg <= key;之后,马上就判断key_reg和key是否相等,而且FPGA是并行的,这又是非阻塞赋值,那这不是肯定相等吗,其实不然。
always语句块是在posedge sys_clk的时候执行,其他时候不执行,而key_reg <= key; 这句虽然执行了,但是并不是马上把key的值给key_reg,而是需要等下一个clk上升沿才真正赋值,此时key_reg是上一clk的值,key就是实时的按键值,此时可以进行比较。
**************蜂鸣器模块**************
module beep_ctrl(input sys_clk,input sys_rst_n,input key_value,input key_flag,output reg beep
);always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beep <= 1'b0;else if(key_flag && (~key_value))beep <= ~beep;
endendmodule
**************顶层模块**************
module top_key_beep(input sys_clk,input sys_rst_n,input key,output beep);//只需要作为导线把两个模块连接到一起即可,不需要是reg型
wire key_value;
wire key_flag; key_debounce key_debounce_u(.sys_clk (sys_clk) ,.sys_rst_n (sys_rst_n), .key (key) ,.key_value (key_value), .key_flag (key_flag) );beep_ctrl beep_ctrl_u(.sys_clk (sys_clk) ,.sys_rst_n (sys_rst_n) ,.key_value (key_value) ,.key_flag (key_flag) ,.beep (beep) ); endmodule
发现顶层模块并不是top_key_beep,所以我们设置一下,
如果设置完之后没有变化,说明我们的代码有错误,一定要好好检查。比如endmodule会忘记加。(如果代码添加进ise之后发现全编译的绿色按钮灰色,下方工具栏很多没有显示,也可能使endmodule未加)
这样就OK了。
ucf文件
NET sys_clk TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 20ns HIGH 50%;NET sys_clk LOC = T8 | IOSTANDARD = LVCMOS33;
NET sys_rst_n LOC = L3 | IOSTANDARD = LVCMOS33;################## KEY ############################
NET key LOC = C3 | IOSTANDARD = LVCMOS33;################## BEEP ############################
NET beep LOC = J11 | IOSTANDARD = LVCMOS33;
编译
编译报错:
Line 29: Target of concurrent assignment or output port connection should be a net type.
原因是:
例化时输出必须为wire类型的!!!!!
RTL图
上次我们用了第一种,是需要我们手动添加的,这次我们用第二个,就不需要手动添加了。
首先是这样的,但是我们还想看内部的图,那么我们可以双击这个rtl图,就可以看到内部
补充——例化模块 的软件操作:
首先两个底层模块加入进ISE,然后创建一个顶层模块。接下来,对于底层模块的例化,我们可以
将这段代码复制进顶层文件即可。
下载及debug
生成bit流文件,就可以下载进开发板了。
发现不能按一下就一直响,响了按一下不能停,问题出在下图那
我们先分析下逻辑,如果计数器从1百万减到1了,就可以把flag变成1,此时让key_value等于0,就是按下了。但是我们忽略了一点,就是按键松开的时候,也需要消抖!!!!那这时候,是不是消抖完还让他等于0呢,不是应该是等于稳定后的key值也就是把这里改成:key_value <=key;
如果按我们刚才那种,那没错,按下去是正常的,但是松开的时候,因为我们flag =1 ,但是按键值key_vlaue却还是0,此时,在下图的逻辑中相当于又按下了一次!!!!
另外,这个bug 也解决了了另外一个问题,就是为什么key_flag只持续了一个clk周期,不能一直等于1,这样对于后面的按键松开消抖不利,实验证明,这样按键会及其不灵敏!!
仿真
tb很简单,思想就是模拟按键抖动即可。
`timescale 1ns / 1nsmodule tb_top_key_beep;// Inputsreg sys_clk;reg sys_rst_n;reg key;// Outputswire beep;// Instantiate the Unit Under Test (UUT)top_key_beep u_top_key_beep (.sys_clk(sys_clk), .sys_rst_n(sys_rst_n), .key(key), .beep(beep));initial begin// Initialize Inputssys_clk <= 1'b0;sys_rst_n <= 1'b0;key <= 1'b1;// Wait 100 ns for global reset to finish#100;sys_rst_n <= 1'b1;// Add stimulus here#150 key <= 1'b0; // 在第250ns按下按键#1_000_000 key <= 1'b1; //模拟按键抖动1ms#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#21_000_000 key <= 1'b1; //20ms之后松开按键#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#17_000_000 key <= 1'b0; //开始按下#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#17_000_000 key <= 1'b1; //松开#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#20_000_000 key <= 1'b0; //松开end//生成时钟 always #10 sys_clk = ~sys_clk;
endmodule
模拟了100ms之后的结果,我设置的是按键按下的延迟先20ms,后两个17ms,再一个20ms,可以看到符合我们的预期,之后前后两个才改变了beep的状态。
FPGA实战篇——【3】按键控制蜂鸣器相关推荐
- FPGA:基础入门按键控制蜂鸣器
题目概述: 使用按键控制蜂鸣器发声.初始状态为蜂鸣器鸣叫,按下开关后蜂鸣器停止鸣叫,再次按下开关,蜂鸣器重新鸣叫. key_debounce.vmodule key_debounce( input s ...
- 51单片机怎么显示当前时间_(进阶篇)51单片机之按键控制蜂鸣器、数码管、按键值移位显示...
一.实操演示- 按键控制蜂鸣器 1.图文详细 独立按键硬件电路 蜂鸣器硬件电路 2.连接方式: J20的第3号引脚连接到J7引脚,即P15连接J7. J29的第7.8号引脚连接到JP1的第1.2号引脚 ...
- 基于FPGA的两位按键控制LED数码管加减计数实验
两位按键控制LED数码管加减计数实验 这是一篇拖了一个多月的文章,主要是基于FPGA利用按键消抖原理与动态数码管驱动原理相结合,来实现一个利用两位按键来控制数码管实现0-99的加法计数或者减法计数功能 ...
- FPGA——输入原理图实现按键控制发光二极管的亮灭
文章目录 前言 一.FPGA的设计流程 二.按键控制发光二极管的亮灭的过程 (一)创建工程 (二)绘制原理图 (三)编译 (四)分配引脚 (五)仿真与时序分析 (六)配置FPGA (七)下载结果 总结 ...
- Arduino基础入门篇07—按键控制LED灯
前面介绍了Arduino数字I/O,通过控制数字引脚输出来控制LED灯亮灭.本篇将介绍数字I/O的输入功能,通过检测按键状态来控制LED灯亮灭,把LED的亮灭变成人为可控制的. 1. 实验材料 Uno ...
- FPGA实战篇——【6】动态数码管
FPGA实战--动态数码管 rtl文件 模块设计: 计数模块,产生数码管的数据 数码管显示驱动模块 时钟分频 数字转码(二进制->BCD码) 位选信号切换 调试warning: ucf文件 ** ...
- FPGA:基础入门按键控制LED灯
题目概述: 使用按键控制LED灯亮灭. 无按键按下--LED全灭 按下KEYO--从右向左的流水灯效果 按下KEY1--从左向右的流水灯效果 按下KEY2--LED闪烁 按下KEY3--LED全亮 编 ...
- Arduino基础入门篇(按键控制LED)
本篇我们主要介绍通过检测按键状态来控制LED灯亮灭,把LED的亮灭变成人为可控制的. 一.按键开关介绍 按键开关主要是指轻触式按键开关,也称之为轻触开关.按键开关是一种电子开关,属于电子元器件类,最早 ...
- c语言写按键控制蜂鸣器,51单片机用按键控制蜂鸣器发出do re mi fa...的声音,...
满意答案 0fhk9 2017.12.30 采纳率:53% 等级:7 已帮助:1961人 T0HEQU 30H T0L EQU 31H ORG 0000H LJMP MAIN ORG 000BH ...
最新文章
- 宏基因组序列物种分类之kraken 1/2和Bracken的使用
- linux系统日志_第十二章:走进Linux世界——系统日志管理,日志轮转。
- 如何移植.NET Framework项目至.NET Core?
- java断点续传 http_http断点续传简单实现(java)
- kafka创建topic_Kafka实战宝典:一文带解决Kafka常见故障处理
- 喂。請罘葽缺蓆涐旳以后
- 分布式事务 - 梁飞的博客 - ITeye博客
- 【Flink】Flink 源码之OperatorChain
- 两边定宽,中间自适应布局的四种实现方法
- C语言 进制转换 将十进制转换为任意进制
- 一篇文章理清产品、运营、营销之间的概念和关系
- 小白刷LeeCode(算法篇)
- 【微前端】微前端——功能团队中缺失的一块拼图
- 亲测解决知网下载的正版国家标准打开不了
- 践行绿色发展理念,产业园区绿色转型发展之五大路径
- H3C服务器BIOS界面,全新改版图形化界面
- 哪吒之魔童降世视听语言影评_《哪吒之魔童降世》影评4篇
- Qos(Quality of Service)
- 【震惊】没有java环境也能运行jar,在不安装jdk下如何运行jar包
- 计算机系400分左右的学校,杭州2021年400分能上计算机学校吗