FPGA开发(十)----------PWM驱动蜂鸣器

         本次实验我们实现PWM驱动一个蜂鸣器,通过按键改变PWM的占空比,改变蜂鸣器的音调。

         首先我们来了解一下PWM的原理,用一个N位的计数器,最大值可表示成2的N次方,最小值为0,计数器以“period”为步进值进行累加,加到最大值之后重新进行下一次的增加。当计数值大于“duty”值的时候,脉冲输出为低,否则输出为高,这样就可以实现脉冲占空比可调的PWM可视,同时“period”可以调节脉冲频率。示意图如下。

                     

         我们来分析一下这PWM输出模块,该模块输入为“period”和“duty”。首先我们给计数器period_cnt设置一个位数,本实验中直接设置为32位,PWM的频率为100HZ,时钟频率为50_000_000HZ,那么每Hz计数为50_000_000/100=500_000,每hz计数为2^32=4294967296,那么每一次需要增加的数为4294967296/500_000=8590,即period的值为8590,当计数器period_cnt大于duty值时,输出为1,否则输出为0即可。相应的我们写出以下代码

/*
	Author:Alawyssun
	Time:2020/03/27
	实现功能:输入PWM的周期和占空比 输出PWM波 
	同时可以接收一个采样率的参数本次试验默认为32位
*/
module ax_pwm
#(
	parameter N = 32 
)
(
    input         clk,
    input         rst,
    input[N - 1:0]period,//每一个step的值  比如100hz  
	 //那么每一个周期的长度为50_000_000/100=500_000
	 //即每一个周期计数到500000为止 但我们计数器是32位 也就是将500000分成2^32份。
	 //用2^32/500000即为每一个时钟周期对应的 计数值
	 //500000/2^32为每一个计数值对应的 时间
    input[N - 1:0]duty,//高电平持续的时间
    output        pwm_out 
    );
 
reg[N - 1:0] period_r;
reg[N - 1:0] duty_r;
reg[N - 1:0] period_cnt;//计数器 相当每次在上一次值的基础上增加一个period的值,当该值大于duty输出为低(即蜂鸣器输出为高电平)
reg pwm_r;
assign pwm_out = pwm_r;
always@(posedge clk or posedge rst)
begin
    if(rst==1)
    begin
        period_r <= { N {1'b0} };
        duty_r <= { N {1'b0} };
    end
    else
    begin
        period_r <= period;
        duty_r   <= duty;
    end
end

always@(posedge clk or posedge rst)
begin
    if(rst==1)
        period_cnt <= { N {1'b0} };
    else
        period_cnt <= period_cnt + period_r;
end

always@(posedge clk or posedge rst)
begin
    if(rst==1)
    begin
        pwm_r <= 1'b0;
    end
    else
    begin
        if(period_cnt >= duty_r)
            pwm_r <= 1'b1;
        else
            pwm_r <= 1'b0;
    end
end

endmodule

        再分析一下主模块, 我们本次实验设置buzzer响铃时间为250ms,当我们检测到按键按下我们将duty改变,同时开启响铃时间计时器,将period和duty传入到PWM模块中,当计时器到达250ms,停止发声,为了达到该目的,我们将PWM输出与开启响铃时间计时器的状态相与即可。相应的代码如下:

/*
	Author:Alawyssun
	Time:2020/03/27
	采样率:32位
	实现功能:实现蜂鸣器输出一定频率的PWM波
	并通过按键实现占空比的切换 每次实现响250ms
	占空比每次增加2^32/10  
*/

module buzzer_pwm_test(
	  input clk,
	  input rst_n,
	  input key1,
	  output buzzer
	 );
parameter IDLE    = 0;
parameter BUZZER  = 1;
wire button_negedge;
wire pwm_out;
reg[31:0] period;
reg[31:0] duty;

reg[3:0] state;
reg[31:0] timer;
assign buzzer = ~(pwm_out & (state == BUZZER));//当输出为高电平 同时是响铃状态的时候响铃

always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
	begin
		period <= 32'd0;
		timer <= 32'd0;
		duty <= 32'd429496729;
		state <= IDLE;
	end
	else
		case(state)
			IDLE:
			begin
				if(button_negedge)
				begin
					period <= 32'd8590;   //  2^32/(50_000_000/100)
					state <= BUZZER;
					duty <= duty + 429496729;//2^32=4294967296
					
				end
			end
			BUZZER:
			begin
				if(timer >= 32'd12_499_999) //响250ms
				begin
					state <= IDLE;
					timer <= 32'd0;
				end
				else
				begin
					timer <= timer + 32'd1;
				end
			end
			default:
			begin
				state <= IDLE;		
			end			
		endcase
end

key_scan key_scan_my
(
    .clk             (clk),
    .rst             (rst_n),
    .key1       (key1),
    .button_out_reg  (button_negedge),
);

ax_pwm ax_pwm_m0(
    .clk      (clk),
    .rst      (~rst_n),
    .period   (period),
    .duty     (duty),
    .pwm_out  (pwm_out)
    );
	
endmodule 

        按键模块仍然保持上次的实验代码即可。

        本次实验结果为按下按键,蜂鸣器响250ms,同时随着按键次数不同,蜂鸣器的频率不断改变。

        本实验的工程文件下载地址为:

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页