回答

收藏

[评测分享] 【PGL22G开发板】+使用蜂鸣器播放音乐

#板卡评测 #板卡评测 2407 人阅读 | 0 人回复 | 2023-04-22

本帖最后由 feifeiz 于 2023-4-28 23:23 编辑

一、       前言
继上次拿到板子后,对板子的资源情况进行了了解,但是没有进行环境搭建,这段时间中,每天学一点,将环境搭建好了,买了一个紫光同创的专用下载器,以及对licence进行了关联。完成后基本准备,后面也进行了基本的使用,本次使用开发板进蜂鸣器实现,旨在进一步熟悉环境,以及对FPGA编程的熟悉,本次原打算使用板载的蜂鸣器进行歌曲的播放,但是查看原理图,厂家使用的是有源的蜂鸣器,于是自己买了一个无源的蜂鸣器进行试验。
二、       知识准备
本次使用蜂鸣器进行歌曲的播放,需要有一定的音乐知识,然后了解1,2,3,4,5,6,7每个音符的实现,首先无源蜂鸣器是指没有震荡源,如果用直流信号无法令其鸣叫。必须用PWM的方波去驱动它,而不同的脉冲频率会使蜂鸣器有不同的响声。
如下表为每个音符对应的频率及半周期

以下是在网上买的蜂鸣器模块,低电平触发,只需要个I/O给不同的脉冲频率就可以发出不同的响声。

虽然现在有了硬件和每个音符的频率,当输出一个周期的脉冲给蜂鸣器可以听到响声,但是持续时间太短,可能都听不清,那么就需要将每个音符发出的响声进行延长,才能听清。
如前面的表格,得到每个音符的周期数,在对每个音符进行多次触发进行延长,如250次。就可以得到每个音符延长后的时钟周期数。通过总的时钟周期数除以每个音符周期时序数就可以得到每个音符播放的次数。
实现原理知道了,接下来就是播放的歌曲,本次播放的是小星星和两只小老虎这种简单的儿歌,通过按键进行歌曲间的切换。
小星星歌谱如下:

两只小老虎歌谱:

三、       硬件设计与连接
本次使用的是买的蜂鸣器模块,接到的是开发板的F4引脚,实物连接如下


对应引脚连接:

  
信号名
  
方向
管脚
端口说明
  
sys_clk
  
inpu
G5
系统时钟
  
sys_rst_n
  
input
G6
复位按键
  
key
  
input
F2
按键,key0
  
buzzer
  
output
F4
蜂鸣器

四、       程序设计
程序如下:
module pwm_buzzer(
input clk , //时钟输入
input rst_n , //复位按键输入
input key_in, //按键输入

output reg buzzer //驱动蜂鸣器
);
wire press ; //线,连接按键标志信号

//定义音符时序周期数
localparam M0 = 98800,
M1 = 95600,
M2 = 85150,
M3 = 75850,
M4 = 71600,
M5 = 63750,
M6 = 56800,
M7 = 50600;
//信号定义
reg [16:0] cnt0 ; //计数每个音符对应的时序周期
reg [10:0] cnt1 ; //计数每个音符重复次数
reg [5 :0] cnt2 ; //计数曲谱中音符个数
reg [16:0] pre_set ; //预装载值
wire [16:0] pre_div ; //占空比
reg [10:0] cishu ; //定义不同音符重复不同次数
wire [10:0] cishu_div ; //音符重复次数占空比
reg flag ; //歌曲种类标志:0小星星,1两只老虎
reg [5 :0] YINFU ; //定义曲谱中音符个数\


always @(posedge clk or negedge rst_n)begin

end
//歌曲种类标志位
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
flag <= 1'b0;
else if(key_in ==0)
flag <= ~flag;
end
//重设音符的个数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
YINFU <= 48;
else if(flag == 1'b1)
YINFU <= 36;
else
YINFU <= 48;
end
//计数每个音符的周期,也就是表示音符的一个周期
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt0 <= 0;
end
else if(press)
cnt0 <= 0;
else begin
if(cnt0 == pre_set - 1)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
//计数每个音符重复次数,也就是表示一个音符的响鸣持续时长
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt1 <= 0;
end
else if(press)
cnt1 <= 0;
else begin
if(cnt0 == pre_set - 1)begin
if(cnt1 == cishu)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
end
//计数有多少个音符,也就是曲谱中有共多少个音符
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt2 <= 0;
end
else if(press)
cnt2 <= 0;
else begin
if(cnt1 == cishu && cnt0 == pre_set - 1) begin
if(cnt2 == YINFU - 1) begin
cnt2 <= 0;
end
else
cnt2 <= cnt2 + 1;
end
end
end
//定义音符重复次数
always @(*) begin
case(pre_set)
M0:cishu = 242;
M1:cishu = 250;
M2:cishu = 281;
M3:cishu = 315;
M4:cishu = 334;
M5:cishu = 375;
M6:cishu = 421;
M7:cishu = 472;
endcase
end
//曲谱定义
always @(*) begin
if(flag == 1'b0) begin
case(cnt2) //小星星歌谱
0 : pre_set = M1;
1 : pre_set = M1;
2 : pre_set = M5;
3 : pre_set = M5;
4 : pre_set = M6;
5 : pre_set = M6;
6 : pre_set = M5;
7 : pre_set = M0;
8 : pre_set = M4;
9 : pre_set = M4;
10: pre_set = M3;
11: pre_set = M3;
12: pre_set = M2;
13: pre_set = M2;
14: pre_set = M1;
15: pre_set = M0;
16: pre_set = M5;
17: pre_set = M5;
18: pre_set = M4;
19: pre_set = M4;
20: pre_set = M3;
21: pre_set = M3;
22: pre_set = M2;
23: pre_set = M0;
24: pre_set = M5;
25: pre_set = M5;
26: pre_set = M4;
27: pre_set = M4;
28: pre_set = M3;
29: pre_set = M3;
30: pre_set = M2;
31: pre_set = M0;
32: pre_set = M1;
33: pre_set = M1;
34: pre_set = M5;
35: pre_set = M5;
36: pre_set = M6;
37: pre_set = M6;
38: pre_set = M5;
39: pre_set = M0;
40: pre_set = M4;
41: pre_set = M4;
42: pre_set = M3;
43: pre_set = M3;
44: pre_set = M2;
45: pre_set = M2;
46: pre_set = M1;
47: pre_set = M0;
endcase
end
else begin
case(cnt2) //两只老虎歌谱
0 : pre_set = M1;
1 : pre_set = M2;
2 : pre_set = M3;
3 : pre_set = M1;
4 : pre_set = M1;
5 : pre_set = M2;
6 : pre_set = M3;
7 : pre_set = M1;
8 : pre_set = M3;
9 : pre_set = M4;
10: pre_set = M5;
11: pre_set = M0;
12: pre_set = M3;
13: pre_set = M4;
14: pre_set = M5;
15: pre_set = M0;
16: pre_set = M5;
17: pre_set = M6;
18: pre_set = M5;
19: pre_set = M4;
20: pre_set = M3;
21: pre_set = M1;
22: pre_set = M5;
23: pre_set = M6;
24: pre_set = M5;
25: pre_set = M4;
26: pre_set = M3;
27: pre_set = M1;
28: pre_set = M2;
29: pre_set = M5;
30: pre_set = M1;
31: pre_set = M0;
32: pre_set = M2;
33: pre_set = M5;
34: pre_set = M1;
35: pre_set = M0;
endcase
end
end
assign pre_div = pre_set >> 1; //除以2
assign cishu_div = cishu * 4 / 5;
//向蜂鸣器输出脉冲
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
buzzer <= 1'b1;
end
else if(pre_set != M0) begin
if(cnt1 < cishu_div) begin
if(cnt0 < pre_div) begin
buzzer <= 1'b1;
end
else begin
buzzer <= 1'b0;
end
end
else begin
buzzer <= 1'b1;
end
end
else
buzzer <= 1'b1;
end
endmodule

程序写好后,进行管脚的约束

点击Device下的I/O,然后将对应管脚填进入

保存后全部编译

编译成功后,将程序下载到开发板中


下载成功后就可以看到实验现象了,蜂鸣器播放小星星,由于视频不知道怎么传,放在文末的压缩包里了。
五、       结论
本次使用无源蜂鸣器完成了小星星和两只小老虎儿歌的播放,实验比较简单,没有涉及到很多逻辑资源,但也是非常有趣的,接下里进行进行探索。
QQ短视频20230422164040.7z (4.84 MB, 下载次数: 0)
分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条