熊熊出没 发表于 2024-8-1 23:46:37

FPGA开辟——蜂鸣器实现音乐播放器的设计

一、概述

我们在举行蜂鸣器的学习的时间,总会在想既然蜂鸣器可以或许发作声音,那么它可以或许播放音乐吗,本日这篇我们文章我们就一起来学习怎样利用利用蜂鸣器来播放音乐,也就是怎样成为一个音乐播放器。
1、蜂鸣器的类型

在设计的时间其实蜂鸣器的类型也对我们末了所实现的效果有一点影响,蜂鸣器分为有源和无源两种,有源蜂鸣器的器件内部自己就带有振荡器,自己来讲完全不用我们举行外部振荡,二无源蜂鸣器是不带振荡器的,以是当涉及到干系频率利用时就必要我们利用外部振荡。这里因为我们利用PWM举行设计实现,以是如果有条件的话利用无源蜂鸣器所实现的效果会更好。
2、PWM介绍

PWM(Pulse Width Modulation),即脉冲宽度调制,是一种模拟信号电平数字编码方法。它通过将有用的电信号分散成离散形式,从而来降低电信号所传递的平均功率。PWM技能的核心在于通过调节脉冲的时间宽度(占空比)来等效地得到所必要合成的相应幅值和频率的波形。通过调整PWM信号的占空比和频率,可以实现声音的音量调节和音调变革。
   

[*]占空比:占空比是脉冲处于较高电压的时间占整个脉冲周期的百分比。通过改变占空比,可以实现对信号、能量等的调节。例如,在LED电路中,高占空比意味着LED非常豁亮,而低占空比则意味着LED较为暗淡。
[*]频率:频率是单位时间内脉冲信号的次数。PWM信号的频率越高,控制效果越靠近模拟信号,但也会受到电路相应时长的限制。
3、PWM调制的实现方式

硬件实现:许多微控制器和数字信号处置惩罚器(DSP)已包罗了PWM控制器芯片,因此可以更轻松地实施数字化控制。这些芯片通过内部定时器或计数器来生成PWM信号,并通过调整干系参数来改变占空比和频率。
软件实现:在没有内置PWM控制器的系统中,也可以通过软件编程来模拟PWM信号。这通常涉及到对定时器中断的准确控制,以及在高电平和低电平之间快速切换IO口的输出状态。
4、音符的干系设计
这是关于高中低音符的频率和周期表,我们将会参考这个对代码举行设计。
https://i-blog.csdnimg.cn/direct/1b5121cfd8604e229e3ffc271708483c.png
二、工程实现

1、本次播放的音乐

本次我们设计得比力简朴,设计了一个《我有一头小毛驴》的简朴音乐播放器,具体曲谱如下:
https://i-blog.csdnimg.cn/direct/adca6d7a922e44848ec32731ca67ecc0.png

2、根本构建思路

   在本次的设计中,采用了一个音符播放播放0.5秒的设计,针对上述曲谱,采用四个字符为一组的方式举行划分,便于代码的赋值。利用三个计数器完成对于音乐播放的干系设计,第一个播放器用于技能每个音符技能的干系频率,第二个计数器用于对于在0.5秒播放时间内对于每个音符重复的次数举行计数,而第三个计数器用于对音符的播放顺序举行一个计数,由此关于音符的设置就设计完成,末了就是对于蜂鸣器举行占空比调制,对每个音符的频率举行一个8分频就完成了最终功能。
3、设计文件的编写

这里新建一个beep.v文件,如下:
//蜂鸣器
module beep(
input         clk,
input         rst_n,
output    reg    beep_out
);
parameter CLK_CLY=50_000_000;//时钟频率
localparamH1=CLK_CLY/523,
            H1_L=CLK_CLY/262,
            H1_H=CLK_CLY/1047,
            H2=CLK_CLY/587,
            H3=CLK_CLY/659,
            H4=CLK_CLY/698,
            H5=CLK_CLY/784,
            H6=CLK_CLY/880,
            H7=CLK_CLY/988;


parameter TIME_500MS=25_000_000;//0.5秒

reg cnt0;// 每个音符频率计数器
wire       add_cnt0;
wire       end_cnt0;

reg cnt1;// 0.5秒内音符周期重复个数
wire       add_cnt1;
wire       end_cnt1;

reg cnt2;// 计数音符播放顺序
wire       add_cnt2;
wire       end_cnt2;
reg    cnt_max;

reg   display;//音符计数器的最大值
//cnt0计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
    cnt0<=0;
else if(add_cnt0)begin
    if(end_cnt0)
      cnt0<=0;
    else
      cnt0<=cnt0+1'b1;
end
end
assign add_cnt0=1'b1;
assign end_cnt0=add_cnt0 &&(cnt0==display-1);

//cnt1计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
    cnt1<=0;
else if(add_cnt1)begin
    if(end_cnt1)
      cnt1<=0;
    else
      cnt1<=cnt1+1'b1;
end
end
assign add_cnt1=end_cnt0;
assign end_cnt1=add_cnt1 &&(cnt1==(TIME_500MS/display-1));

//cnt2计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
    cnt2<=0;
else if(add_cnt2)begin
    if(end_cnt2)
      cnt2<=0;
    else
      cnt2<=cnt2+1'b1;
end
end
assign add_cnt2=end_cnt1;
assign end_cnt2=add_cnt2 &&(cnt2==50-1);

always @(posedge clk or negedge rst_n)begin
if(!rst_n)
    display <=H1;
else begin
    case (cnt2)
       0:display <=H1_L;
       1:display <=H1;
       2:display <=H1;
       3:display <=H3;

       4:display <=H5;
       5:display <=H5;
       6:display <=H5;
       7:display <=H5;
      
       8:display <=H6;
       9:display <=H6;
      10:display <=H6;
      11:display <=H1_H;

      12:display <=H5;
      13:display <=H5;
      14:display <=H5;
      15:display <=H5;

      16:display <=H4;
      17:display <=H4;
      18:display <=H4;
      19:display <=H6;

      20:display <=H3;
      21:display <=H3;
      22:display <=H3;
      23:display <=H3;

      24:display <=H2;
      25:display <=H2;
      26:display <=H2;
      27:display <=H2;

      28:display <=H5;
      29:display <=H5;
      30:display <=H5;
      31:display <=H5;

      32:display <=H1;
      33:display <=H1_L;
      34:display <=H1;
      35:display <=H3;

      36:display <=H5;
      37:display <=H5;
      38:display <=H5;
      39:display <=H5;

      40:display <=H6;
      41:display <=H6;
      42:display <=H6;
      43:display <=H1_H;

      44:display <=H5;
      45:display <=H5;
      46:display <=H5;
      47:display <=H5;

      40:display <=H4;
      41:display <=H4;
      42:display <=H4;
      43:display <=H6;

      44:display <=H3;
      45:display <=H3;
      46:display <=H3;
      47:display <=H3;
      40:display <=H3;
      41:display <=H3;

      42:display <=H2;
      43:display <=H2;
      44:display <=H2;
      45:display <=H3;

      46:display <=H1;
      47:display <=H1;
      48:display <=H1;
      49:display <=H1;
      default: display <=H1;
    endcase
end
end

//pwm输出
//蜂鸣器pwm
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
      beep_out <= 1'b1;
    end
    else if(cnt0 == (display>>3))begin//设置占空比
      beep_out <= 1'b1;
    end
    else if(cnt0==0)begin
      beep_out<= 1'b0;
    end
end
endmodule 4、测试文件的编写

新建beep_tb.v文件,这里出来蜂鸣器输出没有任何鼓励,以是我们只必要举行时钟和复位举行赋值就行。如下:
//定义时间尺度
`timescale 1ns/1ns
module beep_tb ;

//输入信号定义
reg          clk         ;
reg          rst_n         ;
wire         beep_out       ;
//模块例化
beep beep_inst(
/*input      */ .clk      (clk      )   ,
/*input      */ .rst_n      (rst_n    )   ,
/*output       */ .beep_out    (beep_out)   
);

//激励信号产生
parameter CLK_CYC = 20;
//时钟
initial clk=1;
always #(CLK_CYC/2)clk=~clk;

//复位
initial begin
    rst_n= 1'b0;
    #(CLK_CYC*2);
    #3;//复位结束避开时钟上升沿
    rst_n= 1'b1;
end
endmodule
5、仿真波形图

https://i-blog.csdnimg.cn/direct/7c791cc5fffc44bba6cd81ccbba1d9c1.png
通过对于波形图的观察,我们可以看到在三个计数器的不断计数之下蜂鸣器的输出波形占空比在不断举行变革 ,并且在经过对于计数器举行查抄后没有发现什么问题。(这里方针波形图太长,没有截全),感兴趣的可以去自行举行仿真观察。末了举行下板验证之后,蜂鸣器也举行了正常播放,我们的设计成功。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: FPGA开辟——蜂鸣器实现音乐播放器的设计