一.蜂鸣器硬件原理
- 由上图可知,蜂鸣器原理是低电平有效
二.音频原理
- 音频周期频率公式为T(周期)=1/f(频率)。我们需要得到20ns震动的次数,例如高音Do的震动次数计算方式如下:
三.乐谱
-
我们想播放《粉刷匠》,需要得到它的乐谱如下
-
于是我们可以对应每一个音符应该是DO,RE,MI,FA,SO,LA,SI中的哪一个。
-
每两个“ | ”之间设定为1s,那么每一拍设定为250ms,有一些空拍,我们将空拍设定为保持上一个音符的频率。
四.设计思路
- 1.每一个音符计250ms
- 2.计震动的频率
- 3.计音符总个数。例如《粉刷匠》设定64个音符(包括空拍)
- 4.考察音符个数计数器的值,确定每个音符震动频率应该是多少
- 5.震动频率计数器和总频率的一半(以实现占空比50%)比较,来控制蜂鸣器响或不响。
五.代码
/**************************************功能介绍***********************************
Date : 2023年8月3日15:25:24
Author : Alegg xy.
Version : 14.0
Description: 《粉刷匠》
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module beep_music(
input clk ,
input rst_n ,
output reg beep
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_300ms = 25'd15_000_000;//300ms
parameter HIGH_DO = 18'd47750,//震动频率
HIGH_RE = 18'd42250,
HIGH_MI = 18'd37900,
HIGH_FA = 18'd37550,
HIGH_SO = 18'd31850,
HIGH_LA = 18'd28400,
HIGH_XI = 18'd25400;
reg [18:0] freq_r;//寄存频率值
parameter NUM = 7'd64;//共64个音符
//---------<内部信号定义>-----------------------------------------------------
reg [24:0] cnt_300ms ;//一个音符占300ms计数器
wire add_cnt_300ms ;
wire end_cnt_300ms ;
reg [15:0] cnt_freq ;//震动频率计数器
wire add_cnt_freq ;
wire end_cnt_freq ;
reg [5:0] cnt_num ;//音符个数计数器
wire add_cnt_num ;
wire end_cnt_num ;
//计一个音符占250ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_300ms <= 'd0;
end
else if(add_cnt_300ms)begin
if(end_cnt_300ms)begin
cnt_300ms <= 'd0;
end
else begin
cnt_300ms <= cnt_300ms + 1'd1;
end
end
end
assign add_cnt_300ms = 1'b1;
assign end_cnt_300ms = add_cnt_300ms && cnt_300ms == MAX_300ms - 1'd1;
//震动频率计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_freq <= 'd0;
end
else if(add_cnt_freq)begin
if(end_cnt_freq)begin
cnt_freq <= 'd0;
end
else begin
cnt_freq <= cnt_freq + 1'd1;
end
end
end
assign add_cnt_freq = 1'b1;
assign end_cnt_freq = add_cnt_freq && ((cnt_freq == freq_r - 1'd1) || end_cnt_300ms);
//音符个数计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 'd0;
end
else if(add_cnt_num)begin
if(end_cnt_num)begin
cnt_num <= 'd0;
end
else begin
cnt_num <= cnt_num + 1'd1;
end
end
end
assign add_cnt_num = end_cnt_300ms;
assign end_cnt_num = add_cnt_num && cnt_num == NUM - 1'd1 && cnt_300ms == MAX_300ms - 1'd1;
//组合逻辑考察每个音符对应的震动频率
always @(*)begin
case (cnt_num)
6'd0: freq_r = HIGH_SO ;
6'd1: freq_r = HIGH_MI ;
6'd2: freq_r = HIGH_SO ;
6'd3: freq_r = HIGH_MI ;
6'd4: freq_r = HIGH_SO ;
6'd5: freq_r = HIGH_MI ;
6'd6: freq_r = HIGH_DO ;
6'd7: freq_r = HIGH_DO ;
6'd8: freq_r = HIGH_RE ;
6'd9: freq_r = HIGH_FA ;
6'd10: freq_r = HIGH_MI ;
6'd11: freq_r = HIGH_RE ;
6'd12: freq_r = HIGH_SO ;
6'd13: freq_r = HIGH_SO ;
6'd14: freq_r = HIGH_SO ;
6'd15: freq_r = HIGH_SO ;
6'd16: freq_r = HIGH_SO ;
6'd17: freq_r = HIGH_MI ;
6'd18: freq_r = HIGH_SO ;
6'd19: freq_r = HIGH_MI ;
6'd20: freq_r = HIGH_SO ;
6'd21: freq_r = HIGH_MI ;
6'd22: freq_r = HIGH_DO ;
6'd23: freq_r = HIGH_DO ;
6'd24: freq_r = HIGH_RE ;
6'd25: freq_r = HIGH_FA ;
6'd26: freq_r = HIGH_MI ;
6'd27: freq_r = HIGH_RE ;
6'd28: freq_r = HIGH_DO ;
6'd29: freq_r = HIGH_DO ;
6'd30: freq_r = HIGH_DO ;
6'd31: freq_r = HIGH_DO ;
6'd32: freq_r = HIGH_RE ;
6'd33: freq_r = HIGH_RE ;
6'd34: freq_r = HIGH_FA ;
6'd35: freq_r = HIGH_FA ;
6'd36: freq_r = HIGH_MI ;
6'd37: freq_r = HIGH_DO ;
6'd38: freq_r = HIGH_SO ;
6'd39: freq_r = HIGH_SO ;
6'd40: freq_r = HIGH_RE ;
6'd41: freq_r = HIGH_FA ;
6'd42: freq_r = HIGH_MI ;
6'd43: freq_r = HIGH_RE ;
6'd44: freq_r = HIGH_SO ;
6'd45: freq_r = HIGH_SO ;
6'd46: freq_r = HIGH_SO ;
6'd47: freq_r = HIGH_SO ;
6'd48: freq_r = HIGH_SO ;
6'd49: freq_r = HIGH_MI ;
6'd50: freq_r = HIGH_SO ;
6'd51: freq_r = HIGH_MI ;
6'd52: freq_r = HIGH_SO ;
6'd53: freq_r = HIGH_MI ;
6'd54: freq_r = HIGH_DO ;
6'd55: freq_r = HIGH_DO ;
6'd56: freq_r = HIGH_RE ;
6'd57: freq_r = HIGH_FA ;
6'd58: freq_r = HIGH_MI ;
6'd59: freq_r = HIGH_RE ;
6'd60: freq_r = HIGH_DO ;
6'd61: freq_r = HIGH_DO ;
6'd62: freq_r = HIGH_DO ;
6'd63: freq_r = HIGH_DO ;
default:freq_r = HIGH_DO;
endcase
end
//震动频率计数器和对应震动频率总值的一半比较生成flag信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
beep <= 1'd1;
end
else if(cnt_freq < (freq_r >> 1))begin
beep <= 1'b0;
end
else begin
beep <= 1'b1;
end
end
endmodule