本原創教程由芯驛電子科技(上海)有限公司(ALINX)創做,版權歸本公司全部,如需轉載,需受權並註明出處。node
AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4EV-P/AXU5EV-E/AXU5EV-P /AXU9EG/AXU15EG異步
實驗Vivado工程爲「pwm_led」。測試
本文主要講解使用PWM控制LED,實現呼吸燈的效果。spa
以下圖所示,用一個N比特的計數器,最大值能夠表示爲2的N次方,最小值0,計數器以「period」爲步進值累加,加到最大值後會溢出,進入下一個累加週期。當計數器值大於「duty」時,脈衝輸出高,不然輸出低,這樣就能夠完成圖中紅色線所示的脈衝佔空比可調的脈衝輸出,同時「period」能夠調節脈衝頻率,能夠理解爲計數器的步進值。設計
PWM脈寬調製示意圖3d
不一樣的脈衝佔空比的方波輸出後加在LED上,LED燈就會顯示不一樣的亮度,經過不斷地調節方波的佔空比,從而實現LED燈亮度的調節。code
PWM模塊設計很是簡單,在上面的原理中已經講到,這裏再也不說原理。orm
信號名稱 | 方向 | 說明 |
clk | in | 時鐘輸入 |
rst | in | 異步復位輸入,高復位 |
period | in | PWM脈寬週期(頻率)控制。period = PWM輸出頻率 * (2 的N次方) / 系統時鐘頻率。顯然N越大,頻率精度越高。 |
duty | in | 佔空比控制,佔空比 = duty / (2的N次方)* 100% |
PWM模塊(ax_pwm)端口blog
`timescale1ns/1ps module ax_pwm #( parameter N =16//pwm bit width ) ( input clk, input rst, input[N -1:0]period, //pwm step value input[N -1:0]duty, //duty value output pwm_out //pwm output ); reg[N -1:0] period_r; //period register reg[N -1:0] duty_r; //duty register reg[N -1:0] period_cnt; //period counter reg pwm_r; assign pwm_out = pwm_r; always@(posedge clk orposedge rst) begin if(rst==1) begin period_r <={ N {1'b0}}; duty_r <={ N {1'b0}}; end else begin period_r <= period; duty_r <= duty; end end //period counter, step is period value always@(posedge clk orposedge rst) begin if(rst==1) period_cnt <={ N {1'b0}}; else period_cnt <= period_cnt + period_r; end always@(posedge clk orposedge rst) begin if(rst==1) begin pwm_r <=1'b0; end else begin if(period_cnt >= duty_r) //if period counter is bigger or equals to duty value, then set pwm value to high pwm_r <=1'b1; else pwm_r <=1'b0; end end
那麼如何實現呼吸燈的效果呢?咱們知道呼吸燈效果是由暗不斷的變亮,再由亮不斷的變暗的過程,而亮暗效果是由佔空比來調節的,所以咱們主要來控制佔空比,也就是控制duty的值。教程
在下面的測試代碼中,經過設置period的值,設定PWM的頻率爲200Hz,PWM_PLUS狀態便是增長duty值,若是增長到最大值,將pwm_flag置1,並開始將duty值減小,待減小到最小的值,則開始增長duty值,不斷循環。其中PWM_GAP狀態爲調整間隔,時間爲100us。
`timescale1ns/1ps module pwm_test( input clk, //25MHz input rst_n, //low active output led //high-off, low-on ); localparam CLK_FREQ =25; //25MHz localparam US_COUNT = CLK_FREQ ; //1 us counter localparam MS_COUNT = CLK_FREQ*1000; //1 ms counter localparam DUTY_STEP =32'd100000; //duty step localparam DUTY_MIN_VALUE =32'h6fffffff; //duty minimum value localparam DUTY_MAX_VALUE =32'hffffffff; //duty maximum value localparam IDLE =0; //IDLE state localparam PWM_PLUS =1;//PWM duty plus state localparam PWM_MINUS =2;//PWM duty minus state localparam PWM_GAP =3;//PWM duty adjustment gap wire pwm_out; //pwm output reg[31:0] period; //pwm step value reg[31:0] duty; //duty value reg pwm_flag ; //duty value plus and minus flag, 0: plus; 1: minus reg[3:0] state; reg[31:0] timer; //duty adjustment counter assign led =~pwm_out ;//led low active always@(posedge clk ornegedge rst_n) begin if(rst_n ==1'b0) begin period <=32'd0; timer <=32'd0; duty <=32'd0; pwm_flag <=1'b0; state <= IDLE; end else case(state) IDLE: begin period <=32'd17179;//The pwm step value, pwm 200Hz(period = 200*2^32/50000000) state <= PWM_PLUS; duty <= DUTY_MIN_VALUE; end PWM_PLUS : begin if(duty > DUTY_MAX_VALUE - DUTY_STEP) //if duty is bigger than DUTY MAX VALUE minus DUTY_STEP , begin to minus duty value begin pwm_flag <=1'b1; duty <= duty - DUTY_STEP ; end else begin pwm_flag <=1'b0; duty