1、简介
今天购买了AXLINX AX7020的开发板,从今天开始每一个例程都要做文档记录,为自己加油。
本实验,基于ALINX AX7020开发板,芯片为xc7z020clg400-2。
项目介绍:
呼吸灯,就是想人们呼吸频率的一种led灯亮灭的一种表现形式。过程是慢慢变亮,然后变亮以后又慢慢变灭的一种过程。很多初学者会认为硬件逻辑语言怎么能控制电流的高低呢,让灯有多亮就调多亮,所以觉得不好实现,其实不用担心,呼吸灯捅破窗户纸就知道,其实是一个很简单的一个小项目,下面我将一步一步的讲解,并且实现。
PWM(Pluse Width Modulation)脉冲宽度调制,是一种对模拟信号电平进行数字编码的方法。
需要回顾一下占空比的小知识,通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。并广泛应用在从测量、通信、功率控制与变换及LED照明等许多领域中。顾名思义,就是占空比可调的信号,那么什么是占空比呢?
占空比(Duty Cycle or Duty Ratio),可以解释为,在一脉冲序列中(方波),正脉冲序列的持续时间与脉冲总周期的比值。也可理解为,电路释放能量的有效时间与总释放时间的比值。PWM是怎样实现调光呢?想要调节LED的亮度变化,实则是调节控制流经LED的电流。电流增大则LED亮度增强,反之减弱。但由于电流为模拟信号,所以这时就用到了PWM。
假设刚开始时占空比为1%,慢慢的占空比为2%、3%、4%……56%、57%……98%、99%、100%。
这就是LED灯亮的一个过程,我们可以让占空比为1%时,令LED灯亮,其余的部分让LED灭,慢慢的占空比越来越大,亮的部分也越来越多,这就是一个由灭到亮的一个过程。
假设刚开始时占空比为100%,慢慢的占空比为99%、98%、97%……64%、63%……2%、1%、0%。
这就是LED灯灭的一个过程,我们可以让占空比为99%时,令LED灯灭,其余的部分让LED亮,慢慢的占空比越来越小,灭的部分也越来越多,这就是一个由亮到灭的一个过程。
时钟只对着2us计数的一个时钟周期的;比如说晶振为50MHZ,那么每一个时钟周期相当于1/50M=20ns,故第一个计数器为一个2us的计数器,每计100次之后清零;第二个计数器为1ms计数器,每当2us计数器计数100次时,1ms就会增加一次。同理1s计数器也是一样。
将1s分成1000份,每一个周期为1ms,前500份由暗变亮,后500份由亮变暗
将1ms分成500份,每一个周期为2us,
将2us分成100份,每一份为20ns
我们可以看出,当cnt_1s>=cnt_1ms的部分刚好是我们上面所说的占空比的位置,而且当cnt_1s加到499的时候刚好占空比为100%,所以我们就会多了一个条件,当cnt_1s>=cnt_1ms,我们让LED亮,cnt_1s<cnt_1ms的部分我们让他灭。
一个完整的呼吸灯时间为1s,也就是,由亮变暗需要0.5s,由暗变亮需要0.5s;将 1s分为1000等份,也就是由亮变暗,需要500次的变化,每次为1ms,将,1ms分为500等份,占空比100%过渡到0%,需要100次的变化,每次为2us;
`timescale 1ns/1ps
module pwm_led(
input clk,
input rst_n,
output [3:0] leds_o
);
reg [9:0] cnt_2us; //将2us分成100份,每一份为20ns(50MHz);
reg [9:0] cnt_1ms; //将1ms分成500份,每周期为2us;
reg [9:0] cnt_1s; //将1s分成1000份,每周期为1ms,前500由暗变亮,后500由亮边暗
reg flag;
reg pwm;
//生成一周期2us,1/50MHz=20*10-9=20ns,20ns*100=2us
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_2us <= 10'd0;
else begin
if(cnt_2us>=10'd100)
cnt_2us <= 10'd0;
else
cnt_2us <= cnt_2us+1'b1;
end
end
//生成一周期1ms,2us*500=1ms,
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_1ms <= 10'd0;
else begin
if(cnt_2us==10'd99 && cnt_1ms==10'd499)
cnt_1ms <= 10'd0;
else
if(cnt_2us==10'd99)
cnt_1ms <= cnt_1ms+1'b1;
end
end
//生成一周期1s,1ms*500+1ms*500=1s;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_1s <= 10'd0;
else begin
if(cnt_2us==10'd99 && cnt_1ms==10'd499 && cnt_1s==10'd499)
cnt_1s <= 7'd0;
else
if(cnt_2us==10'd99 && cnt_1ms==10'd499)
cnt_1s <= cnt_1s+1'b1;
end
end
//使用flag信号来控制亮暗变化;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
flag <= 1'b0;
else if(cnt_1s==10'd499 && cnt_1ms==10'd499 && cnt_2us == 10'd99)
flag <= ~flag;
else
flag <= flag;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
pwm <= 1'b0;
else if(flag == 1'b0 && cnt_1s >= cnt_1ms)
pwm <= 1'b1;
else if(flag == 1'b0 && cnt_1s < cnt_1ms)
pwm <= 1'b0;
else if(flag == 1'b1 && cnt_1s >= cnt_1ms)
pwm <= 1'b0;
else if(flag == 1'b1 && cnt_1s < cnt_1ms)
pwm <= 1'b1;
else
pwm <= 1'b0;
end
assign leds_o = {4{!pwm}};//开发板LED为低有效
endmodule
测试代码为:
`timescale 1ns/1ps
module tb_pwm_led;
reg clk;
reg rst_n;
wire [3:0] leds_o;
always #10 clk=~clk;
initial
begin
clk = 1'b0;
rst_n = 1'b0;
#100 rst_n = 1'b1;
end
pwm_led uut(
.clk(clk),
.rst_n(rst_n),
.leds_o(leds_o)
);
endmodule
仿真结果为:
同理大家可以尝试更改一下代码,实现
将1s分成1000份,每一个周期为1ms,前500份由暗变亮,后500份由亮变暗
将1ms分成500份,每一个周期为2us,
将2us分成100份,每一份为20ns
将2s分成1000份,每一个周期为2ms,前500份由暗变亮,后500份由亮变暗
将2ms分成500份,每一个周期为4us,
将4us分成200份,每一份为20ns
将4s分成2000份,每一个周期为2ms,前1000份由暗变亮,后1000份由亮变暗
将2ms分成1000份,每一个周期为2us,
将2us分成100份,每一份为20ns
将2s分成2000份,每一个周期为1ms,前1000份由暗变亮,后1000份由亮变暗
将1ms分成1000份,每一个周期为1us,
将1us分成50份,每一份为20ns