【FPGA入门六】蜂鸣器播放两只老虎

编程入门 行业动态 更新时间:2024-10-09 01:14:09

【FPGA入门六】<a href=https://www.elefans.com/category/jswz/34/1769231.html style=蜂鸣器播放两只老虎"/>

【FPGA入门六】蜂鸣器播放两只老虎

文章目录

    • 一.音频音符知识
    • 二.任务
    • 三.工程项目
      • Verilog HDL编写
        • ①设计音频选择文件
        • ②设计音频产生文件
        • ③设计顶层文件
    • 四.总结
    • 五.参考链接

一.音频音符知识

不同的音符振动频率不同,周期T=1/频率f

根据上图可以计算出音符振动的周期,单位微秒。Cyclone IV开发板的晶振是50MHz,振动一次是20纳秒,使用周期时间除以20纳秒得出音符振动的次数。比如高音的DO计算方式如下公式所示。

二.任务

利用蜂鸣器的不同振动频率播放两只老虎。
每个音符持续时间:半拍300ms,一拍500ms

三.工程项目

Verilog HDL编写

①设计音频选择文件

freq_select.v

module freq_select(input wire clk,//时钟input wire rst_n,//复位信号output reg flag//脉宽调制信号
);parameter CNT_DELAY = 24'd14_999_999;//300ms=0.3s,15_000_000次 半拍parameter CNT_DELAY2 = 25'd24_999_999;//500ms=0.5s,25_000_000次 一拍parameter NOTE_NUM = 6'd33;//音符数 34个
//各个音符对应振动次数
//高音
parameter MAX_DO = 16'd47750;
parameter MAX_RE = 16'd42550;
parameter MAX_MI = 16'd37900;
parameter MAX_FA = 16'd37550;
parameter MAX_SO = 16'd31850;
parameter MAX_LA = 16'd28400;
parameter MAX_SI = 16'd25400;
//中音
parameter MID_DO = 17'd95600;
parameter MID_RE = 17'd85150;
parameter MID_MI = 17'd75850;
parameter MID_FA = 17'd71600;
parameter MID_SO = 17'd63750;
parameter MID_LA = 17'd56800;
parameter MID_SI = 17'd50600;
//低音
parameter MIN_DO = 18'd190800;
parameter MIN_RE = 18'd170050;
parameter MIN_MI = 18'd151500;
parameter MIN_FA = 18'd143250;
parameter MIN_SO = 18'd127550;
parameter MIN_LA = 18'd113600;
parameter MIN_SI = 18'd101200;reg  [24:0] cnt_delay	;//300ms或500ms计数器
reg  [ 5:0] cnt_note 	;//音符计数器
reg  [18:0] cnt_freq 	;//音符播放计数器
reg  [18:0] freq_data	;//音符数据寄存器wire [17:0] duty_data	;//占空比
wire 			end_note		;//单个音符播放结束标志
wire 			end_spectrum;//所有音符结束标志reg  [24:0] cnt_delay_r	;//300ms计数功能
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_delay <= 25'd0;endelse if(cnt_delay == cnt_delay_r)begincnt_delay <= 25'd0;//计数器达到最大值,清空endelse begincnt_delay <= cnt_delay + 1'b1;end
end//音符计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_note <= 6'd0;endelse if(end_spectrum)begin//音符数记到最大值cnt_note <= 6'd0;//清零endelse if(cnt_delay == cnt_delay_r)begin//当前音符计时结束cnt_note <= cnt_note + 1'b1;endelse begincnt_note <= cnt_note;//保持end
end//所有音符结束标志
assign end_spectrum = cnt_note == NOTE_NUM && cnt_delay == cnt_delay_r;//最后一个音符持续时间达到300ms,音谱结束//单个音符振动周期
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_freq <= 16'd1;endelse if(end_note)begincnt_freq <= 16'd1;endelse begincnt_freq <= cnt_freq + 1'b1;end
end//单个音符结束标志
assign end_note = cnt_freq == freq_data;//当音符计数器的值达到当前音符的震动次数,该音符就结束//音符数据选择
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginfreq_data <= MAX_DO;endelse begincase(cnt_note)6'd0   : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;end6'd1   : beginfreq_data <= MID_RE;cnt_delay_r <= CNT_DELAY;end6'd2   : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY;end6'd3   : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;end6'd4   : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;end6'd5   : beginfreq_data <= MID_RE;cnt_delay_r <= CNT_DELAY;end6'd6   : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY;end6'd7   : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;end6'd8   : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY;end6'd9   : beginfreq_data <= MID_FA;cnt_delay_r <= CNT_DELAY;end6'd10  : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY2;end6'd11	 : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY;end6'd12	 : beginfreq_data <= MID_FA;cnt_delay_r <= CNT_DELAY;end6'd13	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY2;end6'd14	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY;end6'd15	 : beginfreq_data <= MID_LA;cnt_delay_r <= CNT_DELAY;end6'd16	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY;end6'd17	 : beginfreq_data <= MID_FA;cnt_delay_r <= CNT_DELAY2;end6'd18	 : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY2;end6'd19	 : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;end6'd20	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY;end6'd21	 : beginfreq_data <= MID_LA;cnt_delay_r <= CNT_DELAY;end6'd22	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY;end6'd23	 : beginfreq_data <= MID_FA;cnt_delay_r <= CNT_DELAY2;end6'd24	 : beginfreq_data <= MID_MI;cnt_delay_r <= CNT_DELAY2;end6'd25	 : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY2;end6'd26	 : beginfreq_data <= MID_RE;cnt_delay_r <= CNT_DELAY2;end6'd27	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY2;end6'd28	 : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY2;end6'd29	 : beginfreq_data <= 1'b0;cnt_delay_r <= CNT_DELAY2;end6'd30	 : beginfreq_data <= MID_RE;cnt_delay_r <= CNT_DELAY2;end6'd31	 : beginfreq_data <= MID_SO;cnt_delay_r <= CNT_DELAY2;end6'd32	 : beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY2;end6'd33	 : beginfreq_data <= 1'b0;cnt_delay_r <= CNT_DELAY2;enddefault:beginfreq_data <= MID_DO;cnt_delay_r <= CNT_DELAY;endendcaseend
end//占空比
assign duty_data = freq_data >> 3;//移位越多,占空比越高always@(posedge clk or negedge rst_n)beginif(!rst_n)beginflag <= 1'b0;endelse beginflag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;end
endendmodule
②设计音频产生文件

gen_pwm.v

module gen_pwm(input wire clk,input wire rst_n,input wire flag,output reg beep
);always@(posedge clk or negedge rst_n)beginif(!rst_n)beginbeep <= 1'b0;endelse if(flag)beginbeep <= 1'b1;endelse beginbeep <= 1'b0;end
endendmodule
③设计顶层文件
module top_pwm_beep(input wire clk,input wire rst_n,output wire beep
);wire flag;freq_select inst_freq_select(
.clk  (clk),
.rst_n(rst_n),.flag (flag)
);gen_pwm inst_gen_pwm(
.clk  (clk),
.rst_n(rst_n),
.flag (flag),.beep (beep)
);
endmodule

编译:

查看RTL门级电路:

引脚绑定:

硬件测试:

四.总结

音乐播放这一块需要一点乐理知识,理解不同音符的特点,改变蜂鸣器的振动频率就可以模拟相应的音符音频。占空比的大小也会影响声音的清晰度,特别要注意的是音频音符的结束标志,音符根据时间是否延迟相应节拍的延迟时间判断,音谱根据是否计数到最后一个音符,且延迟时间是否达到该音符的节拍延迟时间。

五.参考链接

更多推荐

【FPGA入门六】蜂鸣器播放两只老虎

本文发布于:2024-02-07 11:01:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1756250.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:蜂鸣器   两只   老虎   入门   FPGA

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!