zoukankan      html  css  js  c++  java
  • PWM控制灯亮暗的verilog实现

    PWM的全称为Pulse-Width Modulation(脉冲宽度调制),即调节脉冲的占空比。当输出的脉冲频率一定时,输出的脉冲占空比越大,相当于输出的有效电平越大,这样也就简单实现了由FPGA来控制模拟量。

    设计原理框图:

    image

    按键消抖,首先采用状态机实现,用状态机做键盘消抖,很好用,不必等待延时,当检测到有按键按下或弹起时能发出相应的键盘消息。设置状态机有 4中状态,A0,A1,A2,A3状态转换图如下:

    image

    image检测这几个管脚是否为低电平,来判断按键是否被按下

    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

    最后板子亮灯,显示平均电压。

    image

  • 相关阅读:
    单例模式-Singlleton
    C#中静态与非静态方法比较
    关于orcale的数据库脚本,记录下来,方便自己以后用到查找
    关于Oracle和SQLServer数据库在.net中拼接数据库语句的不同
    Oracle数据类型与.NET中的对应关系
    Got a packet bigger than 'max_allowed_packet' bytes
    .NET、C#和ASP.NET三者之间的区别(转)
    The use specified as definer('root'@'%') does not exist的解决办法
    app.config .exe.config .vshost.exe.config配置
    python学习:(3)自动化表单提交
  • 原文地址:https://www.cnblogs.com/Fun-with-FPGA/p/4724650.html
Copyright © 2011-2022 走看看