一个经典的蜂鸣器驱动电路:
其中电容 C36 用于提高电路抗干扰性能。 D1 起保护三极管的作用(当三极管突然截止时,无源蜂鸣器两端产生的瞬时感应电动势可以通过 D3 迅速释放掉,避免叠加到三极管集电极上从而击穿三极管。)beep 端口接FPGA 输出管脚,使用时,只需要在beep 信号上输出pwm 波(当然这个PWM波的频率要在蜂鸣器的发生范围内),就能驱动蜂鸣器发声。
我们先看看,如何PWM的模型是如何定义的:
- //模型的定义,完全类比于MCU的定时器PWM发送模块:
- module pwm_generator(
- Clk50M,
- Rst_n,
- cnt_en,
- counter_arr,
- counter_ccr,
- o_pwm
- );
- input Clk50M; //50MHz时钟输入
- input Rst_n; //复位输入,低电平复位
- input cnt_en; //计数使能信号
- input [31:0]counter_arr;//输入32位预重装值
- input [31:0]counter_ccr;//输入32位输出比较值
- output reg o_pwm; //pwm输出信号
- //周期产生
- reg [31:0]counter;//定义32位计数器
- always@(posedge Clk50M or negedge Rst_n)
- if(!Rst_n)
- counter <= 32'd0;
- else if(cnt_en)begin
- if(counter == 0)
- counter <= counter_arr;//计数到0,加载自动预重装寄存器值
- else
- counter <= counter - 1'b1;//计数器自减1
- end
- else
- counter <= counter_arr; //没有使能时,计数器值等于预重装寄存器值
- //与阀值比较翻转电平产生PWM波形
- always@(posedge Clk50M or negedge Rst_n)
- if(!Rst_n) //让PWM输出信号复位时输出低电平
- o_pwm <= 1'b0;
- else if(counter >= counter_ccr)//计数值大于比较值
- o_pwm <= 1'b0; //输出为0
- else //计数值小于比较值
- o_pwm <= 1'b1; //输出为1
-
- endmodule
复制代码
我们看到,完全类比于MCU的定时器PWM发送模块,思路几乎一样。这也看出了FPGA的有点,可以模仿已知时序或者逻辑的其他外设。
我们看看模型的实例化:
module pwm_generator_test(
Clk50M, //系统时钟50M
Rst_n, //系统复位
beep //蜂鸣器控制输出
);
input Clk50M;
input Rst_n;
output beep;
reg [31:0]counter_arr; //预重装值寄存器
wire [31:0]counter_ccr; //输出比较值
reg [24:0]delay_cnt; //500ms延时计数器
reg [4:0]Pitch_num; //音调编号
localparam
L1 = 191130, //低音1
L2 = 170241, //低音2
L3 = 151698, //低音3
L4 = 143183, //低音4
L5 = 127550, //低音5
L6 = 113635, //低音6
L7 = 101234, //低音7
M1 = 95546, //中音1
M2 = 85134, //中音2
M3 = 75837, //中音3
M4 = 71581, //中音4
M5 = 63775, //中音5
M6 = 56817, //中音6
M7 = 50617, //中音7
H1 = 47823, //高音1
H2 = 42563, //高音2
H3 = 37921, //高音3
H4 = 35793, //高音4
H5 = 31887, //高音5
H6 = 28408, //高音6
H7 = 25309; //高音7
//输出比较值为预重装值一半
assign counter_ccr = counter_arr >> 1;
pwm_generator pwm_generator(
.Clk50M(Clk50M),
.Rst_n(Rst_n),
.cnt_en(1'b1),
.counter_arr(counter_arr),
.counter_ccr(counter_ccr),
.o_pwm(beep)
);
//500ms延时计数器计数
always@(posedge Clk50M or negedge Rst_n)
if(!Rst_n)
delay_cnt <= 25'd0;
else if(delay_cnt == 0)
delay_cnt <= 25'd24999999;
else
delay_cnt <= delay_cnt - 1'b1;
//每500ms切换一次音调
always@(posedge Clk50M or negedge Rst_n)
if(!Rst_n)
Pitch_num <= 5'd0;
else if(delay_cnt == 0)begin
if(Pitch_num == 5'd20)
Pitch_num <= 5'd0;
else
Pitch_num <= Pitch_num + 5'd1;
end
else
Pitch_num <= Pitch_num;
//根据音调编号给预重装值给相应的值
always@(*)
case(Pitch_num)
0 :counter_arr = L1;
1 :counter_arr = L2;
2 :counter_arr = L3;
3 :counter_arr = L4;
4 :counter_arr = L5;
5 :counter_arr = L6;
6 :counter_arr = L7;
7 :counter_arr = M1;
8 :counter_arr = M2;
9 :counter_arr = M3;
10:counter_arr = M4;
11:counter_arr = M5;
12:counter_arr = M6;
13:counter_arr = M7;
14:counter_arr = H1;
15:counter_arr = H2;
16:counter_arr = H3;
17:counter_arr = H4;
18:counter_arr = H5;
19:counter_arr = H6;
20:counter_arr = H7;
default:counter_arr = L1;
endcase
endmodule
我们看到蜂鸣器发出不同声调,实际上就是每个固定时间,切换PWM的不同频率而已。
如果对音调对应的周期计时数,按照一个频谱来对应的话,就能奏出美妙的音乐了。
如果要,及时更改对应频率的占空比怎么做,那就在对应的周期数边上,再加上比较值的数值就行了:
19:counter_arr = H6;counter_ccr
=counter_arr
*0.4;
我们让他输出1KPWM波形,并查看:
屏蔽其他的赋值代码:
always@(*)
counter_arr=50000;
下载查看:
PWM就到这里了。
`