赞
踩
文章目录
蜂鸣器(Buzzer)是现代常用的一种电子发声器,主要用于产生声音信号。它是一种一体化结构的电子讯响器,采用直流电压供电,被广泛用于计算机、报警器、电子玩具、定时器等一些列电子产品中。
蜂鸣器按照其驱动方式不同主要分为有源蜂鸣器和无源蜂鸣器,两者的主要区别为蜂鸣器内部是否还有振荡源。一般有源蜂鸣器内部自带振荡源,通电就会发声,而无源蜂鸣器内部不含振荡源,需要外接振荡信号才能发声。
相较于有源蜂鸣器,无源蜂鸣器的成本较低,而我们FPGA开发板上的蜂鸣器一般都是无源蜂鸣器,需要我们对其进行编程配置,我们利用不同的频率变化,控制蜂鸣器发出不同音调的声音。
我们是通过不同的频率去控制蜂鸣器的音调变化的,所以我们想要使蜂鸣器播放音乐,首先我们要直到不同音符所对应的频率,下面是低、中、高音下不同音符的频率对应表:
根据上图可以计算出不同音符振动的周期,我所采用的Cyclong IV开发板上的晶振时钟为50MHZ,每一个周期就是20ns。那么每个音调的分频系数为:50 000 000 / 音调频率(可四舍五入)。
本次设计的要求就是利用FPGA开发板上的无源蜂鸣器,通过不同振动频率播放歌曲两只老虎。
① 首先我们需要计算出每个不同的音符对应的分频系数,由此产生不同的音调,我们需要设计一个分频计数器,来计数当前发出音调的分频系数。
② 然后我们需要一个节拍计数器,我们定义为半拍300ms,一拍500ms。
③ 利用组合逻辑case语句进行设计,将需要的节拍和要发出的音调写入case语句中。
- /*========================================*
- filename : beep_music.v
- description : 无源蜂鸣器播放音乐实验
- time : 2022-11-010
- author : 卡夫卡与海
- *========================================*/
-
- module beep_music(
- input clk ,//系统时钟 50MHZ
- input rst_n ,//系统复位
-
- output reg beep //蜂鸣器控制信号
- );
- //参数定义
- //每个音符震动一次所占用的时钟周期
- //低音
- parameter MIN_DO = 18'd190800,//(50_000_000/262)
- MIN_RE = 18'd170050,//(50_000_000/294)
- MIN_MI = 18'd151500,//(50_000_000/330)
- MIN_FA = 18'd143250,//(50_000_000/349)
- MIN_SO = 18'd127550,//(50_000_000/392)
- MIN_LA = 18'd113600,//(50_000_000/440)
- MIN_XI = 18'd101200;//(50_000_000/494)
- //中音
- parameter MID_DO = 17'd95600,//(50_000_000/523)
- MID_RE = 17'd85150,//(50_000_000/587)
- MID_MI = 17'd75850,//(50_000_000/659)
- MID_FA = 17'd71600,//(50_000_000/698)
- MID_SO = 17'd63750,//(50_000_000/784)
- MID_LA = 17'd56800,//(50_000_000/880)
- MID_XI = 17'd50600;//(50_000_000/988)
- //高音
- parameter MAX_DO = 16'd47755,//(50_000_000/1047)
- MAX_RE = 16'd42553,//(50_000_000/1175)
- MAX_MI = 16'd37907,//(50_000_000/1319)
- MAX_FA = 16'd35790,//(50_000_000/1397)
- MAX_SO = 16'd31887,//(50_000_000/1568)
- MAX_LA = 16'd28409,//(50_000_000/1760)
- MAX_XI = 16'd25419;//(50_000_000/1967)
- parameter TIME_300ms = 24'd14_999_999,//300ms,半拍
- TIME_500ms = 25'd24_999_999;//500ms,一拍
- parameter NOTE_NUM = 6'd33;//音符个数 34个
-
- //信号定义
- 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_flag ;//所有音符结束标志
-
- reg [24:0] cnt_delay_r ;
-
- reg flag ;//蜂鸣器输出标志
-
- //300ms计数器 cnt_delay
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_delay <= 25'd0;
- end
- else if(cnt_delay == cnt_delay_r)begin
- cnt_delay <= 25'd0;
- end
- else begin
- cnt_delay <= cnt_delay + 1'b1;
- end
- end
- //音符计数器 cnt_note
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_note <= 6'd0;
- end
- else if(end_flag)begin
- cnt_note <= 6'd0;
- end
- else if(cnt_delay == cnt_delay_r)begin
- cnt_note <= cnt_note + 1'b1;
- end
- else begin
- cnt_note <= cnt_note;
- end
- end
-
- //所有音符结束标志 end_flag
- assign end_flag = cnt_note == NOTE_NUM && cnt_delay == cnt_delay_r;
-
- //单个音符振动周期 cnt_freq
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_freq <= 19'd1;
- end
- else if(end_note)begin
- cnt_freq <= 19'd1;
- end
- else begin
- cnt_freq <= cnt_freq + 1'b1;
- end
- end
- //单个音符结束标志 end_note
- assign end_note = (cnt_freq == freq_data);
- //音符数据选择 freq_data
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- freq_data <= MAX_DO;
- end
- else begin
- case(cnt_note)
- 6'd0:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd1:begin
- freq_data <= MID_RE;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd2:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd3:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd4:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd5:begin
- freq_data <= MID_RE;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd6:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd7:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd8:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd9:begin
- freq_data <= MID_FA;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd10:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd11:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd12:begin
- freq_data <= MID_FA;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd13:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd14:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd15:begin
- freq_data <= MID_LA;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd16:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd17:begin
- freq_data <= MID_FA;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd18:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd19:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd20:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd21:begin
- freq_data <= MID_LA;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd22:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd23:begin
- freq_data <= MID_FA;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd24:begin
- freq_data <= MID_MI;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd25:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- 6'd26:begin
- freq_data <= MID_RE;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd27:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd28:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd29:begin
- freq_data <= 1'b0;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd30:begin
- freq_data <= MID_RE;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd31:begin
- freq_data <= MID_SO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd32:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_500ms;
- end
- 6'd33:begin
- freq_data <= 1'b0;
- cnt_delay_r <= TIME_500ms;
- end
- default:begin
- freq_data <= MID_DO;
- cnt_delay_r <= TIME_300ms;
- end
- endcase
- end
- end
- //占空比 duty_data
- assign duty_data = freq_data >> 3;//移位越多,占空比越高
- // flag
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- flag <= 1'b0;
- end
- else begin
- flag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;
- end
- end
-
- //输出 beep
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- beep <= 1'b0;
- end
- else if(flag)begin
- beep <= 1'b1;
- end
- else begin
- beep <= 1'b0;
- end
- end
- endmodule
这个原理还是挺简单的,如果感兴趣的话还可以尝试这去写更复杂的音乐,但是这个蜂鸣器的声音真正不是很友好,如果能加一个音频转换器效果应该会好很多。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。