🍎 与其担心未来,不如现在好好努力。在这条路上,只有奋斗才能给你安全感。你若努力,全世界都会为你让路。
蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源他激型与有源自激型。本实验采用无源蜂鸣器,蜂鸣器的发声不同是靠频率不同进行控制的,音调的大小是靠占空比也决定的。
下面附上蜂鸣器的电路图:
由蜂鸣器电路图可以看出,蜂鸣器打开需要BEEP端口为高电平,关闭则是让BEEP端口为低电平。
清楚了蜂鸣器控制方式以后,我们再来看一下如何让蜂鸣器发出“哆来咪发索拉西“的声音。
上图是蜂鸣器发出不同声音的一个频率表。由频率可以得到具体的计数周期,开发板的晶振频率为50MHz,那低音1的计数周期就应该为:,我们可以定义一个音的时间周期为500ms,当计数到190839的一半时,把蜂鸣器打开,当计数到190839时,把蜂鸣器关闭,下一个计数从0重新计数。这样它就能发出低音1的音,也就是"哆"音,同时它的一个占空比就是50%。发出第一个音后,我们可以在第二个500ms让蜂鸣器发出低音2,也就是"来"音,以此类推,我们只定义低音1~低音7总共7个音,定义一个3位宽的数就可以把它们都罗列出来。下面是这次任务的一个波形图。
参照波形图,我们对任务进行代码编写:
module beep //模块开始,定义名称为beep
#(
parameter CNT_MAX = 25'd24_999_999 , //定义全局变量CNT_MAX,时间周期为500ms
parameter DO = 18'd190839 , //定义全局变量DO,计数值为190839,发“哆”音
parameter RE = 18'd170067 , //定义全局变量RE,计数值为170067,发“来”音
parameter MI = 18'd151514 , //定义全局变量MI,计数值为151514,发“咪”音
parameter FA = 18'd143265 , //定义全局变量FA,计数值为143265,发“发”音
parameter SO = 18'd127550 , //定义全局变量SO,计数值为127550,发“嗦”音
parameter LA = 18'd113635 , //定义全局变量LA,计数值为113635,发“拉”音
parameter XI = 18'd101214 //定义全局变量XI,计数值为101214,发“西”音
)
(
input wire sys_clk , //定义sys_clk为输入模式 (时钟)
input wire sys_rst_n , //定义sys_rst_n为输入模式 (复位)
output reg beep_out //定义beep_out为寄存器类型的输出模式
);
reg [24:0] cnt ; //定义cnt为25位宽的寄存器类型
reg [17:0] freq_cnt; //定义freq_cnt为18位宽的寄存器类型
reg [2:0] cnt_500ms; //定义cnt_500ms为3位宽的寄存器类型
reg [17:0] freq_data; //定义freq_data为18位宽的寄存器类型
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0) //复位信号到来
begin
cnt <= 25'd0; //使cnt清0
end
else if(cnt == CNT_MAX) //判断cnt是否计数到最大值
begin
cnt <= 25'd0; //使cnt清0
end
else
cnt <= cnt+ 25'd1; //使cnt + 1
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0) //复位信号到来
begin
cnt_500ms <= 3'd0; //使cnt_500ms清0
end
else if(cnt_500ms == 3'd6&&cnt == CNT_MAX) //判断cnt_500ms是否计数到3'd6
begin
cnt_500ms <= 3'd0; //使cnt_500ms清0
end
else if(cnt == CNT_MAX) //判断cnt是否计数到最大值
begin
cnt_500ms <= cnt_500ms+ 3'd1; //使cnt_500ms + 1
end
else
cnt_500ms <= cnt_500ms; //使cnt_500ms保持不变
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0) //复位信号到来
begin
freq_data <= DO; //把DO的值赋给freq_data
end
else case(cnt_500ms)
3'd0:freq_data <= DO ; //把DO的值赋给freq_data
3'd1:freq_data <= RE ; //把RE的值赋给freq_data
3'd2:freq_data <= MI ; //把MI的值赋给freq_data
3'd3:freq_data <= FA ; //把FA的值赋给freq_data
3'd4:freq_data <= SO ; //把SO的值赋给freq_data
3'd5:freq_data <= LA ; //把LA的值赋给freq_data
3'd6:freq_data <= XI ; //把XI的值赋给freq_data
default:freq_data <= DO ; //把DO的值赋给freq_data
endcase
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0) //复位信号到来
begin
freq_cnt <= 18'd0; //使freq_cnt清0
end
else if((freq_cnt == freq_data)||(cnt == CNT_MAX)) //判断freq_cnt是否计数到freq_data或者cnt计数到最大值
begin
freq_cnt <= 18'd0; //使freq_cnt清0
end
else
freq_cnt <= freq_cnt+ 18'd1; //使freq_cnt + 1
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0) //复位信号到来
begin
beep_out <= 1'b0; //使beep_out清0
end
else if(freq_cnt >= freq_data/2) //判断freq_cnt是否计数到freq_data的一半
begin
beep_out <= 1'b1; //使beep_out置1
end
else
begin
beep_out <= 1'b0; //使beep_out清0
end
end
endmodule //模块结束
下面是仿真的波形:
从图中可以看出cnt_500ms的值是从0计数到6,再次计数会变为0,刚好对应7个音,符合我们要求。
freq_data中的数据对应不同音调的计数周期,因为数值太大,所以在编写仿真文件时,这里把数值改小了,方便查看波形。
cnt的值仿真文件里设置的数为100,这里计数到100会从0开始,与任务波形图符合。
下面是仿真代码:
`timescale 1ns/1ns //时间尺度预编译指令 时间单位/时间精度
module tb_beep(); //定义模块名称为tb_beep
reg sys_clk; //定义sys_clk为reg型
reg sys_rst_n; //定义sys_rst_n为reg型
wire beep_out; //定义beep_out为reg型
initial //初始化
begin
sys_clk = 1'b1 ; //使sys_clk初始化为高电平状态
sys_rst_n = 1'b0 ; //使sys_clk初始化为低电平状态
#20 //延时20ns
sys_rst_n = 1'b1 ; //使sys_rst_n电平拉高
end
always #10 sys_clk = ~sys_clk; //使sys_clk电平10ns电平状态反转一次
beep //例化对象名称
#(
.CNT_MAX(25'd100) , //改变parameter定义的参数
.DO (18'd19) ,
.RE (18'd17) ,
.MI (18'd15) ,
.FA (18'd14) ,
.SO (18'd12) ,
.LA (18'd11) ,
.XI (18'd10)
)
beep_inst //实例化名称
(
.sys_clk (sys_clk ), //使sys_clk信号端口例化为sys_clk
.sys_rst_n (sys_rst_n), //使sys_rst_n信号端口例化为sys_rst_n
.beep_out (beep_out ) //使beep_out信号端口例化为beep_out
);
endmodule //模块结束
🔥🔥🔥本系列文章持续更新,喜欢的话可以关注收藏~🔥🔥🔥