FPGA学习——PWM实现呼吸流水灯(附源码)

文章目录

  • 一、PWM简介
    • 1.1 PWM定义
    • 1.2 PWM参数
  • 二、Verilog实现PWM呼吸灯
  • 三、实现效果
  • 四、总结

一、PWM简介

1.1 PWM定义

PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。
脉宽调制(PWM,Pulse Width Modulation)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。

1.2 PWM参数

  • pwm的频率:是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);也就是说一秒钟PWM有多少个周期

  • 单位: Hz

  • 表示方式: 50Hz 100Hz

  • pwm的周期:T=1/f(周期=1/频率),例如50Hz = 20ms 一个周期,如果频率为50Hz ,也就是说一个周期是20ms
    那么一秒钟就有 50次PWM周期

  • 占空比:是一个脉冲周期内,高电平的时间与整个周期时间的比例

  • 单位: % (0%-100%)

  • 表示方式:20%

  • 脉宽时间占总周期时间的比例,就是占空比

二、Verilog实现PWM呼吸灯

本次实验的主要难点在于如何实现PWM中不同占空比的产生,博主提供的一种思路如下:
由于时钟频率为50MHz,也就是计数50_000_000次为1s,于是我们便可以设置三个计数器,分别为TIME_US、TIME_MS、TIME_1S,

微秒计数器计数50次清零,微秒计数器每次清零时,毫秒计数器+1,毫秒计数器计数至1000次清零,毫秒计数器清零时,1S计数器进1,1S计数器计满1000清零(50×1000×1000 = 50_000_000恰好等于1s时钟震动次数)。
而PWM的产生可以参考以下表达式:

cnt_1s > cnt_ms ? 1’b1 : 1’b0

可以想象,当1S计数器刚进1时,MS计数器会有999次计数大于1S计数器,此时我们只会输出1/1000的高电平,而随着1S计数器的值越来越大(MS计数器计数远快于1S计数器,会不断清零),当1S计数器值为999时,在新一轮的MS计数器计数时,我们便有999/1000的高电平,直至1S计数器计满清零后再次循环,以此我们便获得了渐进式的PWM

具体代码如下:

module pwm_led (
    input   wire        clk,
    input   wire        rst_n,

    output  reg [3:0]   led
);

parameter TIME_US = 6'd50;
parameter TIME_MS = 10'd1000;
parameter TIME_1S = 10'd1000;

reg [5:0]   cnt_us;
reg [9:0]   cnt_ms;
reg [9:0]   cnt_1s;

//us计数器
wire add_cnt_us;//us计数器开始标志
wire end_cnt_us;//us计数器结束标志

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_us <= 1'b0;
    end
    else if(add_cnt_us)begin
       if(end_cnt_us)begin //end_cnt_us为1,计满清零
        cnt_us <= 1'b0;
       end
       else begin
        cnt_us <= cnt_us + 1'b1;
       end 
    end
    else begin
        cnt_us <= cnt_us;
    end
end

assign add_cnt_us = 1'b1;
assign end_cnt_us = add_cnt_us && cnt_us == TIME_US;

//ms计数器
wire add_cnt_ms;//us计数器开始标志
wire end_cnt_ms;//us计数器结束标志

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_ms <= 1'b0;
    end
    else if(add_cnt_ms)begin
       if(end_cnt_ms)begin //end_cnt_ms为1,计满清零
        cnt_ms <= 1'b0;
       end
       else begin
        cnt_ms <= cnt_ms + 1'b1;
       end 
    end
    else begin
        cnt_ms <= cnt_ms;
    end
end

assign add_cnt_ms = end_cnt_us;
assign end_cnt_ms = add_cnt_ms && cnt_ms == TIME_MS;

//1s计数器
wire add_cnt_1s;//1s计数器开始标志
wire end_cnt_1s;//1s计数器结束标志

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_1s <= 1'b0;
    end
    else if(add_cnt_1s)begin
       if(end_cnt_1s)begin //end_cnt_1s为1,计满清零
        cnt_1s <= 1'b0;
       end
       else begin
        cnt_1s <= cnt_1s + 1'b1;
       end 
    end
    else begin
        cnt_1s <= cnt_1s;
    end
end

assign add_cnt_1s = end_cnt_ms;
assign end_cnt_1s = add_cnt_1s && cnt_1s == TIME_1S;

//PWM亮灭控制信号
reg flag;
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flag <= 1'b0;
    end
    else if(end_cnt_1s)begin
        flag <= ~flag;
    end
    else begin
        flag <= flag;
    end
end

//流水灯切换状态
parameter MAX2 = 100_000_000;
reg [27:0]   cnt2;
reg [1:0]   cstate;

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 1'b0;
        cstate <= 1'b0;
    end
    else if(cnt2 == MAX2 - 1'b1)begin
        cnt2 <= cnt2 + 1'b1;
        cstate <= cstate + 1'b1;
    end
    else begin
        cnt2 <= cnt2 + 1'b1;
        cstate <= cstate;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        led <= 4'b0001;
    end
    else begin
        case(cstate)
        2'd0    :   begin
            if(!flag)begin
                led <= cnt_1s > cnt_ms ? 4'b0001 : 4'b0000;
            end
            else if(flag)begin
                led <= cnt_1s < cnt_ms ? 4'b0001 : 4'b0000;
            end
        end
        2'd1    :   begin
            if(!flag)begin
                led <= cnt_1s > cnt_ms ? 4'b0010 : 4'b0000;
            end
            else if(flag)begin
                led <= cnt_1s < cnt_ms ? 4'b0010 : 4'b0000;
            end
        end
        2'd2    :   begin
            if(!flag)begin
                led <= cnt_1s > cnt_ms ? 4'b0100 : 4'b0000;
            end
            else if(flag)begin
                led <= cnt_1s < cnt_ms ? 4'b0100 : 4'b0000;
            end
        end
        2'd3    :   begin
            if(!flag)begin
                led <= cnt_1s > cnt_ms ? 4'b1000 : 4'b0000;
            end
            else if(flag)begin
                led <= cnt_1s < cnt_ms ? 4'b1000 : 4'b0000;
            end
        end
        endcase
    end
end


endmodule

三、实现效果

如有不会创建项目,烧录,请查看博主以前的博文

四、总结

本次实验主要难点在于如何实现不同占空比的PWM,解决这一难点后实际上实验就变成了普通的流水灯实验。

你可能感兴趣的:(fpga开发,学习)