在FPGA中,使用PWM信号来驱动蜂鸣器。首先,需要对系统时钟进行分频,通过计数器实现。计数器的计数最终值用于改变PWM信号的频率,从而实现调节蜂鸣器音调的目的。最后,PWM信号被用来控制蜂鸣器电路。
以下是代码重构:
```verilog
module Beeper(
input clk_in, // 系统时钟
input rst_n_in, // 系统复位,低有效
input tone_en, // 蜂鸣器使能信号
input [4:0] tone, // 蜂鸣器音节控制
output reg piano_out // 蜂鸣器控制输出
);
// 定义计数器和PWM信号相关的变量和参数
reg [5:0] counter; // 计数器
wire [7:0] pwm_out; // PWM信号输出
parameter COUNTER_WIDTH = 6; // 计数器的宽度为6位
parameter PRESCALER_VALUE = 8'h3C; // 预分频器的值为0x3C(96/128)
// 在每个上升沿或下降沿触发函数中更新计数器和PWM信号输出
always @(posedge clk_in or posedge rst_n_in) begin
if (rst_n_in) begin
counter <= CounterWidth'b0;
pwm_out <= 8'h0;
end else begin
if (tone_en) begin
counter <= counter + ONE; // 如果蜂鸣器使能信号为高电平,则计数器加1
if (counter == PRESCALER_VALUE) begin
pwm_out <= tone[COUNTER_WIDTH-1]; // 当计数器的值等于预分频器的值时,将PWM信号输出设置为对应音节的高低电平
counter <= CounterWidth'b0; // 将计数器清零以继续下一次周期
end else begin
pwm_out <= (counter == PRESCALER_VALUE * COUNTER_WIDTH) ? tone[COUNTER_WIDTH-1]: 8'h0; // 根据计数器的值计算PWM信号输出的高电平时间段
end
end else begin
pwm_out <= 8'h0; // 如果蜂鸣器使能信号为低电平,则将PWM信号输出设置为低电平
counter <= CounterWidth'b0; // 将计数器清零以继续下一次周期
end
end
end
```
无源蜂鸣器可以发出不同的音节,与蜂鸣器震动的频率(等于蜂鸣器控制信号的频率)相关。为了让蜂鸣器控制信号产生不同的频率,我们使用计数器计数(分频)实现,不同的音节控制对应不同的计数终值(分频系数)。
计数器根据计数终值计数并分频,产生蜂鸣器控制信号。以下是代码重构:
```verilog
reg [15:0] time_end;
always @(tone) begin
case(tone)
5'd1: time_end = 16'd22935; // L1
5'd2: time_end = 16'd20428; // L2
5'd3: time_end = 16'd18203; // L3
5'd4: time_end = 16'd17181; // L4
5'd5: time_end = 16'd15305; // L5
5'd6: time_end = 16'd13635; // L6
5'd7: time_end = 16'd12147; // L7
5'd8: time_end = 16'd11464; // M1
5'd9: time_end = 16'd10215; // M2
5'd10: time_end = 16'd9100; // M3
5'd11: time_end = 16'd8589; // M4
5'd12: time_end = 16'd7652; // M5
5'd13: time_end = 16'd6817; // M6
5'd14: time_end = 16'd6073; // M7
5'd15: time_end = 16'd5740; // H1
endcase
end
// 根据计数终值计算时间间隔,并生成蜂鸣器控制信号
always @(*) begin
if (time_end != 0) begin
#time_end = ~#time_end;
end else begin
#time_end = ~#time_end;
end
end
```
以下是重构后的代码:
```verilog
module tone_generator(
input wire clk_in,
input wire rst_n_in,
input wire tone_en,
output reg [17:0] time_cnt,
output reg piano_out
);
reg [31:0] time_end;
parameter H2 = 5'd16;
parameter H3 = 5'd4549;
parameter H4 = 5'd4294;
parameter H5 = 5'd3825;
parameter H6 = 5'd3408;
parameter H7 = 5'd3036;
parameter default_time_end = 5'd65535;
always @(posedge clk_in or negedge rst_n_in) begin
if (!rst_n_in) begin
time_cnt <= 1'b0;
end else if (!tone_en) begin
time_cnt <= 1'b0;
end else if (time_cnt >= time_end) begin
time_cnt <= 1'b0;
end else begin
time_cnt <= time_cnt + 1'b1;
end
end
always @(posedge clk_in or negedge rst_n_in) begin
if (!rst_n_in) begin
piano_out <= 1'b0;
end else if (time_cnt == time_end) begin
piano_out <= ~piano_out; //蜂鸣器控制输出翻转,两次翻转为1Hz
end else begin
piano_out <= piano_out;
end
end
integer i;
initial begin
i = $timetp.COUNTER_INITIALIZE;
end
always @(posedge clk_in or posedge i) begin
i <= i + 1;
end
endmodule
```