该文章是我学习正点原子的达芬奇之FPGA开发指南做的学习笔记,内容并非完全原创,用以记录本人的学习脚步。
实验一、呼吸灯
一、简介:
呼吸灯采用PWM信号控制的方式,在固定的频率下,通过改变占空比来达到改变LED亮度的效果。PWM(Pulse Width Modulation):脉冲宽度调制,利用微控制器输出PWM信号,实现对模拟电路控制的一种非常有效的技术,在测量、通信、功率控制等领域得到广泛的应用。
二、设计思路:
利用计数计时器产生固定周期的PWM信号,当PWM信号中的占空比为0%时,LED不亮;当PWM信号中的占空比为100%时,LED最亮。在PWM信号的占空比从0%变化到100%再变化到0%LED的由暗慢慢变亮再慢慢变暗,从而实现LED的“呼吸”效果。
PWM占空比示意图
三、本次实验端口与模块示意图
周期信号计数器用于产生驱动LED的脉冲信号,本次实验的周期信号频率为1KHz,可以通
过调整后级逻辑在每个周期之后进行递增或者递减,最后通过周期计数器与占空比计数器
数值比较,可以实现占空比可调的PWM信号。
四、实验代码:
Design:
module breath_led(
input sys_clk,
input rest_clk_n,
output reg led
);
reg[15:0] preiod_cnt1;
reg[15:0] duty_cnt;
reg flag;
always @(posedge sys_clk or negedge rest_clk_n)begin
if(!rest_clk_n)
preiod_cnt1 <= 16'd0;
else if(preiod_cnt1==16'd50000)
preiod_cnt1 <= 16'd0;
else
preiod_cnt1 <= preiod_cnt1 + 1'b1;
end
always @(posedge sys_clk or negedge rest_clk_n)begin
if(!rest_clk_n)begin
duty_cnt <= 16'd0;
flag <= 1'b0;
end
else begin
if(preiod_cnt1==16'd50000)begin
if(flag==0)begin
if(duty_cnt==16'd50000)begin
flag<=1'b1;
end
else begin
duty_cnt <= duty_cnt + 16'd25;
end
end
else begin
if(duty_cnt==16'd0)begin
flag <= 1'b0;
end
else begin
duty_cnt <= duty_cnt - 16'd25;
end
end
end
end
end
always @(posedge sys_clk)begin
if(preiod_cnt1<=duty_cnt)begin
led <= 1'b1;
end
else begin
led <=1'b0;
end
end
endmodule
Testbench:
`timescale 1ns/1ps
module tb_breath_led;
parameter T = 20;
reg sys_clk;
reg rest_clk_n;
wire led;
initial begin
sys_clk = 1'b0;
rest_clk_n = 1'b0;
#(T+1) rest_clk_n = 1'b1;
end
always #(T/2) sys_clk = ~sys_clk;
breath_led u0_breath_led (
.sys_clk (sys_clk ),
.rest_clk_n (rest_clk_n),
.led (led )
);
endmodule
五、RTL电路以及仿真结果:
六、实验过程中遇到的问题(由于一开始忘记截图了,所以下文不会出现错误提示的截图):
1、在第一次编写代码时,由于将flag以及duty_cnt在多个always语句块中使用,导致编译
时出现了3个critical errors,导致最后behavior仿真时无法进行。最后只能重新编写,将
flag以及duty_cnt放在一个always语句块中赋值。在编写代码时,非常忌讳在多个语句块中
对一个变量赋值。
2、编写testbench时,由于design文件和testbench文件中的信号没有对上,导致无法仿真,
最后经过检查,发现了问题的所在并做修改,终于仿真成功。
七、上板验证:
由于手头没有Xlinx的FPGA开发板,无法进行上板验证。