18. 无源蜂鸣器驱动实验

蜂鸣器是一种一体化结构的电子讯响器,因其体积小、重量轻、价格低、结构牢靠而广泛应用于报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。本章节将详细介绍如何使用按键去控制蜂鸣器发声,相信大家通过本章的学习将会对蜂鸣器以及生活中带有蜂鸣器的电子器件的发声原理会有更深层次的了解。

18.1. 理论学习

18.1.1. 蜂鸣器简介

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

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

beep002

图 24‑1 蜂鸣器(左为有源蜂鸣器,右为无源蜂鸣器)

表格 24‑1 有源和无源蜂鸣器的区别

区别

有源蜂鸣器

无源蜂鸣器

外形高度

引脚部位

黑胶封闭

绿色电路板

电压

响声

连续发声

响一次停一次

由图 24‑1和表格 24‑1所示,我们能比较清晰的认识到有源蜂鸣器和无源蜂鸣器的差别,同时能具有判断其为有源或是无源蜂鸣器的能力。

相对于有源蜂鸣器,无源蜂鸣器的成本更低,声音频率可控。而有源蜂鸣器因其内部自带振荡源,只要加上适当的直流电源即可发声,程序控制较为方便。我们征途FPGA开发板上使用的是无源蜂鸣器,所以这里为大家介绍无源蜂鸣器的驱动方式。

18.1.2. 无源蜂鸣器驱动原理

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

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

beep003

图 24‑2 频率、占空比示意图

如图 24‑2为不同频率,不同占空比的PWM波形图。从图中可以看到,不同频率的波形其单位波形所占时间不同;不同占空比的波形其高电平在单位波形的的占用比例不同。所以我们以此为根据,就能很容易的产生不同频率以及占空比的PWM波了,具体的产生方法将在实战演练为大家讲解。

18.2. 实战演练

在上一小节中,我们对蜂鸣器以及如何驱动无源蜂鸣器做了相关的介绍,这一小节我们将从模块设计、绘制波形图、逻辑关系、代码编写、仿真以及测试一步一步的完成这个实验。

18.2.1. 实验目标

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

18.2.2. 硬件资源

我们使用开发板上的无源蜂鸣器去进行该实验的验证,如图 24‑3所示。

beep004

图 24‑3 硬件资源

由原理图可知,我们只要在BEEP端输入PWM波即可驱动无源蜂鸣器进行鸣叫了。如图 24‑4所示。

beep005

图 24‑4 无源蜂鸣器原理图

18.2.3. 程序设计

硬件资源介绍完毕,我们开始实验工程的程序设计。在本小节,我们将从模块框图,波形图设计等进行讲解。

18.2.3.1. 模块框图

从实验目标我们知道,我们需鸣叫七个音调,而音调是受频率影响的,不同的频率产生的音调即不同,所以这里我们需产生七个不同的频率以发出七个音调,而占空比主要是对音调的音量有影响,这里占空比我们保持为50%即可。七个音调所对应的频率,如表格 24‑2所示。

表格 24‑2 七个基本音调对应频率表

音调

1(Do)

2(Re)

3(Mi)

4(Fa)

5(So)

6(La)

7(Si)

频率

262

294

330

349

392

440

494

所以该实验我们只要循环产生占空比为50%的七个音调频率即可,这里我们使用一个模块即可实现,模块框图如图 24‑5所示。

beep006

图 24‑5 蜂鸣器模块框图

在输入端我们只需要接入时钟和复位即可,输出端我们输出命名为beep的PWM波进行对无源蜂鸣器的驱动,模块各信号描述如表格 24‑3所示。

表格 24‑3 蜂鸣器输入输出信号描述

信号

位宽

类型

功能描述

sys_clk

1bit

Input

工作时钟,频率50MHz

sys_rst_n

1bit

Input

复位信号,低电平有效

beep

1bit

Output

输出控制蜂鸣器

18.2.3.2. 波形图绘制

通过前面讲解我们知道,我们需要循环产生七个不同的频率,所以这里我们使用一个计数器对不同频率进行计数,每个频率计一个值。这样我们就能根据计数值来判断我们该产生什么频率的波形了。波形图如图 24‑6所示:

beep007

图 24‑6 蜂鸣器鸣叫波形图设计

由图 24‑6所示:

cnt_500ms:该信号我们定义为蜂鸣器的鸣叫状态计数。由于我们需鸣叫七个音调,所以我们需要计7个数(0~6),而每个音调的鸣叫时间即计数值的持续时间。所以这里我们需要用一个鸣叫持续时间计数器去控制蜂鸣器各音调的鸣叫持续时间。

cnt:蜂鸣器各音调鸣叫持续时间计数器。在“计数器”章节我们详细的介绍了该如何用计数器进行时间计数,这里就不详细的介绍了。本次实验我们设计让每个音调持续鸣叫0.5s,故这里我们计数到24999999(0.5s)时让鸣叫状态计数器加1。当最后一个音调(cnt_500ms = 3’d6)鸣叫了0.5s时,我们让状态计数器跳转回第一个音调鸣叫状态(cnt_500ms = 3’d0),以此循环,我们就能实现蜂鸣器七个基本音调的循环鸣叫了。

上面我们讲述了蜂鸣器七个音调的循环鸣叫控制,那每一个音调是如何控制他发出我们所需要的的声音的呢?下面根据波形图为大家详细讲解,如图 24‑7所示:

beep008

图 24‑7 “Do”音调PWM波产生波形图

由实验目标可知,刚开始我们是鸣叫音调“Do”,该音调的频率为262,所以这里我们需输出频率为262,占空比为50%的PWM方波。

首先是音调的频率我们该如何产生?我们先计算该频率单个方波的时间:1 / 262 ≈ 0.003816794s=3816794ns;而我们单个系统时钟(50MHz)的时间为:1 / 50000000 = 0.00000002s = 20ns;所以我们需用:3816794 / 20 ≈ 190840个系统时钟去产生一个PWM波,该PWM波形的频率即为262。故我们需先对190840个系统时钟进行计数,这里我们声明freq_cnt信号进行计数。

freq_cnt:音调计数器。我们使用该计数器进行计数,当要鸣叫什么频率的音调我们就让计数器计到该音调所对应的音调分频计数器值即可。这里计数0~190839即为190840个系统时钟,该计数范围内即为一个262频率波形。

freq_data:音调分频计数值。该信号我们用于定义各音调频率的计数值,当我们需要鸣叫什么频率的值时,我们就让该信号的值定义为该频率的计数值。如上图所示我们此时需要输出的PWM波形频率为262,按上面所述的计算方法,计数值即为190839。

duty_data:占空比计数值。通过改变该值我们可改变PWM波的占空比。前面说到我们是产生占空比为50%的波形,即高电平时间所占信号周期的百分比为50%。信号周期为190840话,50%占空比的高电平持续时间即为信号周期的一半,即为95420。

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

以上就是“Do”音调的输出PWM波产生方式,其余音调的产生方式是一样的,我们只要改变freq_data与duty_data的值就能产生不同频率的PWM波了,本次实验所需产生的频率相对应的freq_data和duty_data的值如表格 24‑4所示:

表格 24‑4 频率对应表

频率/Hz

音调分频计数值(freq_data)

占空比计数值(duty_data)

262

190840

95420

294

170068

85034

330

151515

75757

349

143266

71633

392

127551

63775

440

113636

56818

494

101214

50607

我们根据上面的频率对照表我们可画出“Re”音调的波形图,如图 24‑8所示:

beep009

图 24‑8 “Re”音调PWM波产生波形图

如上图所示,我们只需改变“freq_data”与“duty_data”的值,就能产生相应的频率的PWM波形,后面音调的波形产生也是如此,这里就不再一一讲解了,下面我们看看代码需如何实现的吧。

18.2.3.3. 代码编写

当波形图绘制完成之后,参照绘制波形图的时序逻辑关系,我们可以较快的编写完模块代码。各模块的代码如下所示:

代码清单 24‑1 无源蜂鸣器驱动模块(beep.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
module beep
#(
parameter TIME_500MS = 25'd24999999, //0.5s计数值
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 wire sys_clk , //系统时钟,频率50MHz
 input wire sys_rst_n , //系统复位,低有效

 output reg beep //输出蜂鸣器控制信号
 );

 ////
 //\* Parameter and Internal Signal \//
 ////

 //reg define
 reg [24:0] cnt ; //0.5s计数器
 reg [17:0] freq_cnt ; //音调计数器
 reg [2:0] cnt_500ms ; //0.5s个数计数
 reg [17:0] freq_data ; //音调分频计数值

 //wire define
 wire [16:0] duty_data ; //占空比计数值

 ////
 //\* Main Code \//
 ////

 //设置50%占空比:音阶分频计数值的一半即为占空比的高电平数
 assign duty_data = freq_data >> 1'b1;

 //cnt:0.5s循环计数器
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt <= 25'd0;
 else if(cnt == TIME_500MS )
 cnt <= 25'd0;
 else
 cnt <= cnt + 1'b1;

 //cnt_500ms:对500ms个数进行计数,每个音阶鸣叫时间0.5s,7个音节一循环
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_500ms <= 3'd0;
 else if(cnt == TIME_500MS && cnt_500ms == 6)
 cnt_500ms <= 3'd0;
 else if(cnt == TIME_500MS)
 cnt_500ms <= cnt_500ms + 1'b1;

 //不同时间鸣叫不同的音阶
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 freq_data <= DO;
 else case(cnt_500ms)
 0: freq_data <= DO;
 1: freq_data <= RE;
 2: freq_data <= MI;
 3: freq_data <= FA;
 4: freq_data <= SO;
 5: freq_data <= LA;
 6: freq_data <= XI;
 default: freq_data <= DO;
 endcase

 //freq_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 freq_cnt <= 18'd0;
 else if(freq_cnt == freq_data \|\| cnt == TIME_500MS)
 freq_cnt <= 18'd0;
 else
 freq_cnt <= freq_cnt + 1'b1;

 //beep:输出蜂鸣器波形
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 beep <= 1'b0;
 else if(freq_cnt >= duty_data)
 beep <= 1'b1;
 else
 beep <= 1'b0;

 endmodule

代码是根据波形图设计进行编写的,这里我们只对部分代码做个简单的说明。

代码4~10行定义了各音调分频的计数值。

代码38行是对占空比计数值(duty_data)的赋值,前面我们说到实现的是50%的占空比,所以数值是freq_data的一半,这里我们将freq_data向右移动一位以实现freq_data/2的计算

18.2.3.4. 仿真验证

仿真代码编写

当我们代码都写完了之后,我们可以编写仿真代码,对其进行仿真,可以验证我们的代码是否能实现我们的功能。

代码清单 24‑2 无源蜂鸣器驱动仿真文件(tb_beep.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
\`timescale 1ns/1ns
module tb_beep();

////
//\* Parameter and Internal Signal \//
////

//reg define
reg sys_clk ; //时钟
 reg sys_rst_n ; //复位

 ////
 //\* Main Code \//
 ////

 //对时钟,复位信号赋初值
 initial
 begin
 sys_clk = 1'b1;
 sys_rst_n <= 1'b0;
 #100
 sys_rst_n <= 1'b1;
 end

 //产生时钟信号
 always #10 sys_clk = ~sys_clk;

 ////
 //\* Instantiation \//
 ////

 beep
 #(
 .TIME_500MS(25'd24999), //0.5s计数值
 .DO (18'd190 ), //"哆"音调分频计数值(频率262)
 .RE (18'd170 ), //"来"音调分频计数值(频率294)
 .MI (18'd151 ), //"咪"音调分频计数值(频率330)
 .FA (18'd143 ), //"发"音调分频计数值(频率349)
 .SO (18'd127 ), //"梭"音调分频计数值(频率392)
 .LA (18'd113 ), //"拉"音调分频计数值(频率440)
 .XI (18'd101 ) //"西"音调分频计数值(频率494)
 )
 beep_inst
 (
 .sys_clk (sys_clk ), //系统时钟,频率50MHz
 .sys_rst_n (sys_rst_n ), //系统复位,低有效

 .beep (beep ) //输出蜂鸣器控制信号
 );

 endmodule

仿真文件第34~41行,我们对各参数重新定义了数值,这样可缩短仿真时间。

仿真波形分析

配置好仿真文件,使用ModelSim对参考代码进行仿真,仿真结果如下。

beep010

图 24‑9 无源蜂鸣器仿真波形图

如图 24‑9所示,我们抓取的是“Do”音调和“Re”音调的波形图,由于我们仿真时把各参数缩小了约1000倍,所以其单位波形的时间也缩小了约1000倍,而前面说到“Do”音调的单位方波持续时间为3816794ns,这里将时间缩小1000倍为3816ns,与上述抓取的单位波形持续时间几乎一致,说明我们 的设计正确。

18.3. 上板调试

18.3.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 24‑5所示。

表格 24‑5 引脚分配表

信号名

信号类型

对应引脚

备注

sys_clk

input

E1

时钟

sys_rst_n

input

M15

复位

beep

output

J11

蜂鸣器

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 24‑10所示。

beep011

图 24‑10 管脚分配

18.3.1.1. 结果验证

管脚配置完成之后重新进行编译,编译完之后就可以进行下载验证了,在下载之前首先将电源与下载线与开发板连接好,连接好之后上电,如图 24‑11所示。

beep012

图 24‑11 下载连线图

打开下载界面后,当检测到下载器(USB-Blaster)已连接之后,即可点击“Add File…”添加sof文件,添加好后点击“start”开始下载,随后界面会显示下载成功,如图 24‑12所示。

beep013

图 24‑12 下载成功界面

下载成功后蜂鸣器会循环鸣叫“哆来咪发梭拉西”音调。

18.4. 章末总结

到这里,本章节讲解完毕,通过本章节的学习,相信读者对于蜂鸣器以及控制无源蜂鸣器都能较为熟练的掌握。

18.5. 拓展训练

实验要求:我们加个按键控制模块,用按键进行对无源蜂鸣器的鸣叫状态。