无源蜂鸣器
理论学习
蜂鸣器按其结构可分为电磁式蜂鸣器和压电式蜂鸣器两种类型。电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳组成。压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器以及共鸣箱、外壳等组成。压电式蜂鸣器是以压电陶瓷的压电效应,来带动金属片的振动而发声;而电磁式蜂鸣器则是用电磁的原理,通电时将金属振动 膜吸下,不通电时以振动膜的弹力弹回。由于两种蜂鸣器发声原理不同,电压式结构简单耐用但音调单一、音色差,适用于报警器等设备;而电磁式由于音色好,所以多用于语音、音乐等设备。
蜂鸣器按其是否带有信号源又分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器的内部装有集成电路,不需要音频驱动电路,只需要接通直流电源就能直接发出声响。而无源蜂鸣器只有外加音频驱动信号才能发出声响。
无源蜂鸣器驱动原理
无源蜂鸣器与有缘蜂鸣器不同,因其内部不带震荡源,所以其无法向有缘蜂鸣器那样直接用直流信号驱动,这里需要使用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