驱动无源蜂鸣器

一、前言

         本实验使用的无源蜂鸣器只有外加音频驱动信号才能发出声响。只要控制输入的 PWM 方波,输入不同的 PWM 方波发出的声音就不一样了。而不同频率和占空比的方波发出的声音是不同的,其中频率对音调有影响,占空比对音量大小有影响。只需产生不同频率和占空比的 PWM 方波去驱动无源蜂鸣器就能让无源蜂鸣器发出不同的音调了。本文通过驱动无源蜂鸣器进行七个基本音调“哆来咪发梭拉西”的循环鸣叫,每个音阶持续鸣叫 0.5s 后鸣叫下一个音阶。

二、程序设计

        我们已经知道不同的频率产生的音调不同,所以这里我们需产生七个不同的频率以发出七个音调,而占空比主要是对音调的音量有影响,这里占空比我们保持为 50%即可。七个音调所对应的频率(Hz),如下表所示

音调1234567
频率262294330349392440494

        知道了不同音调对应的频率,我们如何通过程序去实现呢?以音调“Do”为例,该音调的频率为 262,所以这里我们需输出频率为 262Hz,占空比为 50%的 PWM 方波。先计算该频率单个方波的时间:1 / 262 ≈ 0.003816794s=3816794ns;程序设计使用的系统时钟(50MHz)的时间为:1 / 50000000 = 0.00000002s = 20ns;所以我们需用:3816794 / 20 ≈ 190840 个系统时钟去产生一个 PWM 波,该 PWM 波形的频率即为 262Hz。要想实现占空比为 50%的波形,即高电平时间所占信号周期的百分比为 50%。信号周期为 190840 的话,50%占空比的高电平持续时间即为信号周期的一半,即为 95420。以上就是“Do”音调的输出 PWM 波产生方式,其余音调的产生方式是一样的,下表是计算出7个音调对应的数值。

频率/HZ
音调分频计数值
占空比计数值

262

190840
95420
294
170068
85034
330
151515
75757
349
143266
71633
392
127551
63775
440
113636
56818
494
101214
50607

        话不多说直接上代码,程序参考野火FPGA有兴趣的同学可以购买开发板学习。

`timescale  1ns/1ns

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

仿真代码

`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'd2499),   //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

仿真波形

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

三、总结

        程序相对简单,主要是学习通过计数的形式设计程序从而控制无源蜂鸣器。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值