大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
今天給大俠帶來基于FPGA的呼吸燈設計,附源碼,獲取源碼,請在“FPGA技術(shù)江湖”公眾號內(nèi)回復“呼吸燈設計源碼”,可獲取源碼文件。話不多說,上貨。
設計背景
呼吸燈廣泛應用于手機之上,并成為各大品牌新款手機的賣點之一。如果手機里面有未處理的通知,比如說未接來電,未查收的短信等等,呼吸燈就會在控制之下完成由亮到暗的逐漸變化,感覺好像是人在呼吸,起到一個通知提醒的作用。
設計原理
關(guān)于呼吸燈設計實現(xiàn)的理論主要是PWM有關(guān)知識。PWM(Pluse Width Modulation)脈沖寬度調(diào)制,是一種對模擬信號電平進行數(shù)字編碼的方法。通過高分辨率計數(shù)器的使用,方波的占空比被調(diào)制用來對一個具體模擬信號的電平進行編碼。并廣泛應用在從測量、通信、功率控制與變換及LED照明等許多領(lǐng)域中。顧名思義,就是占空比可調(diào)的信號,那么什么是占空比呢?
占空比(Duty Cycle or Duty Ratio),可以解釋為,在一脈沖序列中(方波),正脈沖序列的持續(xù)時間與脈沖總周期的比值。也可理解為,電路釋放能量的有效時間與總釋放時間的比值。
PWM是怎樣實現(xiàn)調(diào)光呢?想要調(diào)節(jié)LED的亮度變化,實則是調(diào)節(jié)控制流經(jīng)LED的電流。電流增大則LED亮度增強,反之減弱。但由于電流為模擬信號,所以這時就用到了PWM。正如下圖所示:
使用一系列等幅不等寬的脈沖來代替一個正弦波,脈沖的寬度根據(jù)正弦波a的幅度變化,幅度高,則脈沖寬,反之。
多數(shù)負載需要的PWM調(diào)制頻率都高于10Hz,要想實現(xiàn)呼吸燈的效果,必須提高調(diào)制頻率,通常調(diào)制頻率為1Khz~200Khz之間。在LED控制中PWM作用于電源部分,脈寬調(diào)制的脈沖頻率通常大于100Hz,人眼就不會感到閃爍。這里我們?nèi)WM調(diào)制頻率為1KHz,PWM周期為1ms。
脈沖頻率一定時,輸出脈沖的占空比越大,相當于輸出的有效電平越大,隨著占空比的不同,LED的亮度也將不同。如占空比為0時,則LED不亮,為100%時,則LED最亮,我們讓占空比從0~100%變化,再從100%~0不斷變化,則就可實現(xiàn)呼吸燈效果。
本設計呼吸燈的一個周期為2s,分為占空比增“吸”和占空比減“呼”兩種模式,每個為1s,一個PWM周期為2ms,所以每個模式包含1000個PWM周期,將每個PWM周期分為1000份,即每個時間段2us。
設計框架
設計框架圖:50M時鐘
設計代碼
設計模塊huxi_led_state代碼:
module huxi_led_state(clk,led,rst_n);
input clk;
input rst_n;
output reg led;
parameter T = 100_000;
localparam s0 = 1'b0;
localparam s1 = 1'b1;
reg [25:0] lw;
reg [25:0] hw;
reg [16:0] count;
// 產(chǎn)生2MS的脈沖
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
count <= 1'b0;
end
else
begin
if(count == T - 1)
begin
count <= 1'b0;
end
else
begin
count <= count + 1'b1;
end
end
wire flag;
assign flag =(count == T - 1) ? 1'b1:1'b0;
reg state;
// 通過在一個周期中加減高低電平的時間來產(chǎn)生PWM波
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
lw <= T - 100;
hw <= 100;
state <= 1'b0;
end
else
begin
case (state)
s0:begin
if(flag && (lw > 100)) //判斷低電平的時間
begin
lw <= lw - 100;
hw <= hw + 100;
state <= s0;
end
else if(flag && (lw == 100))
begin
hw <= hw - 100;
lw <= lw + 100;
state <= s1;
end
else
begin
hw <= hw;
lw <= lw;
state <= s0;
end
end
s1:begin
if(flag && (hw > 100)) //判斷高電平的時間
begin
hw <= hw - 100;
lw <= lw + 100;
state <= s1;
end
else if(flag && (hw ==100))
begin
hw <= hw + 100;
lw <= lw - 100;
state <= s0;
end
else
begin
hw <= hw;
lw <= lw;
state <= s1;
end
end
default : state <= s0;
endcase
end
reg [25:0] cnt;
reg sum;
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
sum <= 1'b0;
led <= 1'b1;
cnt <= 1'b0;
end
else
case (sum)
s0:begin
if(cnt < hw -1 )
begin
led <= 1'b0;
cnt <= cnt + 1'b1;
end
else
begin
cnt <= 1'b0;
sum <= s1;
end
end
s1:begin
if(cnt < lw -1)
begin
led <= 1'b1;
cnt <= cnt + 1'b1;
end
else
begin
cnt <= 1'b0;
sum <= s0;
end
end
default:sum <= s0;
endcase
endmodule
仿真測試
測試模塊代碼:
`timescale 1ns/1ps
module huxi_led_state_tb();
reg clk;
reg rst_n;
wire led;
parameter T = 100_000;
initial begin
clk = 1'b1;
rst_n = 1'b0;
#200.1 rst_n = 1'b1;
end
always #10 clk = ~ clk;
huxi_led_state huxi_led_state_date(
.clk(clk),
.led(led),
.rst_n(rst_n)
);
endmodule
仿真圖:
仿真中可以看到點亮led等高電平在不停的增高,然后會降低,通過驗證我們的設計是正確的。