【FPGA——Cyclone Ⅳ学习笔记】四.按键控制PWM蜂鸣器(EP4CE6F17C8)

一.原理图

【FPGA——Cyclone Ⅳ学习笔记】四.按键控制PWM蜂鸣器(EP4CE6F17C8)_第1张图片
【FPGA——Cyclone Ⅳ学习笔记】四.按键控制PWM蜂鸣器(EP4CE6F17C8)_第2张图片
由蜂鸣器的原理图可看出,当I/O口(C11)输出低电平时蜂鸣器响!

二.代码和注释

1.key_debounce.v

按键消抖代码,前面介绍过了,此处不再说明。

module key_debounce(
	input              sys_clk,          //外部50M时钟
	input              sys_rst_n,        //外部复位信号,低有效
	
	input  [2:0]       key,              //外部按键输入,按下后为低电平
	output reg  	  	 key_flag,         	   //按键数据有效信号,即表示延时结束,按键已稳定
	output reg  [2:0]  key_value       //按键消抖后的数据  
	);

//reg define    
reg [19:0] delay_cnt;	//消抖延时的计数器
reg [2:0]  key_reg;		//按键值存储

//*****************************************************
//**                    main code
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin 
	if (!sys_rst_n) 
	begin 
		key_reg   <= 3'b111;	//按键值复位,全为高电平
		delay_cnt <= 19'd0;		//计数器清零
	end
	else 
	begin
		key_reg <= key;	//非阻塞赋值,因此下行if判断中的key_reg仍为前一次的数据,而非此次的key
		if(key_reg != key)	//一旦检测到按键状态发生变化(有按键被按下或释放)
			delay_cnt <= 19'd1_000_000;  //给延时计数器重新装载初始值(计数时间为20ms)
		else if(key_reg == key) 
		begin  //在按键状态稳定时,计数器递减,开始20ms倒计时
			if(delay_cnt > 19'd0)
				delay_cnt <= delay_cnt - 1'b1;
			else
				delay_cnt <= delay_cnt;
		end           
	end   
end

always @(posedge sys_clk or negedge sys_rst_n) 
begin 
	if (!sys_rst_n) 
	begin 
		key_flag  <= 1'b0;	
		key_value <= 3'b111;	          
	end	 
	else 
	begin
		if(delay_cnt == 19'd1) //减到1而不是0的原因是:复位情况和无按键按下时cnt恒为零,则key_flag会一直为1
		begin   //同时,当计数器递减到1时,说明按键稳定状态维持了20ms
			key_flag  <= 1'b1; //此时消抖过程结束,给出一个时钟周期的标志信号
			key_value <= key;  //并寄存此时按键的值
		end
		else 
		begin
			key_flag  <= 1'b0;	//延时未到,不给出有效信号
			key_value <= key_value; 
		end  
	end   
end
   
endmodule 

2.buzzer_pwm.v

module buzzer_pwm
#(
	parameter N=16		//可给外部调用的常数参数,即在实例化时可传递参数
)
(
	input 			sys_clk,
	input 			sys_rst_n,
	input [N-1:0]  period,	//用于控制计数的速度,从而控制PWM的频率
	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;	//PWM计数器
reg pwm_r;

assign pwm_out=pwm_r;

always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		period_r<={ N{1'b0} };
		duty_r<={ N{1'b0} };
	end
	
	else
	begin
		period_r<=period;	//实时更新PWM频率
		duty_r<=duty;	//实时更新PWM脉宽
	end
end

always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
		period_cnt<={ N{1'b0} };
	else
	period_cnt<=period_cnt+period_r;	//PWM计数器,每个时钟上升沿以设定速度(period_r)计数
end

always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		pwm_r<=1'b0;
	end
	
	else
	begin
		if(period_cnt>=duty_r)	//输出PWM
			pwm_r<=1'b1;	//计数器数值大于比较值时输出高电平
		else
			pwm_r<=1'b0;	//计数器数值小于比较值时输出低电平
	end
end
endmodule	

【FPGA——Cyclone Ⅳ学习笔记】四.按键控制PWM蜂鸣器(EP4CE6F17C8)_第3张图片
此图即为PWM产生的原理,通过一个32位的计数器与一个32位的设定值进行比较。当计数器的值小于比较值时,输出高电平;当计数器的值大于比较值时,输出低电平,从而产生PWM波。
计数器的计数速度period_r可以自行设定

通过上图不难发现:
1.比较值duty越大,高电平持续时间越长,即占空比越大。因此控制比较值的大小就可以控制占空比,从而控制蜂鸣器的音量。

2.由于计数器在每个时钟周期(50MHz->0.02ns)增加period_r。当计数器的位数固定时,计数速度period_r越大,PWM的周期越小,频率越大。因此控制计数速度period_r的大小可以控制PWM频率,从而控制蜂鸣器的音调。

3.key_buzzer_test.v

此代码时实验现象是:按键按下后,蜂鸣器会对应响250ms,KEY1改变蜂鸣器音量,KEY2改变蜂鸣器音调。

module key_buzzer_test
(
	input sys_clk,
	input sys_rst_n,
	input [2:0] key,
	output buzzer
);

parameter IDLE=0;	//蜂鸣器关闭状态
parameter BUZZER=1;	//蜂鸣器打开状态

wire [2:0] key_value;	//按键值
wire key_flag;		//按键有效标志
wire pwm_out;

reg [31:0] period;	//PWM计数速度
reg [31:0] duty;	//比较值
reg[31:0] timer;	//250ms延时定时器
reg state;

assign buzzer=~(pwm_out&(state==BUZZER));	//PWM为高电平且蜂鸣器处于打开状态时为蜂鸣器的I/O口输出PWM

always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		period<=32'd8590;
		timer<=32'd0;
		duty<=32'd429496729;
		state<=IDLE;
	end
	
	else
	begin
		case(state)
			IDLE:
			begin
				if(key_flag&&key_value[0]==0)	//KEY1控制音量(PWM脉宽)
				begin
					period<=32'd8590;	//PWM计数速度固定
					state<=BUZZER;		//打开蜂鸣器
					duty<=duty+32'd429496729;	//比较值增加,
				end
				else if(key_flag&&key_value[1]==0)	//KEY2控制音调(PWM频率)
				begin
					period<=period+32'd17180;	//PWM计数速度增加,PWM频率增加
					state<=BUZZER;
					duty<=32'd429496729;	//比较值固定
				end
				else;						
			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
		endcase
	end
end

key_debounce u_key_debounce
(
	.sys_clk   (sys_clk),
	.sys_rst_n (sys_rst_n),
	.key		  (key),
	.key_flag  (key_flag),
	.key_value (key_value),
);

buzzer_pwm#
(
   .N(32)	//为模块的常量参数进行参数传递
)
u_buzzer_pwm
(
	.sys_clk   (sys_clk),
	.sys_rst_n (sys_rst_n),
	.period 	  (period),
	.duty		  (duty),
	.pwm_out	  (pwm_out)
);

endmodule

你可能感兴趣的:(FPGA学习笔记)