FPGA实战——按键控制蜂鸣器
⽬录
实验任务:
复位后蜂鸣器发声,按下按键后停⽌发声,再次按下继续发声。
蜂鸣器
有源⽆源的判断:
1.将蜂鸣器引脚朝上,可以看出有绿⾊电路板的⼀种是⽆源蜂鸣器,没有电路板⽽⽤⿊胶封闭的⼀种是有源蜂鸣器。
2.万⽤表电阻档Rxl档: ⽤⿊表笔接蜂鸣器 "+"引脚,红表笔在另⼀引脚上来回碰触,如果触发出咔、咔声的且电阻只有8Ω(或16Ω)的是⽆源蜂鸣器;如果能发出持续声⾳的,且电阻在⼏百欧以上的,是有源蜂鸣器。
本次实验使⽤有源蜂鸣器
硬件设计
当BUZZER引脚输出低电平时,PNP导通,蜂鸣器发声⼯作。
程序设计
rtl⽂件
这次我们⽤到了按键消抖,需要单独写⼀个模块⽐较⽅便,所以这节开始,我们就开始⽤多个.V⽂件,然后写顶层模块调⽤例化的底层模块这种写法了。回顾⼀下例化的知识(伪代码): ⽐如我已经定义了
<;底层led模块>
module pled(
10种打隐私的方法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)begin
if(!sys_rst_n)begin
key_reg <=1'b1;//1为未按下
delay_cnt <=20'd0;//计数器值清零
end
else begin
key_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;
end
end
end
//按键抖动判断
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
key_value <=1'b1;
key_flag <=1'b0;
end
else if(delay_cnt ==20'd1) begin //从100_0000到了1,说明持续了20ms
key_flag <=1'b1;
key_value <= key;//寄存此时的按键值
end
else begin //计数器减到0
key_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)begin
if(!sys_rst_n)
beep <=1'b0;
else if(key_flag &&(~key_value))
beep <=~beep;
end
endmodule
**************顶层模块**************
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了。