前言
通过往期的按键控制蜂鸣器课程,我们了解了蜂鸣器器件,本次课程将使用蜂鸣器,播放我小时候经常听的《两只老虎》音乐,来勾起我童年的回忆。
一、回顾蜂鸣器
我们回顾一下蜂鸣器的知识:
- 按照工作原理可分为:压电式蜂鸣器和电磁式蜂鸣器。
- 按照音源可分为:有源蜂鸣器和无源蜂鸣器。
- 有源蜂鸣器:内部有振荡源,直接通以直流电即可发出声音。
- 无源蜂鸣器:内部无振荡源,需要通以方波、PWM信号才能发出声音。
无源蜂鸣器需要输入一定频率的方波或者脉冲宽度调制(Pulse Width Modulation,PWM)信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音。《红楼梦》中“未见其人先闻其声”指的是王熙凤,就是因为每个人发出的声音频率不同,所以音色也会不同,我们可以通过音色就知道谁是谁。
二、乐谱知识
2.1 音符音频
音频(Audio),指人耳可以听到的声音频率在20HZ~20kHz之间的声波。乐普是由音符组成的,不同的音符拥有不同的频率。音频和周期的关系如下公式所示。
T
(
周
期
)
=
1
f
(
频
率
)
(1)
T(周期) = \frac{1}{f(频率)}\tag1
T(周期)=f(频率)1(1)
我们可以根据表中音符频率用公式(1)计算出音符振动的周期,单位微秒。Cyclone IV开发板的晶振是50MHz,振动一次是20纳秒,使用周期时间除以20纳秒得出音符振动的次数。比如高音的DO计算方式如下公式(2)所示。
D O ( 高 ) = 955 × 1 0 3 20 = 47750 (2) DO(高) = \frac{955\times10^3}{20}\tag2=47750 DO(高)=20955×103=47750(2)
2.2 两只老虎乐谱
两只老虎乐谱一共有34个音符,1对应DO,2对应RE,3对应MI…。一个音符持续的时间很短,需要设置一个持续时间,重复播放该音符,这样我们才能听得出来。本实验中设置音符持续时间(节拍)300毫秒,要想使两只老虎听起来更完美,同学们下去得学习乐谱中节拍知识,根据乐谱中节拍设置音符的持续时间。
三、系统框图
四、模块调用
五、模块原理图
六、工程源码
6.1 音符选择模块代码
module freq_select
(
input wire clk ,//时钟信号
input wire rst_n,//复位信号
output reg flag//pwm标志
);
parameter CNT_MAX = 24'd14_999_999;//300ms
parameter NUM_FRE = 6'd33 ;//34个音符
parameter DO = 16'd47750 ;//1
parameter RE = 16'd42250 ;//2
parameter MI = 16'd37900 ;//3
parameter FA = 16'd37550 ;//4
parameter SO = 16'd31850 ;//5
parameter LA = 16'd28400 ;//6
parameter XI = 16'd25400 ;//7
reg [23:0] cnt_delay ;//300ms计数器
reg [5:0] lut_data ;//乐谱数据寄存器
reg [15:0] cnt_freq ;//音符音频计数器
reg [15:0] freq_data ;//音符数据寄存器
wire [14:0] duty_data ;//占空比数据
wire end_note ;//音符结束标志
wire end_spectrum;//音谱结束标志
//单个音符持续时间计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay <= 24'd0;
end
else if(cnt_delay == CNT_MAX)begin
cnt_delay <= 24'd0;
end
else begin
cnt_delay <= cnt_delay + 1'd1;
end
end
//音符计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_freq <= 16'd0;
end
else if(end_note)begin
cnt_freq <= 16'd0;
end
else begin
cnt_freq <= cnt_freq + 1'd1;
end
end
//音谱计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
lut_data <= 6'd0;
end
else if(end_spectrum)begin
lut_data <= 6'd0;
end
else if(cnt_delay == CNT_MAX)begin
lut_data <= lut_data + 1'd1;
end
else begin
lut_data <= lut_data;
end
end
//音符查找表模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
freq_data <= DO;
end
else begin
case(lut_data)
6'd0: freq_data <= DO;
6'd1: freq_data <= RE;
6'd2: freq_data <= MI;
6'd3: freq_data <= DO;
6'd4: freq_data <= DO;
6'd5: freq_data <= RE;
6'd6: freq_data <= MI;
6'd7: freq_data <= DO;
6'd8: freq_data <= MI;
6'd9: freq_data <= FA;
6'd10: freq_data <= SO;
6'd11: freq_data <= MI;
6'd12: freq_data <= FA;
6'd13: freq_data <= SO;
6'd14: freq_data <= SO;
6'd15: freq_data <= LA;
6'd16: freq_data <= SO;
6'd17: freq_data <= FA;
6'd18: freq_data <= MI;
6'd19: freq_data <= DO;
6'd20: freq_data <= SO;
6'd21: freq_data <= LA;
6'd22: freq_data <= SO;
6'd23: freq_data <= FA;
6'd24: freq_data <= MI;
6'd25: freq_data <= DO;
6'd26: freq_data <= RE;
6'd27: freq_data <= SO;
6'd28: freq_data <= DO;
6'd29: freq_data <= DO;
6'd30: freq_data <= RE;
6'd31: freq_data <= SO;
6'd32: freq_data <= DO;
6'd33: freq_data <= DO;
default:freq_data <= DO;
endcase
end
end
assign duty_data = freq_data >> 1;//占空比50%
assign end_note = cnt_freq == freq_data;
assign end_spectrum = lut_data == NUM_FRE && cnt_delay == CNT_MAX;
//pwm信号产生模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
end
else begin
flag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;
end
end
endmodule
6.2 pwm产生模块代码
module gen_pwm
(
input wire clk ,//时钟
input wire rst_n,//复位信号
input wire flag ,//pwm标志信号
output reg beep//蜂鸣器信号
);
//pwm控制蜂鸣器模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
beep <= 1'b1;
end
else if(flag)begin
beep <= 1'b0;
end
else begin
beep <= 1'b1;
end
end
endmodule
6.3 顶层模块代码
module pwm_beep(
input wire clk ,
input wire rst_n,
output wire beep
);
parameter CNT_MAX = 24'd14_999_999;//300ms
parameter DO = 16'd47750 ;//1
parameter RE = 16'd42250 ;//2
parameter MI = 16'd37900 ;//3
parameter FA = 16'd37550 ;//4
parameter SO = 16'd31850 ;//5
parameter LA = 16'd28400 ;//6
parameter XI = 16'd25400 ;//7
wire flag;
//实例化音频选择模块
freq_select#(
.CNT_MAX (CNT_MAX),
.DO (DO) ,
.RE (RE) ,
.MI (MI) ,
.FA (FA) ,
.SO (SO) ,
.LA (LA) ,
.XI (XI)
) u_freq_select(
.clk (clk) ,
.rst_n (rst_n),
.flag (flag)
);
//实例化pwm产生模块
gen_pwm u_gen_pwm
(
.clk (clk) ,
.rst_n (rst_n),
.flag (flag) ,
.beep (beep)
);
endmodule
七、仿真测试
7.1 测试代码
`timescale 1ns/1ns
module pwd_beep_tb();
parameter CNT_MAX = 24'd21;//一个音符持续时间
parameter DO = 16'd7;//1
parameter RE = 16'd6;//2
parameter MI = 16'd5;//3
parameter FA = 16'd4;//4
parameter SO = 16'd3;//5
parameter LA = 16'd2;//6
parameter XI = 16'd1;//7
parameter CYCLE = 20;
reg clk ;
reg rst_n;
wire beep ;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0 ;
rst_n = 1'b0 ;
#(CYCLE) ;
rst_n = 1'b1 ;
#(7*CYCLE*CNT_MAX*34);
$stop ;
end
pwm_beep#(
.CNT_MAX (CNT_MAX),
.DO (DO) ,
.RE (RE) ,
.MI (MI) ,
.FA (FA) ,
.SO (SO) ,
.LA (LA) ,
.XI (XI)
) u_pwm_beep(
.clk (clk) ,
.rst_n (rst_n),
.beep (beep)
);
endmodule
7.2 仿真结果
八、管脚信息
元件 | 管脚 |
---|---|
KEY1 | E15 |
KEY2 | E16 |
KEY3 | M16 |
KEY4 | M15 |
CLOCK(时钟) | E1 |
BUZZER(蜂鸣器) | J1 |
九、运行效果
蜂鸣器播放两只老虎文章来源:https://www.toymoban.com/news/detail-400664.html
总结
以上就是本期蜂鸣器播放两只老虎的主要内容,通过本次的学习,同学们下去可以使用蜂鸣器播放其他音乐,只要你手里面有乐谱,原理上是可以编写任何音乐。赶紧行动起来,成为一个会写代码的音乐家吧!谢谢你的观看。文章来源地址https://www.toymoban.com/news/detail-400664.html
到了这里,关于蜂鸣器播放《两只老虎》的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!