蜂鸣器播放两只老虎"/>
【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入门六】蜂鸣器播放两只老虎
发布评论