FPGA入门学习——无源蜂鸣器

无源蜂鸣器

理论学习

蜂鸣器按其结构可分为电磁式蜂鸣器和压电式蜂鸣器两种类型。电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳组成。压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器以及共鸣箱、外壳等组成。压电式蜂鸣器是以压电陶瓷的压电效应,来带动金属片的振动而发声;而电磁式蜂鸣器则是用电磁的原理,通电时将金属振动 膜吸下,不通电时以振动膜的弹力弹回。由于两种蜂鸣器发声原理不同,电压式结构简单耐用但音调单一、音色差,适用于报警器等设备;而电磁式由于音色好,所以多用于语音、音乐等设备。

蜂鸣器按其是否带有信号源又分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器的内部装有集成电路,不需要音频驱动电路,只需要接通直流电源就能直接发出声响。而无源蜂鸣器只有外加音频驱动信号才能发出声响。

无源蜂鸣器驱动原理

无源蜂鸣器与有缘蜂鸣器不同,因其内部不带震荡源,所以其无法向有缘蜂鸣器那样直接用直流信号驱动,这里需要使用PWM方波才能驱动其发声。

如何发出不同的声音呢?上面说到需要使用PWM方波才能驱动其发声,所以这里我们只要控制输入的PWM方波,输入不同的PWM方波发出的声音就不一样了。而不同频率和占空比的方波发出的声音是不同的,其中频率对音调有影响,占空比对音量大小有影响。所以我们只需产生不同频率和占空比的PWM方波去驱动无源蜂鸣器就能让 无源蜂鸣器发出不同的音调了。

实战演练

本次实验我们驱动无源蜂鸣器进行七个基本音调“哆来咪发梭拉西”的循环鸣叫,每个音阶持续鸣叫0.5s后鸣叫下一个音阶。

程序框图

从实验目标我们知道,我们需鸣叫七个音调,而音调是受频率影响的,不同的频率产生的音调即不同,所以这里我们需产生七个不同的频率以发出七个音调,而占空比主要是对音调的音量有影响,这里占空比我们保持为50%即可。
在这里插入图片描述

波形图绘制

在这里插入图片描述
cnt:每个鸣叫保持0.5s,用来计数
cnt_500ms:当cnt计数到最大值时,cnt_500ms加1,用来控制蜂鸣器鸣叫状态
在这里插入图片描述
freq_cnt:音调计数器,用来控制每个鸣叫的频率

freq_data:音调分频计数值,该信号用于定义各音调频率的计数值,当我们需要鸣叫什么频率的值时,我们就让该信号的值定义为该频率的计数值。

duty_data:占空比计数值。通过改变该值可改变PWM波的占空比。

beep:输出控制蜂鸣器信号,该信号即为PWM方波。当我们需要输出频率为262时,我们需要产生波形的一个方波时间为190840个系统时钟,而高电平的持续时间即为95420,,所以如上波形图所示,当音阶计数器(freq_cnt)中的值大于等于占空比计数值时,我们将beep信号输出高电平,小于时输出低电平,这样我们输出的信号即为频率为262,占空比为50%的PWM波了,我们将该信号接入到驱动蜂鸣器的I/O引脚即能驱动蜂鸣器发出“Do”的音调。

代码编写

module beep
#(
	parameter CNT_MAX = 25'd24_999_999,
	parameter DO = 18'd190839 , //"哆"音调分频计数值(频率262)
	parameter RE = 18'd170067 , //"来"音调分频计数值(频率294)
	parameter MI = 18'd151514 , //"咪"音调分频计数值(频率330)
	parameter FA = 18'd143265 , //"发"音调分频计数值(频率349)
	parameter SO = 18'd127550 , //"梭"音调分频计数值(频率392)
	parameter LA = 18'd113635 , //"拉"音调分频计数值(频率440)
	parameter XI = 18'd101214   //"西"音调分频计数值(频率494)
)
(
	input clk,reset,
	output reg beep
);

reg[24:0] cnt;
reg[2:0] cnt_500ms;
reg[17:0] freq_cnt;
reg[17:0] freq_data;
wire[16:0] duty_data;

// cnt赋值
always @(posedge clk or negedge reset)
	if(reset == 1'b0)
		cnt <= 25'd0;
	else if(cnt == CNT_MAX)
		cnt <= 25'd0;
	else
		cnt <= cnt + 25'd1;

// cnt_500ms赋值
always @(posedge clk or negedge reset)
	if(reset == 1'b0)
		cnt_500ms <= 3'd0;
	else if(cnt == CNT_MAX && cnt_500ms == 3'd6)
		cnt_500ms <= 3'd0;
	else if(cnt == CNT_MAX)
		cnt_500ms <= cnt_500ms + 3'd1;
	else
		cnt_500ms <= cnt_500ms;
		
//freq_cnt赋值
always @(posedge clk or negedge reset)
	if(reset == 1'b0)
		freq_cnt <= 18'd0;
	else if(freq_cnt == freq_data || cnt == CNT_MAX)
		freq_cnt <= 18'd0;
	else
		freq_cnt <= freq_cnt + 18'd1;

//freq_data赋值
always @(posedge clk or negedge reset)
	if(reset == 1'b0)
		freq_data <= DO;
	else
		case(cnt_500ms)
			3'd0:freq_data <= DO;
			3'd1:freq_data <= RE;
			3'd2:freq_data <= MI;
			3'd3:freq_data <= FA;
			3'd4:freq_data <= SO;
			3'd5:freq_data <= LA;
			3'd6:freq_data <= XI;
			default:freq_data <= DO;
		endcase

assign duty_data = freq_data >> 1;

// 输出信号
always @(posedge clk or negedge reset)
	if(reset == 1'b0)
		beep <= 1'b0;
	else if(freq_cnt >= duty_data)
		beep <= 1'b1;
	else
		beep <= 1'b0;

endmodule

仿真测试

`timescale 1 ns/ 1 ns
module beep_vlg_tst();

// test vector input registers
reg clk;
reg reset;
// wires                                               
wire beep;
                     
beep 
#(
	.CNT_MAX(25'd24_999_99),
	.DO(18'd19083) , 
	.RE(18'd17006) , 
	.MI(18'd15151) , 
	.FA(18'd14326) , 
	.SO(18'd12755) , 
	.LA(18'd11363) , 
	.XI(18'd10121)
)
i1 (
	.beep(beep),
	.clk(clk),
	.reset(reset)
);
initial                                                
begin                                                  
	clk = 1'b1;
	reset <= 1'b0;
	#20
	reset <= 1'b1;
end                                                    
always #10 clk = ~clk;                                                
                                                                
endmodule


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值