基于ZYNQ-7000系列的FPGA学习笔记8——呼吸灯
上期内容,我们学习了按键控制蜂鸣器,这一期我们开始学习呼吸灯
1. 实验要求
控制领航者核心板上的led,实现一个呼吸灯的效果,具体要求如下:
- 呼吸灯由暗渐亮和由亮渐暗的时长都是2s,总共一个呼吸的周期为4s
- 需要通过PWM信号调节led的亮灭。实现呼吸灯
2. 功能分析
想要实现呼吸灯的效果,需要使用到PWM波,那么我们的功能实现就需要围绕产生PWM信号来完成,我是这样设计的:
- 已知led从暗到亮的时间是2s,那么只需要将2s分成100份,然后让占空比从0到99变化即可。
- 那么一个PWM信号占用的时间就是 2s / 100 = 20ms
- 同时占空比要从0到99之间变化,那么最小的计时周期就是20ms / 100 = 200us
- 也就是说,我们只需要通过系统时钟,然后分别定义200us,20ms,2s、已经led变化的标志位,就可以解决这个问题。
3. 模块设计
根据上述的分析,我们可以设计如下的功能框图:
4. 波形图
然后我们根据模块设计,绘制对应的波形图,如下:
5.代码编写
根据我们绘制出的波形图,编写对应的rtl代码,如下:
//模块端口定义
module breath_led(
input sys_clk,
input sys_rst_n,
output reg led
);
//定义计数值的最大值
parameter CNT_200US_MAX = 14'd10000;
parameter CNT_20MS_MAX = 7'd100;
parameter CNT_2S_MAX = 7'd100;
//定义200us的计数值,20ms的计数值,2s的计数值和led_flag
reg [13:0] cnt_200us;
reg [7:0] cnt_20ms;
reg [7:0] cnt_2s;
reg led_flag;
//200us的计时
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0) //初始值为0
cnt_200us <= 14'd0;
else if(cnt_200us < (CNT_200US_MAX - 1) )
cnt_200us <= cnt_200us + 14'd1;
else
cnt_200us <= 14'd0;
end
//计时20ms:每当cnt_200us计数到最大值,cnt_20ms改变一次
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0) //初始值为0
cnt_20ms <= 7'd0;
else if(cnt_200us == (CNT_200US_MAX -1)) begin
if(cnt_20ms == (CNT_20MS_MAX -1))
cnt_20ms <= 7'd0;
else
cnt_20ms <= cnt_20ms + 7'd1;
end
else
cnt_20ms <= cnt_20ms;
end
//计时2s:每当cnt_20ms和cnt_200us计数到最大值,cnt_2s改变一次
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cnt_2s <= 7'd0;
else if( (cnt_20ms == (CNT_20MS_MAX -1)) && (cnt_200us == (CNT_200US_MAX -1)) ) begin
if(cnt_2s == ( CNT_2S_MAX -1 ))
cnt_2s <= 7'd0;
else
cnt_2s <= cnt_2s +7'd1;
end
else
cnt_2s <= cnt_2s;
end
//控制led_flag,为0表示渐亮,为1表示渐灭
//每当cnt_20ms、cnt_2s和cnt_200us计数到最大值,led_flag改变一次
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
led_flag <= 1'b0;
else if( (cnt_20ms == (CNT_20MS_MAX -1)) && (cnt_200us == (CNT_200US_MAX -1)) && (cnt_2s == ( CNT_2S_MAX -1 )) )
led_flag <= ~led_flag;
else
led_flag <= led_flag;
end
//控制led灯的状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
led <= 1'b0;
else if( ((led_flag == 1'b0) && (cnt_20ms <= cnt_2s) ) || ( (led_flag == 1'b1) && (cnt_20ms > cnt_2s) ) )
led <= 1'b1;
else
led <= 1'b0;
end
endmodule
紧接着,编写测试文件:
`timescale 1ns / 1ns
module tb_breath_led();
//定义时钟周期为20ns
parameter CLK_PERIOD = 20;
//定义计数值的最大值
parameter CNT_200US_MAX = 14'd4;
parameter CNT_20MS_MAX = 7'd5;
parameter CNT_2S_MAX = 4'd5;
//定义输入和输出
reg sys_clk;
reg sys_rst_n;
wire led;
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生时钟
always #(CLK_PERIOD/2) sys_clk=~sys_clk;
breath_led #( .CNT_200US_MAX (CNT_200US_MAX),
.CNT_20MS_MAX (CNT_20MS_MAX ),
.CNT_2S_MAX (CNT_2S_MAX )
) u_breath_led(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.led (led)
);
endmodule
6. 代码仿真
完成代码编写之后,我们开始仿真,仿真结果如图:
可以看到,led的输出已经实现了pwm的信号输出,且占空比逐渐增大或减小,证明我们编写的rtl代码是没有问题的,下一步就是添加约束文件,分析综合
7. 添加约束文件并分析综合
添加如下的约束文件:
#时序约束
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
#IO 管脚约束
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS33} [get_ports led]
下一步分析综合,得到如下内部连接图:
再下一步就是生成比特流文件,然后上板调试了,这里结果我就还是不展示了。
以上就是本期的所有内容,创造不易,点个关注再走呗。
原文地址:https://blog.csdn.net/weixin_67907028/article/details/144300553
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!