基於FPGA的呼吸燈設計


1. 項目簡介

  呼吸燈,指的是一個LED從暗到亮,從亮到暗逐漸變化,如此循環反復,就像人的呼吸一樣有節奏。它采用PWM的方式,在固定的頻率下,通過調整占空比的方式來控制LED燈亮度的變化。

PWM(Pulse Width Modulation),即脈沖寬度調制,是一種對模擬信號電平進行數字編碼的方法。通過高分辨率計數器的使用,方波的占空比被調制用來對一個具體模擬信號的電平進行編碼,被廣泛應用於測量、通信、功率控制等領域。

2. 設計要求

  實現呼吸燈的效果,即由滅漸亮,然后再由亮漸滅,要求一個完整的呼吸過程包含:呼氣(2秒)和吸氣(2秒),周期為4秒。系統框圖如下圖所示,其中,clk為系統時鍾、50Mhz,rst_n為系統復位,低電平有效,led[3:0]為輸出led燈,高電平點亮。

3. 設計原理

  LED的亮度與流過的電流成正比。不同的脈沖占空比的方波輸出后加在LED上,LED燈就會顯示不同的亮度,通過不斷地調節方波的占空比,從而實現LED燈亮度的調節。在一定的頻率下,如果占空比是0,則LED不亮;如果占空比是100%,則LED最亮;如果占空比剛好是50%,則LED亮度適中。如果我們讓占空比從0%-100%變化,再從100%-0%不斷變化,就可以實現LED一呼一吸的效果。其設計思路如下:

(1)一個完整的呼吸過程包含:呼氣(2秒)和吸氣(2秒),周期為4秒。

(2)考慮呼氣的過程,就是讓led燈要有亮滅的變化,從暗逐漸到亮。這里必須要將2秒的時間進行拆分,為什么?

(3)為什么要將2秒進行拆分?因為如果不拆分,只有一個固定不變的占空比,無論占空比是多少,led始終是固定的,即led的亮度也是固定的。

(4)如何拆分?理論上:我們可以無限的細分,拆分的份數越多,led的變化就越流暢;實際上:由於人的眼睛有視覺暫留效應,對應光的變化,人眼的分辨率是30-40ms,所以我們無限細分是無意義的。

參數取值:為了led的亮度變化比較流暢,我們取2ms的間隔將2s進行拆分,也就是每隔20ms我們給led燈一個新的亮度,

這樣我們人眼就能分辨出這個亮度的變化,則 cnt * T = 2000ms,T=2ms,cnt=1000,即拆分1000份。

(5)20ms周期連續變化示意圖

由步驟4得知,我們將2秒拆分為1000份的2ms,也就是2ms周期的波形在時序圖上看是會重復1000次;為了體現led燈亮度,每一份20ms波形的占空比都不能相同,且必須是連續增加/減小的。如下圖示意

(6)由步驟5得知,波形重復1000次,也就是2ms的時間內,我們一共有1000個狀態,每一個狀態就是一個占空比t * 1000 = 20ms,則 t = 2us,由公式計算得知,2us的占空比為2/2*1000 %100 = 1‰

(7)從上面的描述中,我們可以總結出,要做出呼吸燈,我們需要利用三個計數器分別計數(類似於數字鍾中秒、分鍾、小時)。第一個計數器cnt_2us用來對2us的計數,第二個計數器cnt_2ms實現對2ms計數,第三個計數器實現對2s計數。三個計數器中,利用后面兩個計數器cnt_2ms和cnt_2s的大小進行比較,來改變一個時鍾周期內的占空比。

4. 設計實現

module led_breath(
    input           wire                clk,        //系統時鍾,50Mhz
    input           wire                rst_n,    //系統復位,低電平有效
    output         wire     [3:0]        led        //led燈,高電平點亮
);
    
    parameter      T_2us     = 100;
    parameter      T_2ms      = 100_000;
    parameter      T_2s      = 100_000_000;
    
    reg    [6:0]        cnt_2us;
    reg     [9:0]        cnt_2ms;
    reg    [9:0]        cnt_2s;
    reg                valid;
    wire                wave;
    
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            cnt_2us <= 7'd0;
        else
            if(cnt_2us < T_2us - 1'b1)
                cnt_2us <= cnt_2us + 1'b1;
            else
                cnt_2us <= 7'd0;
    end
    
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            cnt_2ms <= 10'd0;
        else
            if(cnt_2us == T_2us - 1'b1)
                if(cnt_2ms < T_2ms/T_2us - 1'b1)
                    cnt_2ms <= cnt_2ms + 1'b1;
                else
                    cnt_2ms <= 10'd0;
            else
                cnt_2ms <= cnt_2ms;
    end
    
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            cnt_2s <= 10'd0;
        else
            if((cnt_2us == T_2us - 1'b1)&&(cnt_2ms == T_2ms/T_2us - 1'b1))
                if(cnt_2s < T_2s/T_2ms - 1'b1)
                    cnt_2s <= cnt_2s + 1'b1;
                else
                    cnt_2s <= 10'd0;
            else
                cnt_2s <= cnt_2s;
    end
    
    assign wave = (cnt_2s > cnt_2ms) ? 1'b1 : 1'b0;
    
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            valid <= 1'b0;
        else
            if((cnt_2us == T_2us - 1'b1)&&(cnt_2ms == T_2ms/T_2us - 1'b1)&&(cnt_2s == T_2s/T_2ms - 1'b1))
                valid <= ~valid;
            else
                valid <= valid;
    end
    
    assign led[0] = (valid == 1'b0) ? wave : ~wave;
    assign led[1] = (valid == 1'b0) ? wave : ~wave;
    assign led[2] = (valid == 1'b0) ? wave : ~wave;
    assign led[3] = (valid == 1'b0) ? wave : ~wave;
    
endmodule 

5. 仿真測試

`timescale 1ns/1ps
module led_breath_tb();
    reg                        clk;
    reg                        rst_n;
    wire        [3:0]            led;
        
    defparam led_breath_inst.T_2us = 10;
    defparam led_breath_inst.T_2ms = 100;
    defparam led_breath_inst.T_2s  = 1000;
    
    led_breath led_breath_inst(
        .clk            (clk),
        .rst_n        (rst_n),
        .led            (led)
    );
    
    initial begin
        repeat(1000*10)begin
            clk = 1'b0;
            #10;
            clk = 1'b1;
            #10;
        end
    end
    
    initial begin
        rst_n = 1'b0;
        #21;
        rst_n = 1'b1;
    end

endmodule

 

注:PWM波一般可以使用單片機或者專用PWM波生成電路產生(一般簡單的單片機就可以,然后選擇PWM控制器路數比較多的即可),也有很多用CPLD或者FPGA來實現PWM波的,但是實際應用中,由於FPGA成本和功耗上對單片機不具備優勢,所以實際產品中很少會用FPGA或者CPLD芯片。

主要來源於:陪您一起學習FPGA-郝旭帥團隊_嗶哩嗶哩_bilibili


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM