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