在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

```