FPGA基础入门篇(九)使用 Verilog 实现 LED 呼吸灯效果

FPGA基础入门篇(九)使用 Verilog 实现 LED 呼吸灯效果

呼吸灯为常见的数字IC设计案例,也比较简单,主要是关于呼吸灯的原理需要理解。常见的应用在手机的呼吸灯,这里我们采用硬件描述语言来实现LED呼吸灯的效果,即在1s内,LED灯由暗逐渐变亮,再1s内再由亮逐渐变暗。

一、设计原理

呼吸灯设计原理归结为对于分频占空比的应用,就是先分频,然后再设置占空比的设计。占空比也就是控制LED暗亮的时间达到具有呼吸灯的效果。

首先要引入 脉冲宽度调制(PWM) 的概念,LED的点亮和熄灭,是电平高低变换的结果,可以将一高一低看作一个周期,每个周期一亮一灭,会显示为LED的闪烁,当周期很短,也就是频率很高时,这种闪烁将不被肉眼识别,会让人产生LED连续发光的感觉。在一个周期内,高电平时长与一个周期时长的比叫做占空比占空比越高,相当于通过LED的电流就越大,视觉上的感觉就越亮。说到这里,应该就有了做呼吸灯的思路,就是改变占空比!让占空比小幅度有级提升,就会有LED无级变亮的感觉。反之就会变暗。占空比越高,亮度越亮。

首先将1s 分为1000份(1ms),然后在1ms内在继续分为1000份(1us),每一个1ms内,依次增加亮灯时间
即:
第1个1ms内亮灯1us
第2个1ms内亮灯2us
第3个1ms内亮灯2us
… …
第1000个1ms内亮灯1000us

二、基于Verilog设计
module led_breath(
input clk_i,    //100MHz
input rst_n_i,
output led_o
);

//1s计数器
reg flag_1s;
reg [26:0] cnt_1s;
always @(posedge clk_i)
begin
        if (! rst_n_i)begin
            cnt_1s <= 27'b0;
            flag_1s <= 1'b0;
        end
        else if(cnt_1s == 27'd100_000_000 ) begin
            cnt_1s <= 27'b0;
            flag_1s <= 1'b1;
        end
        else begin
            cnt_1s <= cnt_1s + 1'b1;
            flag_1s <= 1'b0;
        end
end

//1ms计数器
reg flag_1ms;
reg [17:0] cnt_1ms;
always @(posedge clk_i)
begin
        if (! rst_n_i)begin
            cnt_1ms <= 18'b0;
            flag_1ms <= 1'b0;
        end
        else if(cnt_1ms == 18'd100_000 ) begin
            cnt_1ms <= 18'b0;
            flag_1ms <= 1'b1;
        end
        else begin
            cnt_1ms <= cnt_1ms + 1'b1;
            flag_1ms <= 1'b0;
        end
end

//1us计数器
reg flag_1us;
reg [6:0] cnt_1us;
always @(posedge clk_i)
begin
        if (! rst_n_i)begin
            cnt_1us <= 7'b0;
            flag_1us <= 1'b0;
        end
        else if(cnt_1us == 7'd100) begin
            cnt_1us <= 7'b0;
            flag_1us <= 1'b1;
        end
        else begin
            cnt_1us <= cnt_1us + 1'b1;
            flag_1us <= 1'b0;
        end
end

//计数多少个ms
reg [9:0] number_ms;
always @(posedge clk_i)
begin
        if (! rst_n_i )begin
            number_ms <= 10'd0;
        end
        else begin
                if(number_ms == 10'd1000)begin
                    number_ms <= 10'd0;
                end
                else if(flag_1ms == 1'b1) begin
                    number_ms <= number_ms + 1'b1;
                end
                else begin
                    number_ms <= number_ms;
                end
        end
end



//计数多少个us
reg [9:0] number_us;
always @(posedge clk_i or negedge rst_n_i)
begin
        if (! rst_n_i )begin
            number_us <= 10'd0;
        end
        else begin
                if(number_us == 10'd1000)begin
                    number_us <= 10'd0;
                end
                else if(flag_1us == 1'b1) begin
                    number_us <= number_us + 1'b1;
                end
                else begin
                    number_us <= number_us;
                end
        end
end

//module of led breath
//
wire led_flag0; //由暗逐渐变亮
wire led_flag1; //由亮逐渐变暗
assign  led_flag0 = (number_ms > number_us)? 1: 0;
assign  led_flag1 = (number_ms > number_us)? 0: 1;

assign led_o = (led_flag)? led_flag0 : led_flag1;

reg led_flag;		//每隔1s 翻转一次信号
always @(posedge clk_i or negedge rst_n_i)
begin
        if (! rst_n_i)begin
                led_flag <= 1'b1;
        end
        else if (flag_1s == 1'b1) begin
                led_flag <= ~ led_flag;
        end
        else begin
                led_flag <= led_flag;
        end

end


endmodule

仿真结果
FPGA基础入门篇(九)使用 Verilog 实现 LED 呼吸灯效果_第1张图片
FPGA基础入门篇(九)使用 Verilog 实现 LED 呼吸灯效果_第2张图片
当复位信号复位时,ms和us计数器均开始计数.
1us,1ms的时间均通过计数器来控制
每过1us,us计数器累加(number_us),一直到1ms累加到1000us即计数清零。
每过1ms,ms计数器累加(number_ms),一直到1ms累加到1000ms即计数清零。

当 number_us < number_ms,led_flag被拉高成高电平。这样在1s内,有1000次 led_flag 被拉高的,并且每次时间间隔一次增加。(0到999)

经过上板验证,确实可以实现LED灯有呼吸效果。

关于使用C语言程序来控制GPIO实现LED呼吸灯的效果可以参考下面一篇博文,主要介绍使用ZYNQ PS部分来实现。

你可能感兴趣的:(数字IC设计-FPGA)