本帖最后由 feifeiz 于 2023-4-28 23:23 编辑
一、 前言继上次拿到板子后,对板子的资源情况进行了了解,但是没有进行环境搭建,这段时间中,每天学一点,将环境搭建好了,买了一个紫光同创的专用下载器,以及对licence进行了关联。完成后基本准备,后面也进行了基本的使用,本次使用开发板进蜂鸣器实现,旨在进一步熟悉环境,以及对FPGA编程的熟悉,本次原打算使用板载的蜂鸣器进行歌曲的播放,但是查看原理图,厂家使用的是有源的蜂鸣器,于是自己买了一个无源的蜂鸣器进行试验。 二、 知识准备本次使用蜂鸣器进行歌曲的播放,需要有一定的音乐知识,然后了解1,2,3,4,5,6,7每个音符的实现,首先无源蜂鸣器是指没有震荡源,如果用直流信号无法令其鸣叫。必须用PWM的方波去驱动它,而不同的脉冲频率会使蜂鸣器有不同的响声。 如下表为每个音符对应的频率及半周期
以下是在网上买的蜂鸣器模块,低电平触发,只需要个I/O给不同的脉冲频率就可以发出不同的响声。
虽然现在有了硬件和每个音符的频率,当输出一个周期的脉冲给蜂鸣器可以听到响声,但是持续时间太短,可能都听不清,那么就需要将每个音符发出的响声进行延长,才能听清。 如前面的表格,得到每个音符的周期数,在对每个音符进行多次触发进行延长,如250次。就可以得到每个音符延长后的时钟周期数。通过总的时钟周期数除以每个音符周期时序数就可以得到每个音符播放的次数。 实现原理知道了,接下来就是播放的歌曲,本次播放的是小星星和两只小老虎这种简单的儿歌,通过按键进行歌曲间的切换。 小星星歌谱如下:
两只小老虎歌谱:
三、 硬件设计与连接本次使用的是买的蜂鸣器模块,接到的是开发板的F4引脚,实物连接如下
对应引脚连接:
信号名 | | | | sys_clk | | | | sys_rst_n | | | | key | | | | buzzer | | | |
四、 程序设计程序如下: 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,然后将对应管脚填进入
保存后全部编译
编译成功后,将程序下载到开发板中
下载成功后就可以看到实验现象了,蜂鸣器播放小星星,由于视频不知道怎么传,放在文末的压缩包里了。 五、 结论本次使用无源蜂鸣器完成了小星星和两只小老虎儿歌的播放,实验比较简单,没有涉及到很多逻辑资源,但也是非常有趣的,接下里进行进行探索。 |