蜂鸣器播放《两只老虎》


前言

  通过往期的按键控制蜂鸣器课程,我们了解了蜂鸣器器件,本次课程将使用蜂鸣器,播放我小时候经常听的《两只老虎》音乐,来勾起我童年的回忆。


一、回顾蜂鸣器

  我们回顾一下蜂鸣器的知识:

  1. 按照工作原理可分为:压电式蜂鸣器和电磁式蜂鸣器。
  2. 按照音源可分为:有源蜂鸣器和无源蜂鸣器。
  3. 有源蜂鸣器:内部有振荡源,直接通以直流电即可发出声音。
  4. 无源蜂鸣器:内部无振荡源,需要通以方波、PWM信号才能发出声音。

在这里插入图片描述

图1. 蜂鸣器原理图

  无源蜂鸣器需要输入一定频率的方波或者脉冲宽度调制(Pulse Width Modulation,PWM)信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音。《红楼梦》中“未见其人先闻其声”指的是王熙凤,就是因为每个人发出的声音频率不同,所以音色也会不同,我们可以通过音色就知道谁是谁。


二、乐谱知识

2.1 音符音频

  音频(Audio),指人耳可以听到的声音频率在20HZ~20kHz之间的声波。乐普是由音符组成的,不同的音符拥有不同的频率。音频和周期的关系如下公式所示。
T ( 周 期 ) = 1 f ( 频 率 ) (1) T(周期) = \frac{1}{f(频率)}\tag1 T()=f()1(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毫秒,要想使两只老虎听起来更完美,同学们下去得学习乐谱中节拍知识,根据乐谱中节拍设置音符的持续时间。

请添加图片描述

图2. 两只老虎乐谱

三、系统框图

在这里插入图片描述

图3. 系统框图

四、模块调用

在这里插入图片描述

图4. 模块关系示意图

五、模块原理图

在这里插入图片描述

图5. 模块原理图

六、工程源码

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 仿真结果

在这里插入图片描述

图6. 仿真结果

八、管脚信息

pin planner

图7. pin planner
元件管脚
KEY1E15
KEY2E16
KEY3M16
KEY4M15
CLOCK(时钟)E1
BUZZER(蜂鸣器)J1
表2. 管脚信息表

九、运行效果

蜂鸣器播放两只老虎


总结

  以上就是本期蜂鸣器播放两只老虎的主要内容,通过本次的学习,同学们下去可以使用蜂鸣器播放其他音乐,只要你手里面有乐谱,原理上是可以编写任何音乐。赶紧行动起来,成为一个会写代码的音乐家吧!谢谢你的观看。

  • 35
    点赞
  • 171
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值