FPGA学习|15无源蜂鸣器

一、蜂鸣器
1、什么是蜂鸣器
蜂鸣器按其结构可分为电磁式蜂鸣器和压电式蜂鸣器两种类型;蜂鸣器按其是否带有信号源又分为有源蜂鸣器和无源蜂鸣器。
有源蜂鸣器的内部装有集成电路,不需要音频驱动电路,只需要接通直流电源就能直接发出声响;而无源蜂鸣器只有外加音频驱动信号才能发出声响。
2、无源蜂鸣器原理
无源蜂鸣器与有源蜂鸣器不同,因其内部不带震荡源,所以其无法像有源蜂鸣器那样直接用直流信号驱动,这里需要使用PWM方波才能驱动其发声。
输入不同频率和占空比的PWM方波发出的声音是不同的,其中频率对音调有影响,占空比对音量大小有影响。
FPGA学习|15无源蜂鸣器
文章图片

【FPGA学习|15无源蜂鸣器】二、实现
目标:实现七个音调的循环发声,每个音调发声五秒,占空比50%
例如:DO的频率时262Hz,其计数周期为0~19084之间循环计数
FPGA学习|15无源蜂鸣器
文章图片

同理,其余的也是这样,其表格如下,占空比对音量的大小有关系,我们要求的是50%,因此其计数值时音调分频计数值的一半
FPGA学习|15无源蜂鸣器
文章图片

1.波形图
FPGA学习|15无源蜂鸣器
文章图片

2.程序

module beep #( parameterCNT_MAX = 25'd24999999, parameterDO= 18'd190839,//"哆"音调分频计数值(频率262) parameterRE= 18'd170067,//"来"音调分频计数值(频率294) parameterMI= 18'd151514,//"咪"音调分频计数值(频率330) parameterFA= 18'd143265,//"发"音调分频计数值(频率349) parameterSO= 18'd127550,//"梭"音调分频计数值(频率392) parameterLA= 18'd113635,//"拉"音调分频计数值(频率440) parameterSI= 18'd101213 //"西"音调分频计数值(频率494))( inputwiresys_clk, inputwiresys_rst_n,outputregbeep ); reg[24:0]cnt; //0.5s计数器 reg[2:0]cnt_500ms; //0.5s个数计数 reg[17:0]fre_cnt; //音调计数器 reg[17:0]fre_data; //音调分频计数值 wire[16:0]duty_data; //占空比计数值//cnt:0.5s循环计数器 always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) cnt <= 25'd0; else if(cnt == CNT_MAX) cnt <= 25'd0; else cnt <= cnt + 1'b1; //cnt_500ms:对500ms个数进行计数,每个音阶鸣叫时间0.5s,7个音节一循环 always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) cnt_500ms <= 3'd0; else if ((cnt_500ms == 3'd6)&&(cnt == CNT_MAX)) cnt_500ms <= 3'd0; else if (cnt == CNT_MAX) cnt_500ms <= cnt_500ms + 1'b1; else cnt_500ms <= cnt_500ms; /*fre_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数 与下面的方式一样能够实现其功能,此方法比较复杂,只供参考*/ /* always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) fre_cnt <= 18'd0; elsecase (cnt_500ms) 3'd0:if ((fre_cnt == DO)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd1:if ((fre_cnt == RE)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd2:if ((fre_cnt == MI)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd3:if ((fre_cnt == FA)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd4:if ((fre_cnt == SO)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd5:if ((fre_cnt == LA)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; 3'd6:if ((fre_cnt == SI)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +18'd1; default:fre_cnt <= 18'd0; endcase */ //fre_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数 always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) fre_cnt <= 18'd0; else if ((fre_cnt == fre_data)||(cnt == CNT_MAX)) fre_cnt <= 18'd0; else fre_cnt <= fre_cnt +1'b1; //不同时间鸣叫不同的音阶 always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) fre_data <= DO; elsecase (cnt_500ms) 3'd0: fre_data <= DO; 3'd1: fre_data <= RE; 3'd2: fre_data <= MI; 3'd3: fre_data <= FA; 3'd4: fre_data <= SO; 3'd5: fre_data <= LA; 3'd6: fre_data <= SI; default: fre_data <= DO; endcase//设置50%占空比:音阶分频计数值的一半即为占空比的高电平数 assign duty_data = https://www.it610.com/article/fre_data>> 1; //beep:输出蜂鸣器波形 always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) beep <= 1'b0; else if (duty_data >= fre_cnt) beep <= 1'b1; else if (duty_data < fre_cnt) beep <= 1'b0; else beep <= beep; endmodule

3、仿真程序
`timescale1ns/1ns moduletb_beep(); regsys_clk; //时钟 regsys_rst_n; //复位//对时钟,复位信号赋初值 initial begin sys_clk=1'b1; sys_rst_n<=1'b0; #100 sys_rst_n<=1'b1; end//产生时钟信号 always #10 sys_clk =~sys_clk; beep #( .CNT_MAX(25'd24999),//0.5s计数值 .DO(18'd190),//"哆"音调分频计数值(频率262) .RE(18'd170),//"来"音调分频计数值(频率294) .MI(18'd151),//"咪"音调分频计数值(频率330) .FA(18'd143),//"发"音调分频计数值(频率349) .SO(18'd127),//"梭"音调分频计数值(频率392) .LA(18'd113),//"拉"音调分频计数值(频率440) .SI(18'd101)//"西"音调分频计数值(频率494) ) beep_inst ( .sys_clk(sys_clk),//系统时钟,频率50MHz .sys_rst_n(sys_rst_n ),//系统复位,低有效.beep(beep)//输出蜂鸣器控制信号 ); endmodule

    推荐阅读