设计规划
驱动无源蜂鸣器进行七个基本音调“哆来咪发梭拉西”的循环鸣叫,每个音阶持续鸣叫0.5s后鸣叫下一个音阶。
对于无源蜂鸣器,输入不同的PWM方波就能发出不同的声音。方波的频率影响音调,占空比影响音量。音调频率对应表格如下,占空比保持为50%。因此这个实验我们只要循环产生占空比为50%的七个音调频率即可。
Do | Re | Mi | Fa | So | La | Si |
---|---|---|---|---|---|---|
262 | 294 | 330 | 349 | 392 | 440 | 494 |
现在我们考虑,除了时钟和复位,需要几个计数器:
1、蜂鸣维持0.5s,(50MHz对应20ns)计数从0-24999999,计数器名称cnt。
2、需要蜂鸣7个调,状态计数从0-6,cnt计满一次就+1,计数器名称cnt_500ms。
3、不同的调对应不同频率,需要一个频率计数器freq_cnt。
以Do为例,频率为262,周期为1/262=3.816794ms。时钟频率50MHz,对应周期是20ns,即计数个数为190840个,对应do的freq_cnt计数从0-190839。占空比为50%,即高电平和低电平的时长一样,高电平持续时钟脉冲个数为95420。
频率 | 262 | 294 | 330 | 349 | 392 | 440 | 494 |
---|---|---|---|---|---|---|---|
频率数值 | 190840 | 170068 | 151515 | 143266 | 127551 | 113636 | 101214 |
占空比数值 | 95420 | 85034 | 75757 | 71633 | 63775 | 56818 | 50607 |
以Re为例的波形图:
改变频率数值freq_data和占空比计数duty_data,就可以得到不同频率的波形。
编写代码
module beep
#(
parameter TIME_500MS = 25'd24999, //0.5s计数值
parameter DO = 18'd190 , //Do频率262
parameter RE = 18'd170 , //Re频率294
parameter MI = 18'd151 , //Mi频率330
parameter FA = 18'd143 , //Fa频率349
parameter SO = 18'd127 , //So频率392
parameter LA = 18'd113 , //La频率440
parameter XI = 18'd101 //Si频率494
)
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg beep
);
//reg define
reg [24:0] cnt ; //0.5s计数器
reg [17:0] freq_cnt ; //音调计数器
reg [2:0] cnt_500ms ; //0.5s个数计数
reg [17:0] freq_data ; //音调分频计数值,取不同的DO,RE...可以得到不同频率的波形
//wire define
wire [16:0] duty_data ; //占空比计数值,即DO,RE...的一半
assign duty_data = freq_data > > 1'b1; //二进制右移一位就是原值的1/2
//cnt:0.5s循环计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == TIME_500MS )
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 == TIME_500MS && cnt_500ms == 6)
cnt_500ms <= 3'd0;
else if(cnt == TIME_500MS)
cnt_500ms <= cnt_500ms + 1'b1;
//不同时间鸣叫不同的音阶
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_data <= DO;
else case(cnt_500ms)
0: freq_data <= DO;
1: freq_data <= RE;
2: freq_data <= MI;
3: freq_data <= FA;
4: freq_data <= SO;
5: freq_data <= LA;
6: freq_data <= XI;
default: freq_data <= DO;
endcase
//freq_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_cnt <= 18'd0;
else if(freq_cnt == freq_data || cnt == TIME_500MS)
freq_cnt <= 18'd0;
else
freq_cnt <= freq_cnt + 1'b1;
//beep:输出蜂鸣器波形
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
beep <= 1'b0;
else if(freq_cnt >= duty_data)
beep <= 1'b1;
else
beep <= 1'b0;
endmodule