目录
项目要求:
完成一个呼吸灯,要求从亮到灭的时间位2s,从灭到亮的时间为2s,完成呼吸灯的一个过程需要四秒。
实现原理:
呼吸灯的实现主要是利用人眼视觉差来实现“呼”“吸”过程的效果,我们只需产生一个逐渐改变占空比的方波即可,即PWM。我们可以将2秒等分为1000份,这样每一个份即为2/1000s,假设第一份2/1000s为高电平(占空比100%),然后逐渐减少占空比,经过1000次以后,该方波占空比变为0,我们设定方波为高电平时,led灯亮,否则灭,这样我们就实现了从亮到灭的“”呼“”过程,类似的对于“吸”这个过程,我们需要设置一个flag_r标志信号,使其亮灭与“呼”过程相反即可。下图给出了该方波占空比的波形图。
整个呼吸效果的时序图如如下:
由上图可见有两个“1000”和一个“100”,要注意的是这三者间的关系,pwm_cyc_cnt的1000指的是将亮--->灭的整个2s的过程分为1000份(2/1000s),clk50mcnt_1000中的1000指的是(2/1000s)的“小块”进一步分1000份(2/1000/1000s)=2000ns,这个2000ns需要频率为50mhz的晶振抖动100次,故clk50mcnt中的100表示此含义。需要注意的是clk50mcnt使在基准时钟的基础上产生的,clk50mcnt_1000是在clk50mcnt的基础上产生的,pwm_cyc_cnt是在clk50mcnt_1000的基础上产生的。
进行1000次等分的就是为了在短时间内快速变化,由于人眼的视觉差,使得整个过程平滑流畅。
用我们所期望的pwm波形对比可发现,当clk50mcnt_1000<pwm_cyc_cnt时将led电平拉低/高就能产生我们所需要的由亮到暗/由暗到亮的呼吸效果。
主代码
module led_breath(
input wire clk,
input wire rst_n,
output wire [3:0]led
);
reg [6:0] clk50mcnt;
reg [9:0] clk50mcnt_1000;
reg [9:0] pwm_cyc_cnt;
reg flag_r;
wire rst;
wire [3:0]led_wave;
assign rst = ~rst_n;
//lk50mcnt counter
always@(posedge clk)begin
if(rst == 1'b1)begin
clk50mcnt <= 'd0;
end
else if (clk50mcnt == 'd99)begin
clk50mcnt <= 'd0;
end
else begin
clk50mcnt <= clk50mcnt + 1'b1;
end
end
//clk50mcnt_1000 counter
always@(posedge clk)begin
if(rst == 1'b1)begin
clk50mcnt_1000 <= 'd0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999)begin
clk50mcnt_1000 <= 'd0;
end
else if (clk50mcnt == 'd99)begin
clk50mcnt_1000 <= clk50mcnt_1000 + 1'b1;
end
end
//pwm_cyc_cnt counter
always@(posedge clk)begin
if(rst == 1'b1)begin
pwm_cyc_cnt <= 'd0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
pwm_cyc_cnt <= 'd0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999)begin
pwm_cyc_cnt <= pwm_cyc_cnt + 1'b1;
end
end
always@(posedge clk)begin
if(rst == 1'b1)begin
flag_r <= 1'b0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
flag_r <= ~flag_r;
end
else begin
flag_r <= flag_r;
end
end
assign led_wave = ( clk50mcnt_1000 < pwm_cyc_cnt)?4'b0000:4'b1111;
assign led = (flag_r <= 4'b0000)?led_wave:~led_wave;
endmodule
仿真效果:
加入按键切换呼吸速度
由于呼吸的速度是由clk50mcnt控制的,若两个呼吸单程分别为2s和1s,故我们需要重新设定一个7位宽的now_cnt变量来储存不同速率的clk50mcnt(us_2s_cnt、us_1s_cnt),和一个两位宽的输入信号(key_in).
input [1:0] key_in;
reg [6:0] now_cnt;
parameter us_2s_cnt = 'd99;
parameter us_1s_cnt = 'd49;
最后利用key_in来切换呼吸速率
always@(posedge clk)begin
if(rst == 1'b1)begin
now_cnt <= 7'd0;
end
else if (key_in[0]==1&&key_in[1]==0) begin
now_cnt <= us_2s_cnt ;
end
else if (key_in[1]==1&&key_in[0]==0) begin
now_cnt <= us_1s_cnt ;
end
end
按键切换呼吸速率代码
module led_breath(
input wire clk,
input wire rst_n,
input [1:0] key_in,
output wire [3:0]led
);
reg [6:0] clk50mcnt;
reg [9:0] clk50mcnt_1000;
reg [9:0] pwm_cyc_cnt;
reg flag_r;
reg [6:0] now_cnt;
wire rst;
wire [3:0] led_wave;
assign rst = ~rst_n;
parameter us_2s_cnt = 'd99;
parameter us_1s_cnt = 'd49;
//lk50mcnt counter
always@(posedge clk)begin
if(rst == 1'b1)begin
clk50mcnt <= 'd0;
end
else if (clk50mcnt == now_cnt-1)begin
clk50mcnt <= 'd0;
end
else begin
clk50mcnt <= clk50mcnt + 1'b1;
end
end
//clk50mcnt_1000 counter
always@(posedge clk)begin
if(rst == 1'b1)begin
clk50mcnt_1000 <= 'd0;
end
else if (clk50mcnt == now_cnt-1 && clk50mcnt_1000 == 'd999)begin
clk50mcnt_1000 <= 'd0;
end
else if (clk50mcnt == now_cnt-1)begin
clk50mcnt_1000 <= clk50mcnt_1000 + 1'b1;
end
end
//pwm_cyc_cnt counter
always@(posedge clk)begin
if(rst == 1'b1)begin
pwm_cyc_cnt <= 'd0;
end
else if (clk50mcnt == now_cnt-1 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
pwm_cyc_cnt <= 'd0;
end
else if (clk50mcnt == now_cnt-1 && clk50mcnt_1000 == 'd999)begin
pwm_cyc_cnt <= pwm_cyc_cnt + 1'b1;
end
end
// flag_r
always@(posedge clk)begin
if(rst == 1'b1)begin
flag_r <= 1'b0;
end
else if (clk50mcnt == now_cnt-1 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
flag_r <= ~flag_r;
end
else begin
flag_r <= flag_r;
end
end
//key control
always@(posedge clk)begin
if(rst == 1'b1)begin
now_cnt <= 7'd0;
end
else if (key_in[0]==1&&key_in[1]==0) begin
now_cnt <= us_2s_cnt ;
end
else if (key_in[1]==1&&key_in[0]==0) begin
now_cnt <= us_1s_cnt ;
end
end
assign led_wave = ( clk50mcnt_1000 < pwm_cyc_cnt)?4'b0000:4'b1111;
assign led = (flag_r <= 4'b0000)?led_wave:~led_wave;
endmodule
如需按键消抖,加入按键消抖模块即可。
实验总结:
在代码编写中flag标志信号没有理解到位导致实验现象只有“吸”没有“呼”的现象。
错误示范:
always@(posedge clk)begin
if(rst == 1'b1)begin
flag_r <= 1'b0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
flag_r <= 1'b1;
end
else begin
flag_r <= 1'b0;
end
end
修改后:
always@(posedge clk)begin
if(rst == 1'b1)begin
flag_r <= 1'b0;
end
else if (clk50mcnt == 'd99 && clk50mcnt_1000 == 'd999 && pwm_cyc_cnt == 'd999)begin
flag_r <= ~flag_r;
end
else begin
flag_r <= flag_r;
end
end