相关文章推荐
咆哮的木瓜  ·  keyword not supported ...·  9 月前    · 
性感的青椒  ·  visual studio ...·  2 年前    · 

复位后蜂鸣器发声,按下按键后停止发声,再次按下继续发声。

有源无源的判断:
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)begin
	if(!sys_rst_n)begin
		key_reg   <= 1'b1;       		     //1为未按下
		delay_cnt <= 20'd0;    			     //计数器值清零
	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;
//按键抖动判断
always @ (posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		key_value  <= 1'b1;
        key_flag   <= 1'b0;
	else if(delay_cnt == 20'd1) begin         //从100_0000到了1,说明持续了20ms
		key_flag   <= 1'b1;
		key_value  <= key;                    //寄存此时的按键值
	else begin                                //计数器减到0
		key_flag   <= 1'b0;					  //这是个flag 标志位,不需要一直为1,只需要一个周期即可。
		key_value  <= key_value;
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;
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了。

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图,就可以看到内部
在这里插入图片描述
在这里插入图片描述

补充——例化模块 的软件操作:

首先两个底层模块加入进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 / 1ns
module tb_top_key_beep;
	// Inputs
	reg sys_clk;
	reg sys_rst_n;
	reg key;
	// Outputs
	wire 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 Inputs
		sys_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;      //松开
    //生成时钟  
	always #10 sys_clk = ~sys_clk;
endmodule

模拟了100ms之后的结果,我设置的是按键按下的延迟先20ms,后两个17ms,再一个20ms,可以看到符合我们的预期,之后前后两个才改变了beep的状态。
在这里插入图片描述

FPGA实战——按键控制蜂鸣器目录FPGA实战——按键控制蜂鸣器实验任务:蜂鸣器硬件设计程序设计rtl文件按键消抖ucf文件编译RTL图补充——例化模块 的软件操作:下载及debug仿真实验任务:复位后蜂鸣器发声,按下按键后停止发声,再次按下继续发声。蜂鸣器有源无源的判断:1.将蜂鸣器引脚朝上,可以看出有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的一种是有源蜂鸣器。2.万用表电阻档Rxl档: 用黑表笔接蜂鸣器 "+"引脚,红表笔在另一引脚上来回碰触,如果触发出咔、咔声的且电阻只有8Ω
定时器计数器 定时器:设置定时值,开始定时器之后,计数器开始计数,计数达到定时值后产生计数满标志信号,提示设定的时间到达。 计数器:计数时钟脉冲的上升沿或者下降沿的次数,或等待指定次数的脉冲波形信号出现后,产生响应信号。
Verilog Tips 1:TestBench编写注意事项【concurrent assignment to a non-net ‘xxxx‘ is not permitted】解决
一仿真,报错 concurrent assignment to a non-net ‘xxxx’ is not permitted 原因分析: 对于待测试模块的输出 “dout_7888”,在编写测试文件的时候,不能将与之交联的“dout_7888”定义为 reg 型,须改为 wire 型。 对于模块中的输出来说 即,不能以 TestBench中的 reg 型赋值给被测模块作为输出的 wire 型; 同,不能以 TestBench中的
A:通过FPGA的PWM信号控制无源蜂鸣器模块的振荡频率,从而使其发出不同的声音。具体步骤如下: 1. 将FPGA开发板上的PWM引脚与无源蜂鸣器模块的控制引脚连接。 2. 在FPGA的设计软件中设置PWM的频率和占空比,以达到控制无源蜂鸣器发声的效果。 3. 将FPGA开发板上的代码下载到开发板中,让FPGA开始工作,这时无源蜂鸣器模块会根据PWM信号发出相应的声音。 需要注意的是,不同的无源蜂鸣器模块有不同的频率响应范围,具体使用时需要根据实际情况调整PWM信号的频率和占空比。