超级版主
- 积分
- 4754
- 金钱
- 4754
- 注册时间
- 2019-5-8
- 在线时间
- 1239 小时
|
本帖最后由 正点原子运营 于 2021-10-30 10:06 编辑
1)实验平台:正点原子新起点V2FPGA开发板
2) 章节摘自【正点原子】《新起点之FPGA开发指南 V2.1》
3)购买链接:https://detail.tmall.com/item.htm?id=609758951113
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_xinqidian(V2).html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:712557122
第十章 按键控制蜂鸣器实验
蜂鸣器(Buzzer)是现代常用的一种电子发声器,主要用于产生声音信号。蜂鸣器在生活中已经得到广泛使用,其典型应用包括医疗,消防等领域的各种报警装置以及日常生活中的各种警报器等。本章我们主要学习如何使用按键来控制蜂鸣器发声。
本章包括以下几个部分:
1010.1简介
10.2实验任务
10.3硬件设计
10.4程序设计
10.5下载验证
10.1简介
蜂鸣器按照驱动方式主要分为有源蜂鸣器和无源蜂鸣器,其主要区别为蜂鸣器内部是否含有震荡源。一般的有源蜂鸣器内部自带了震荡源,只要通电就会发声。而无源蜂鸣器由于不含内部震荡源,需要外接震荡信号才能发声。
图 10.1.1 左边为有源蜂鸣器 右边为无源蜂鸣器
图 10.1.2 本次实验使用的有源蜂鸣器
如图 10.1.1所示,从外观上看,两种蜂鸣器很相似,如将两种蜂鸣器的引脚都朝上放置,可以看出有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的一种是有源蜂鸣器。
相较于有源蜂鸣器,无源蜂鸣器成本更低,且发声频率可控。而有源蜂鸣器控制相对简单,由于内部自带震荡源,只要加上合适的直流电压即可发声。本次实验使用的蜂鸣器为图 10.1.2中的有源蜂鸣器。
10.2实验任务
本节实验任务是使用按键控制蜂鸣器发声。初始状态为蜂鸣器鸣叫,按下开关后蜂鸣器停止鸣叫,再次按下开关,蜂鸣器重新鸣叫。
10.3硬件设计
图 10.3.1 蜂鸣器控制电路原理图
图 10.3.1为蜂鸣器控制电路的原理图,我们可以看到蜂鸣器受到三极管的控制,这里三极管充当开关的作用,它基极连接到FPGA的IO引脚当FPGA输出高电平时三极管导通,蜂鸣器鸣叫反之蜂鸣器停止鸣叫。
本实验的管脚分配如下表所示:
表 10.3.1 按键控制蜂鸣器实验管脚分配
对应的TCL约束语句如下:
set_location_assignment PIN_E16 -to key
set_location_assignment PIN_D12 -to beep
set_location_assignment PIN_M2 -to sys_clk
set_location_assignment PIN_M1 -to sys_rst_n
set_instance_assignment -name CURRENT_STRENGTH_NEW 12MA -to beep
需要说明的是,由于蜂鸣器需要的驱动电流较大,使用默认8mA的驱动电流有可能出现蜂鸣器发声较小的情况,解决方法是将蜂鸣器输出的驱动电流修改成12mA或者是16mA,如下图所示:
图 10.3.2 按键控制蜂鸣器管脚分配图
当然也可以在对应的TCL约束语句中修改,如上文TCL的最后一行所示,具体使用哪种方法大家根据自己喜好来。
10.4程序设计
由实验任务可知,我们只需要在按键按下时改变蜂鸣器的鸣叫状态,但实际上在按键按下的过程中存在按键抖动的干扰,体现在数字电路中就是不断变化的高低电平,为避免在抖动过程中采集到错误的按键状态,我们需要对按键数据进行消除抖动处理。因此本系统应至少包含按键消抖模块和蜂鸣器控制模块,按键控制蜂鸣器系统框图如图 10.4.1所示。
图 10.4.1 按键控制蜂鸣器系统框图
这里我们补充下如何查看软件生成的模块端口及信号连接图。首先在对工程进行编译,然后点击菜单栏的【Tools】→【NetList Viewers】→【RTL Viewer】,如图 10.4.2所示:
图 10.4.2 打开端口及信号连接图
稍后就可以看到软件生成的模块端口及信号连接图了,如图 10.4.3所示:
图 10.4.3 端口及信号连接图
需要注意的是,必须已经执行过综合或编译之后,才能打开模块端口及信号连接图。打开之后,按下键盘的【Ctrl】键,滚动鼠标的滚轮可以对生连接图进行放大和缩小。模块端口及信号连接图可以比较清晰的查看各个模块端口信号的连接,同时双击模块,也可以进一步查看模块的原理图。
由图 10.4.3的连接图可知,顶层模块例化了以下两个模块,按键消抖模块(key_debounce)和蜂鸣器控制模块(beep_control)。顶层模块(top_key_beep)完成了对另外两个模块的例化。按键消抖模块,主要起到延时采样,防止按键抖动的干扰。蜂鸣器控制模块,通过对按键信号的识别,起到控制蜂鸣器鸣叫的作用。
按键消抖模块(key_debounce):对按键信号延时采样,将消抖后的按键信号和按键数据有效信号输出至beep_control模块。
蜂鸣器控制模块(beep_control):根据输入的按键信号和按键数据有效信号,来控制蜂鸣器的鸣叫。
在这里我们介绍一下按键消抖的原理。通常我们所使用的开关为机械弹性开关,当我们按下或松开按键时,由于弹片的物理特性,不能立即闭合或断开,往往会在断开或闭合的短时间内产生机械抖动,消除这种抖动的过程即称为按键消抖。
按键消抖可分为硬件消抖和软件消抖。硬件消抖主要使用RS触发器或电容等方法实现消抖,一般在按键较少时使用。软件消抖的原理主要为按键按下或松开后延时5ms—20ms采样,也可以在检测到按键状态稳定后采样,即避开抖动区域后再采样,如图 10.4.4所示。
图 10.4.4 按键消抖原理图
顶层模块代码如下:
- 1 module top_key_beep(
- 2 input sys_clk, //时钟信号50Mhz
- 3 input sys_rst_n, //复位信号
- 4
- 5 input key, //按键信号
- 6 output beep //蜂鸣器控制信号
- 7 );
- 8
- 9 //wire define
- 10 wire key_value;
- 11 wire key_flag;
- 12
- 13 //*****************************************************
- 14 //** main code
- 15 //*****************************************************
- 16
- 17 //例化按键消抖模块
- 18 key_debounce u_key_debounce(
- 19 .sys_clk (sys_clk),
- 20 .sys_rst_n (sys_rst_n),
- 21
- 22 .key (key),
- 23 .key_flag (key_flag),
- 24 .key_value (key_value)
- 25 );
- 26
- 27 //例化蜂鸣器控制模块
- 28 beep_control u_beep_control(
- 29 .sys_clk (sys_clk),
- 30 .sys_rst_n (sys_rst_n),
- 31
- 32 .key_flag (key_flag),
- 33 .key_value (key_value),
- 34 .beep (beep)
- 35 );
- 36
- 37 endmodule
复制代码
在顶层模块中例化了按键消抖模块和按键控制蜂鸣器模块。
按键消抖模块代码如下:
- 1 module key_debounce(
- 2 input sys_clk, //外部50M时钟
- 3 input sys_rst_n, //外部复位信号,低有效
- 4
- 5 input key, //外部按键输入
- 6 output reg key_flag, //按键数据有效信号
- 7 output reg key_value //按键消抖后的数据
- 8 );
- 9
- 10 //reg define
- 11 reg [31:0] delay_cnt;
- 12 reg key_reg;
- 13
- 14 //*****************************************************
- 15 //** main code
- 16 //*****************************************************
- 17 always @(posedge sys_clk or negedge sys_rst_n) begin
- 18 if (!sys_rst_n) begin
- 19 key_reg <= 1'b1;
- 20 delay_cnt <= 32'd0;
- 21 end
- 22 else begin
- 23 key_reg <= key;
- 24 if(key_reg != key) //一旦检测到按键状态发生变化(有按键被按下或释放)
- 25 delay_cnt <= 32'd1000000; //给延时计数器重新装载初始值(计数时间为20ms)
- 26 else if(key_reg == key) begin //在按键状态稳定时,计数器递减,开始20ms倒计时
- 27 if(delay_cnt > 32'd0)
- 28 delay_cnt <= delay_cnt - 1'b1;
- 29 else
- 30 delay_cnt <= delay_cnt;
- 31 end
- 32 end
- 33 end
- 34
- 35 always @(posedge sys_clk or negedge sys_rst_n) begin
- 36 if (!sys_rst_n) begin
- 37 key_flag <= 1'b0;
- 38 key_value <= 1'b1;
- 39 end
- 40 else begin
- 41 if(delay_cnt == 32'd1) begin //当计数器递减到1时,说明按键稳定状态维持了20ms
- 42 key_flag <= 1'b1; //此时消抖过程结束,给出一个时钟周期的标志信号
- 43 key_value <= key; //并寄存此时按键的值
- 44 end
- 45 else begin
- 46 key_flag <= 1'b0;
- 47 key_value <= key_value;
- 48 end
- 49 end
- 50 end
- 51
- 52 endmodule
复制代码
程序中第25行不断检测按键状态,一旦发现按键状态发生改变,就给计数器delay_cnt赋初值1000000。在按键状态不发生改变时,delay_cnt递减从而实现倒计时的功能,在倒计时过程中,一旦检测到按键状态发生改变,则说明有抖动产生,此时重新给delay_cnt赋初值,并开始新一轮倒计时。在50Mhz时钟驱动下,delay_cnt若能由1000000递减至1,则说明按键状态保持稳定时间达20ms,此时输出一个时钟周期的通知信号key_flag,并将此时的按键数据寄存输出。
蜂鸣器控制模块的代码如下:
- 1 module beep_control(
- 2 //input
- 3 input sys_clk, //系统时钟
- 4 input sys_rst_n, //复位信号,低电平有效
- 5
- 6 input key_flag, //按键有效信号
- 7 input key_value, //消抖后的按键信号
- 8 output reg beep //蜂鸣器控制信号
- 9 );
- 10
- 11 //*****************************************************
- 12 //** main code
- 13 //*****************************************************
- 14 always [url=home.php?mod=space&uid=95564]@[/url] (posedge sys_clk or negedge sys_rst_n) begin
- 15 if(!sys_rst_n)
- 16 beep <= 1'b1;
- 17 else if(key_flag && (~key_value)) //判断按键是否有效按下
- 18 beep <= ~beep;
- 19 end
- 20
- 21 endmodule
复制代码
beep初始状态为高电平,蜂鸣器鸣叫,当检测到按键有效信号key_flag为高电平,同时按键信号key_value为低电平时说明按键被有效按下,此时beep取反,蜂鸣器停止鸣叫。当按键再次按下时,beep再次取反,蜂鸣器重新开始鸣叫。
为了验证我们的程序,我们在modelsim内对代码进行仿真。
Test bench模块代码如下
- 1 `timescale 1 ns/ 1 ns
- 2 module tb_top_key_beep();
- 3
- 4 //parameter define
- 5 parameter T = 20;
- 6
- 7 //reg define
- 8 reg key;
- 9 reg sys_clk;
- 10 reg sys_rst_n;
- 11 reg key_value;
- 12
- 13 // wire define
- 14 wire beep;
- 15
- 16 //*****************************************************
- 17 //** main code
- 18 //*****************************************************
- 19
- 20 //给信号初始值
- 21 initial begin
- 22 key <= 1'b1;
- 23 sys_clk <= 1'b0;
- 24 sys_rst_n <= 1'b0;
- 25 #20 sys_rst_n <= 1'b1; //在第20ns的时候复位信号信号拉高
- 26 #30 key <= 1'b0; //在第50ns的时候按下按键
- 27 #20 key <= 1'b1; //模拟抖动
- 28 #20 key <= 1'b0; //模拟抖动
- 29 #20 key <= 1'b1; //模拟抖动
- 30 #20 key <= 1'b0; //模拟抖动
- 31 #170 key <= 1'b1; //在第300ns的时候松开按键
- 32 #20 key <= 1'b0; //模拟抖动
- 33 #20 key <= 1'b1; //模拟抖动
- 34 #20 key <= 1'b0; //模拟抖动
- 35 #20 key <= 1'b1; //模拟抖动
- 36 #170 key <= 1'b0; //在第550ns的时候再次按下按键
- 37 #20 key <= 1'b1; //模拟抖动
- 38 #20 key <= 1'b0; //模拟抖动
- 39 #20 key <= 1'b1; //模拟抖动
- 40 #20 key <= 1'b0; //模拟抖动
- 41 #170 key <= 1'b1; //在第800ns的时候松开按键
- 42 #20 key <= 1'b0; //模拟抖动
- 43 #20 key <= 1'b1; //模拟抖动
- 44 #20 key <= 1'b0; //模拟抖动
- 45 #20 key <= 1'b1; //模拟抖动
- 46 end
- 47
- 48 //50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
- 49 always # (T/2) sys_clk <= ~sys_clk;
- 50
- 51 //例化key_beep模块
- 52 top_key_beep u1 (
- 53 .beep(beep),
- 54 .key(key),
- 55 .sys_clk(sys_clk),
- 56 .sys_rst_n(sys_rst_n)
- 57 );
- 58
- 59 endmodule
复制代码
仿真波形图如下:
图 10.4.5 仿真波形图
测试代码中,为了方便仿真波形的查看,将按键消抖模块中的延时采样的延时时间改为四个时钟周期(将按键消抖模块中的第26行代码 delay_cnt <= 32'd1000000; 改为delay_cnt <= 32'd4;)。tb_key_beep模块中第22行到第45行为信号的激励。从图 10.4.5可以看到,第50ns时,将key拉低,并在50至130ns时模拟按键抖动,可见在按键抖动停止后的第4个时钟周期时,key_flag出现一个时钟周期的高电平,同时beep被拉低(蜂鸣器停止鸣叫);在第300ns时松开按键,随后模拟按键抖动,同理可知在抖动结束后的第四个时钟周期,key_flag信号被拉高。读者可以仔细观察仿真波形结合代码深入理解,仔细体会key_flag信号和key信号之间的关系。
10.5下载验证
首先我们打开按键控制蜂鸣器工程,在工程所在的路径下打开top_key_beep/par文件夹,在里面找到“top_key_beep.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及下划线组成,不能出现中文、空格以及特殊字符等。top_key_beep工程打开后如图 10.5.1所示。
图 10.5.1 打开工程
工程打开后通过点击工具栏中的“Programmer”图标(图中红框位置)打开下载界面。
下载界面如图 10.5.2所示(图中已下载完毕),查看图中是否已经加载下载文件(sof文件)。如果没有,则需要通过点击“Add File”按钮添加流水灯工程中key_led/par/output_files目录下的“key_led.sof”文件。
图 10.5.2 下载界面
图 10.5.3 新起点蜂鸣器
开发板电源打开后,在程序下载界面点击“Hardware Setup”, 在弹出的对话框中选择当前的硬件连接为“USB-Blaster”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板中。
下载完成后,就可以利用按键来控制蜂鸣器了。
|
|