FPGA实战------蜂鸣器

FPGA蜂鸣器

之前已经学完了led花样点灯,数码管静动显示。接下来开始开发板上的另一个基础元件–蜂鸣器。



前言

这里用蜂鸣器唱一个生日快乐歌,从网上就能找到生日快乐的简谱,我找了好几个都不一样,最后用了一个差不多的。
在这里插入图片描述

一、蜂鸣器

按照工作原理可分为:压电式蜂鸣器和电磁式蜂鸣器。
按照音源可分为:有源蜂鸣器和无源蜂鸣器。
有源蜂鸣器:内部有振荡源,直接通以直流电即可发出声音。
无源蜂鸣器:内部无振荡源,需要通以方波、PWM信号才能发出声音。
咱这里用的是无源蜂鸣器。
无源蜂鸣器需要输入一定频率的方波或者脉冲宽度调制(Pulse Width Modulation,PWM)信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音。

二、音调频率

音频(Audio),指人耳可以听到的声音频率在20HZ~20kHz之间的声波。乐谱由音符组成,不同的音符拥有不同的频率。音频和周期的关系如下公式所示。
在这里插入图片描述

在这里插入图片描述
Cyclone IV开发板的晶振是50MHz,振动一次是20纳秒,使用周期时间除以20纳秒得出音符振动的次数。比如高音的DO计算方式如下
在这里插入图片描述

三、代码

/*
 * @Projcet: beep
 * @Author: Yang.
 * @Date: 2023-08-23 08:31:27
 * @LastEditors: Yang.
 * @LastEditTime: 2023-09-18 17:59:34
 */
//---------<模块及端口声名>------------------------------------------------------
module beep_ctrl( 
    input        clk     ,
    input        rst_n   ,
    output       beep     
);

//------------------------<音节代码>---------------------------
localparam  G1 = 5'd01 ,
            G2 = 5'd02 ,
            G3 = 5'd03 ,
            G4 = 5'd04 ,
            G5 = 5'd05 ,
            G6 = 5'd06 ,
            G7 = 5'd07 ;
localparam  M1 = 5'd08 ,
            M2 = 5'd09 ,
            M3 = 5'd10 ,
            M4 = 5'd11 ,
            M5 = 5'd12 ,
            M6 = 5'd13 ,
            M7 = 5'd14 ;
localparam  D1 = 5'd15 ,
            D2 = 5'd16 ,
            D3 = 5'd17 ,
            D4 = 5'd18 ,
            D5 = 5'd19 ,
            D6 = 5'd20 ,
            D7 = 5'd21 ;
parameter   MAX_700MS = 26'd35_000_000 ;
parameter   MAX_FLAG  = 7'd55 ;

reg         beep_r     ;//控制蜂鸣器
reg  [5:0]  flag       ;//记录蜂鸣器的音节位置
reg  [4:0]  music_r    ;//记录音节内容
reg  [25:0] cnt_700ms  ;//时间计数器
wire        add_cnt_700ms ;
wire        end_cnt_700ms ;
reg  [17:0] MAX_music  ;//用于记录音节频率
reg  [17:0] cnt_music  ;//记录单个音节的震荡次数

//------------------------<单个音节振动时间>---------------------------
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_700ms <= 0 ;     
    end
    else if (add_cnt_700ms) begin
        if (end_cnt_700ms) begin
            cnt_700ms <= 0 ;
        end
        else begin
            cnt_700ms <= cnt_700ms + 1 ;
        end
    end
end

assign add_cnt_700ms = 1'b1 ;
assign end_cnt_700ms = add_cnt_700ms && cnt_700ms == MAX_700MS - 1 ;

//------------------------<记录音节位置>---------------------------
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        flag <= 0 ;     
    end
    else if (flag == MAX_FLAG && cnt_700ms == MAX_700MS - 1) begin
        flag <= 0 ;
    end
    else if (flag != MAX_FLAG && cnt_700ms == MAX_700MS - 1) begin
        flag <= flag + 1 ;//每700ms响一个音
    end
    else begin
        flag <= flag ;
    end
end

//------------------------<记录音节频率>---------------------------
always @(*) begin
    case (music_r)
        D1      :   MAX_music = 18'd47778   ;
        D2      :   MAX_music = 18'd42565   ;
        D3      :   MAX_music = 18'd37922   ;
        D4      :   MAX_music = 18'd35793   ;
        D5      :   MAX_music = 18'd31888   ;
        D6      :   MAX_music = 18'd28409   ;
        D7      :   MAX_music = 18'd25310   ;
        M1      :   MAX_music = 18'd23889   ;
        M2      :   MAX_music = 18'd21283   ;
        M3      :   MAX_music = 18'd18961   ;
        M4      :   MAX_music = 18'd17897   ;
        M5      :   MAX_music = 18'd15944   ;
        M6      :   MAX_music = 18'd14205   ;
        M7      :   MAX_music = 18'd12655   ;
        G1      :   MAX_music = 18'd11945   ;
        G2      :   MAX_music = 18'd10641   ;
        G3      :   MAX_music = 18'd9480    ;
        G4      :   MAX_music = 18'd8948    ;
        G5      :   MAX_music = 18'd7972    ;
        G6      :   MAX_music = 18'd7102    ;
        G7      :   MAX_music = 18'd6327    ;
        default :   MAX_music = 18'd6327    ;
    endcase
end

//------------------------<记录音节位置>---------------------------
always@(*)begin
    case (flag)
        6'd00 :  music_r = M5 ;
        6'd01 :  music_r = M6 ;
        6'd02 :  music_r = M7 ;
        6'd03 :  music_r = M5 ;
        6'd04 :  music_r = G1 ;
        6'd05 :  music_r = M7 ;
        6'd06 :  music_r = M5 ;
        6'd07 :  music_r = M6 ;
        6'd08 :  music_r = M7 ;
        6'd09 :  music_r = M5 ;
        6'd10 :  music_r = G2 ;
        6'd11 :  music_r = G1 ;
        6'd12 :  music_r = M5 ;
        6'd13 :  music_r = M4 ;
        6'd14 :  music_r = G5 ;
        6'd15 :  music_r = G3 ;
        6'd16 :  music_r = G1 ;
        6'd17 :  music_r = M7 ;
        6'd18 :  music_r = M6 ;
        6'd19 :  music_r = G5 ;
        6'd20 :  music_r = G4 ;
        6'd21 :  music_r = G3 ;
        6'd22 :  music_r = G1 ;
        6'd23 :  music_r = G2 ;
        6'd24 :  music_r = G1 ;
        6'd25 :  music_r = M5 ;
        6'd26 :  music_r = M6 ;
        6'd27 :  music_r = M7 ;
        6'd28 :  music_r = M5 ;
        6'd29 :  music_r = G1 ;
        6'd30 :  music_r = M7 ;
        6'd31 :  music_r = M5 ;
        6'd32 :  music_r = M6 ;
        6'd33 :  music_r = M7 ;
        6'd34 :  music_r = M5 ;
        6'd35 :  music_r = G2 ;
        6'd36 :  music_r = G1 ;
        6'd37 :  music_r = M5 ;
        6'd38 :  music_r = M4 ;
        6'd39 :  music_r = G5 ;
        6'd40 :  music_r = G3 ;
        6'd41 :  music_r = G1 ;
        6'd42 :  music_r = M7 ;
        6'd43 :  music_r = M6 ;
        6'd44 :  music_r = G5 ;
        6'd45 :  music_r = G4 ;
        6'd46 :  music_r = G3 ;
        6'd47 :  music_r = G1 ;
        6'd48 :  music_r = G2 ;
        6'd49 :  music_r = G1 ;
        default: music_r = M5 ;
    endcase
end

//------------------------<每个音节在哪个位置进行震荡>---------------------------
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_music <= 0 ;
    end
    else if (cnt_700ms == MAX_700MS - 1 || cnt_music == MAX_music - 1) begin
        cnt_music <= 0 ;
    end
    else begin
        cnt_music <= cnt_music + 1 ;
    end
end

//------------------------<在指定位置震荡>---------------------------
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        beep_r <= 1'b1;
    end
    else if (cnt_music >= (MAX_music - 1'd1) / 2) begin
        beep_r <= 1'b0;
    end
    else begin
        beep_r <= 1'b1;
    end
end

assign beep = beep_r;

endmodule

总结

最后听起来还是有一点错误,不过仔细听能听出来大致是生日快乐歌。主要问题还是简谱找的不对,大家自己去找一找吧。太吵就没有录视频。
如果想听别的曲子就找简谱,音符上边有个点就是高音,下边有个点就是低音,上下没点就是中音。然后更改代码里的flag的位宽、数量,还有music_r,按照简谱排下来就行。
如果连续两个音符是同一个音调,蜂鸣器会响一个音中间没有间隔,听起来会很别扭。
别的不用改,也没啥需要注意的了。

对了,图中圈起来的那里要有那个黑色的套,蜂鸣器才会响。以前蜂鸣器一直不响,是这里没有黑色的套的问题。-_-’
在这里插入图片描述

接下来就是按键消抖,很简单,直接上一个万用模版套用就行。

博客上传的工程都在百度网盘,有压缩包也有整个文件,可以自行下载。
链接:https://pan.baidu.com/s/1lQqqWZXfb3i6XHwkKf52zg
提取码:yang

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值