按键消抖+蜂鸣器


前言

  继前面的课程:按键控制led灯,本期将推出按键控制蜂鸣器发声。


一、认识蜂鸣器

  蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源他激型与有源自激型。区别:有源蜂鸣器内部有震荡源,无源蜂鸣器内部没有震荡源。
无源蜂鸣器

图1. 无源蜂鸣器

有源峰鸣器

图2. 有源蜂鸣器

二、任务描述

  本实验是按键控制蜂鸣器发音。蜂鸣器的初始状态为鸣叫状态。
  1.如果是鸣叫状态,按下按键后就停止鸣叫。
  2.如果是停止鸣叫状态,按下按键后就重新开始鸣叫。


三、按键消抖

  按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能会误以为按下多次按键。

  1. 按键消抖目的:消除按键抖动对我们程序的影响。

真实按键

图3. 按键特写
3. 按键消抖解决方案1:延迟采样。

延迟采样1

图4. 延迟采样

4. 按键消抖解决方案2:信号变化频率平稳后并且持续20ms则采样。

延迟采样2

图5. 信号平稳后采样

四、系统框图

系统框图

图6. 系统框图

  按键控制蜂鸣器,中间通过FPGA对按键消抖,再将消抖后的按键信息输入给蜂鸣器,最后让蜂鸣器发声。

五、模块描述

模块描述

图7. 模块描述

  一共三个模块,key_debounce用于编写按键消抖模块,beep_control用于编写控制蜂鸣器发声模块,最后使用top_key_beep调用两个模块。

六、项目代码

  1. 创建key_debounce文件,编写key_debounce模块代码。
module key_debounce(//按键消抖模块
    input clk,
    input rst_n,
    input key_in,//未消抖的按键

    output reg key_down//消抖过后的按键值
);

//定义按键按下四个状态
localparam IDLE = 4'b0001;
localparam FILTER_DOWN = 4'b0010;//按键按下的过程
localparam HOLD_DOWN = 4'b0100;
localparam FILTER_UP = 4'b1000;//按键松开的过程

parameter TIME_DELAY = 20'd100_0000;//消抖20ms

wire add_delay_cnt;//开始计时的标志
wire end_delay_cnt;//结束计时的标志

reg [19:0] cnt;//计数寄存器,计时20ms

reg [3:0] state_c;//现态
reg [3:0] state_n;//次态

reg key_in_r0;
reg key_in_r1;

wire n_edge;//按键按下
wire p_edge;//按键松开

//各个状态之前,相互切换到条件
wire idle2filter_down_start;//to
wire filter_down2hold_down_start;
wire filter_down2idle_start;
wire hold_down2filter_up_start;
wire filter_up2idle_start;


//接收按键信号
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_in_r0 <= 1'b1;
        key_in_r1 <= 1'b1;
    end
    else begin 
        key_in_r0 <= key_in;
        key_in_r1 <= key_in_r0;
    end 
end

assign n_edge = ~key_in_r0 & key_in_r1;//按键按下
assign p_edge = ~key_in_r1 & key_in_r0;//按键松开


always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin 
        state_c <= state_n;
    end 
end

always @(*)begin 
     if(!rst_n)begin
       state_n = IDLE;
     end
     else begin 
        case (state_c)//判断当前状态

            IDLE:begin
                if(idle2filter_down_start)
                    state_n <= FILTER_DOWN;
                else
                    state_n <= state_c;
            end

            FILTER_DOWN:begin
                if(filter_down2hold_down_start)begin//to
                    state_n <= HOLD_DOWN;
                end
                else if(filter_down2idle_start)begin
                    state_n <= IDLE;
                end
                else
                    state_n <= state_c;
            end 

            HOLD_DOWN: begin
                if(hold_down2filter_up_start)begin
                    state_n <= FILTER_UP;
                end
                else
                    state_n <= state_c;
            end

            FILTER_UP:begin
                if(filter_up2idle_start)begin
                    state_n <= IDLE;
                end
                else 
                    state_n <= state_c;
            end
            default: ;
        endcase
     end 
end

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_down <= 0;
    end 
    else if(hold_down2filter_up_start)begin 
        key_down <= ~key_in_r1;//如果不去反,按键按下就输出0,取反,按键按下就输出1,按键按下输出高电平,否则输出低电平
    end 
    else begin 
        key_down <= 1'b0;
    end 
end

assign idle2filter_down_start = (state_c == IDLE) && (n_edge);
assign filter_down2idle_start = (state_c == FILTER_DOWN) &&(add_delay_cnt && p_edge);
assign filter_down2hold_down_start = (state_c == FILTER_DOWN) && (end_delay_cnt && !p_edge);
assign hold_down2filter_up_start = (state_c == HOLD_DOWN) && (p_edge);
assign filter_up2idle_start = (state_c ==  FILTER_UP) && (end_delay_cnt);

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt <= 0;
    end 
    else if(add_delay_cnt)begin 
            if(end_delay_cnt)begin 
                cnt <= 0;
            end
            else begin 
                cnt <= cnt + 1;
            end 
    end
   else  begin
       cnt <= cnt;
    end
end 

assign add_delay_cnt = (state_c == FILTER_DOWN) || (state_c == FILTER_UP);
assign end_delay_cnt = add_delay_cnt && cnt == TIME_DELAY - 1'd1;

endmodule

  1. 创建beep_control文件,编写beep_ctrl模块代码。
module beep_ctrl(
	input clk,
    input rst_n,
    input key_in,

    output reg beep
);

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        beep <= 1'b1;
    end 
    else if(key_in)begin 
        beep <= ~beep;
    end 
    else begin 
        beep <= beep;
    end 
end

endmodule 
  1. 创建top_key_beep文件,编写top_key_beep模块代码。
module top_key_beep(
    input clk,
    input rst_n,
    input key,

    output beep
);

wire key_reg;//消抖完成过后的键值

key_debounce u_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key_in (key),

.key_down (key_reg)
);

beep_ctrl u_beep_ctrl(
.clk (clk),
.rst_n (rst_n),
.key_in (key_reg),

.beep (beep)
);

endmodule

七、管脚信息

元件管脚
KEY1E15
KEY2E16
KEY3M16
KEY4M15
CLOCK(时钟)E1
BUZZER(蜂鸣器)J1
表1. 管脚信息表

pin planner

图8. pin planner


八、运行效果

按键控制蜂鸣器+按键消抖


总结

  以上就是今天要讲的内容,本文介绍了按键控制蜂鸣器发声,由于按键存在抖动,所以通过按键抖动模块进行消抖。

  • 12
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值