PWM的全称为Pulse-Width Modulation(脉冲宽度调制),即调节脉冲的占空比。当输出的脉冲频率一定时,输出的脉冲占空比越大,相当于输出的有效电平越大,这样也就简单实现了由FPGA来控制模拟量。
设计原理框图:
按键消抖,首先采用状态机实现,用状态机做键盘消抖,很好用,不必等待延时,当检测到有按键按下或弹起时能发出相应的键盘消息。设置状态机有 4中状态,A0,A1,A2,A3状态转换图如下:
检测这几个管脚是否为低电平,来判断按键是否被按下
AO: 初始时位于A0状态,当扫描发现有按键按下时,转入到A1状态。当处于A1状态时,当扫描有按键按下并且键值等于A1状态下的键值时,转入到A2状态,否则转入A0状态。当处于A2状态时,当扫描发现有按键按下并且键值等于A3状态下的键值时,转入到A3状态,否则转入A0状态,当处于A3状态时,当扫描发现无键按下时,转入A0状态,同时发出键弹起消息或弹起键的键值入队。
always @(posedge clk) begin case (state) s0: begin key_out<=1‘b1; if(key==1‘b0) state<=s1; else state<=s0; end s1: begin if(key==1‘b0) state<=s2; else state<=s0; end s2: begin if(key==1‘b0) state<=s3; else state<=s0; end s3: begin if(key==1‘b0) begin key_out<=1‘b0; state<=s3; end else begin key_out<=1‘b1; state<=s0; end end default: state<=s0; endcase end
计数器按键消抖:1.检测管教电平是否拉低;2.若检测到低电平,启动计数器,延时20ms左右的时间;3.再次检测管脚是否低电平;4.若还是低电平,确定按键被按下,输出控制信号。在此例程中,最终LED控制实现的功能:按键 Key2 连接到 rst_n 信号,key1 连接到 key1 信号。最终的结果是:当按下 key2 键的时候,系统复位,只有一个 LED 点亮。松开 key2,没有键按下的时候, 四个 LED 交替两灭, 流水灯操作。 当按下 key1 键时, 执行下面语句 else if(key_low) led_r <= 4‘b0;四个灯全亮。
module led ( clk,rst_n,key1, led ); input clk; //时钟信号输入 input rst_n; //复位信号输入 input key1; //按键按下 output[3:0] led; //LED灯点亮 //------------------------键盘消抖程序--------------------------------------------------- reg reg0_key; reg reg1_key; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin reg0_key <= 1‘b1; //将1‘b1赋值给reg0_key寄存器 reg1_key <= 1‘b1; //将1’b1赋值给reg1_key寄存器 end else begin reg0_key <= key1; //将按键输入赋值给reg0_key reg1_key <= reg0_key; //根据非阻塞赋值的原理,reg1_key 存储的值是 reg0_key 上一个时钟的值 end end //当寄存器 key1 由 1 变为 0 时,led_an 的值变为高,维持一个时钟周期 wire key_an; assign key_an = reg1_key & ( ~reg0_key); //用来检测下降沿的典型程序 //--------------------------------------------------------------------------- reg[19:0] cnt_key; //计数寄存器 //检测完下降沿,启动计时器,延时20ms always @ (posedge clk or negedge rst_n) begin if (!rst_n) cnt_key <= 20‘d0; //异步复位 else if(key_an) cnt_key <=20‘d0; else cnt_key <= cnt_key + 1‘b1; end reg reg_low; reg reg1_low; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin reg_low <= 1‘b1; end else if(cnt_key == 20‘hfffff) begin reg_low <= key1; //cnt == 20‘hfffff,20ms end end always @(posedge clk or negedge rst_n) begin if (!rst_n) reg1_low <= 1‘b1; else reg1_low <= reg_low; end //--------------------------------------------------------------------------- //当寄存器 reg_low 由 1 变为 0 时,key_low 的值变为高,维持一个时钟周期 wire key_low = reg1_low & ( ~reg_low); //===============LED 控制================================== reg[21:0] cnt; // always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 22‘b0; else cnt <= cnt + 1‘b1; end reg enable_r; always @(posedge clk or negedge rst_n) begin if(!rst_n) enable_r <= 1‘b0; else if (cnt == 22‘h3fffff) enable_r <= 1‘b1; else enable_r <= 1‘b0; end wire enable; assign enable = enable_r; reg[3:0] led_r; always @(posedge clk or negedge rst_n) begin if(!rst_n) led_r <= 4‘b0111; else if(key_low) led_r <= 4‘b0; else if(enable && !key_low) led_r <= {led_r[0],led_r[3:1]}; else ; end wire[3:0] led; assign led = led_r; endmodule
回到PWM控制灯光亮暗,通过时钟计时器的计数与脉宽的计数比较大小,去定义灯光的亮暗。当时钟计数器到达设定的数值时,如果脉宽加满则溢出清0,并在此时判断按键有无按下,若按下,则调节脉宽的宽度,来进行下一轮的占空比比较,若脉宽未满,亦将其加满。对应的程序为:
module pwm(clk,reset,key,led); input clk,reset,key; output led; reg pwm_out; reg key_out; parameter s0=2‘b00,s1=2‘b01,s2=2‘b10,s3=2‘b11; reg [1:0] state; reg [31:0] clk_counter; reg [9:0] pwm_counter; reg flag; /******************按键消抖**************************/ always @(posedge clk) begin case (state) s0: begin key_out<=1‘b1; if(key==1‘b0) state<=s1; else state<=s0; end s1: begin if(key==1‘b0) state<=s2; else state<=s0; end s2: begin if(key==1‘b0) state<=s3; else state<=s0; end s3: begin if(key==1‘b0) begin key_out<=1‘b0; state<=s3; end else begin key_out<=1‘b1; state<=s0; end end default: state<=s0; endcase end always @(posedge clk) begin clk_counter<=clk_counter+1‘b1; if (clk_counter[13:4]<pwm_counter) pwm_out=1; else pwm_out=0; if (clk_counter[15]==1‘b1) begin if (flag==1‘b1) begin flag<=1‘b0; if (key_out==1‘b0) pwm_counter<=(pwm_counter+10‘b0000000011); else pwm_counter<=pwm_counter; end end else flag<=1‘b1; end assign led=pwm_out; endmodule
最后板子亮灯,显示平均电压。