集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 1203|回复: 3

4位闪烁灯设计就这么简单

[复制链接]
月影星痕 发表于 2019-9-5 15:53:05 | 显示全部楼层 |阅读模式
4位闪烁灯设计


1 项目背景LED灯的理论、教学板的原理图,已经在案例1位闪烁灯中有详细的描述,在此不再讲述,有兴趣的读者可以返回去阅读。
2 设计目标
本工程使用4个LED灯---LED1~LED4,实现一个呼吸灯的功能。这4个灯具体的变化情况为:
第1个灯隔1秒,亮1秒后变暗;然后第2个灯隔1秒,亮2秒后变暗;然后第3个灯隔1秒,亮3秒后变暗;最后第4个灯隔1秒,亮4秒后变暗。之后循环往复。
下面是波形图:

图 154
上板效果图如下图所示。
   
图 155

3 设计实现3.1 顶层信号
新建目录:D:\mdy_book\huxiled。在该目录中,新建一个名为huxiled.v的文件,并用GVIM打开,开始编写代码。
我们先分析一下板子上的LED灯。每个LED灯都有一个信号来控制,该信号为0,则灯亮,如果该信号为1,则灯来。现在我们要控制4个LED灯亮灭,那就需要4个信号,假设分别为led0、led1、led2和led3。这4个信号分别连接到4个led灯上。如果要让LED0灯0亮,LED1~3灯来,那FPGA就让led0信号为0,led1~3信号都为1。下面表格表示了硬件电路图的连接关系。
器件
原理图信号
FPGA管脚
FPGA工程信号
LED6
LED1_NET
AA4
led0
LED7
LED2_NET
AB4
led1
LED8
LED3_NET
AA5
led2
LED9
LED4_NET
AB8
led3
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n
综上所述,我们这个工程需要6个信号:时钟clk,复位rst_n、led0、led1、led2和led3。将module的名称定义为huxiled。为此,代码如下:
1
2
3
4
5
6
7
8
module huxiled(
clk    ,
rst_n  ,
led0   ,
led1   ,
led2   ,
led3
);
其中clk、rst_n是输入信号,led0、led1、led2、led3是输出信号,并且六个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下:
1
2
3
4
5
6
7
input               clk    ;
input               rst_n  ;

output              led0   ;
output              led1   ;
output              led2   ;
output              led3   ;

3.2 信号设计
我们再分析一下功能需求,第1个灯隔1秒后,亮1秒;然后第2个灯隔1秒后,亮2秒;然后第3个灯隔1秒后,亮3秒,最后第4个灯隔1秒,亮4秒。如此循环往复。
上面的功能需求,也可以翻译成:对于LED0,复位后,先灭1秒,亮1秒,然后再灭12秒,循环往复;对于LED1,复位后,先灭3秒,亮2秒,然后再灭9秒,循环往复;对于LED2,复位后,先灭6秒,亮3秒,然后再灭5秒,循环往复;对于LED3,先灭10秒,亮4秒,循环往复。
再将其翻译成信号来理解:
复位后,让信号led0=1并持续1秒,然后让led0=0并持续1秒,然后让led0=1持续12秒。循环往复。
复位后,让信号led1=1并持续3秒,然后让led1=0并持续2秒,然后让led1=1持续9秒。循环往复。
复位后,让信号led2=1并持续6秒,然后让led2=0并持续3秒,然后让led2=1持续5秒。循环往复。
复位后,让信号led3=1并持续10秒,然后让led3=0并持续4秒。循环往复。
再将其翻译成波形如下图所示。

图 156
由图中可看到,信号led0~led3的变化单位最小是1秒,同时4个信号都是经过14秒后就循环一次。由至简设计法的思想,很容易就得出我们需要2个计数器,1个计数器用来计算1秒时间,另1个计数器用来计算14秒。有了这两个计数器,led0~led3的变化时间就有了标准。
我们用1个计数器用来计算1秒时间,该计数器名称为cnt0。本工程的工作时钟是50MHz,即周期为20ns,计数器计数到1_000_000_000/20=50_000_000个,我们就能知道1秒时间到了。该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assign add_cnt==1。综上所述,该计数器的代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
always @(posedge clk or negedge rst_n)begin
IF(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end

assign add_cnt0 = 1 ;
assign end_cnt0 = add_cnt0 && cnt0== 50_000_000-1 ;


我们再用1个计数器用来表示14秒,名称为cnt1。该计数器表示次数,自然是每隔1秒就加1,那就是end_cnt0。该计数器一共要数14次。所以代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==14-1 ;
有了两个计数器,我们来思考输出信号led0的变化。概括起来,led0有两种变化点:变0和变1。变0的原因都是计数到1秒时间,也就是add_cnt1 && cnt1==1-1时,led0变0。变1的原因,则是数到2秒时间时,即add_cnt1 && cnt1==2-1时,led0变1。所以led0信号的代码如下:

1
2
3
4
5
6
7
8
9
10
11
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led0 <= 1 ;
end
else if(add_cnt1 && cnt1==1-1)begin
led0 <= 0 ;
end
else if(add_cnt1 && cnt1==2-1)begin
led0 <= 1 ;
end
end


接下来我们思考输出信号led1的变化。概括起来,led1有两种变化点:变0和变1。变0的原因都是计数到3秒时间,也就是add_cnt1 && cnt1==3-1时,led1变0。变1的原因,则是数到5秒时间时,即add_cnt1 && cnt1==5-1时,led1变1。所以led1信号的代码如下:
1
2
3
4
5
6
7
8
9
10
11
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led1 <= 1 ;
end
else if(add_cnt1 && cnt1==3-1)begin
led1 <= 0 ;
end
else if(add_cnt1 && cnt1==5-1)begin
led1 <= 1 ;
end
end
接下来我们思考输出信号led2的变化。概括起来,led2有两种变化点:变0和变1。变0的原因都是计数到6秒时间,也就是add_cnt1 && cnt1==6-1时,led2变0。变1的原因,则是数到9秒时间时,即add_cnt1 && cnt1==9-1时,led2变1。所以led2信号的代码如下:

1
2
3
4
5
6
7
8
9
10
11
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led2 <= 1 ;
end
else if(add_cnt1 && cnt1==6-1)begin
led2 <= 0 ;
end
else if(add_cnt1 && cnt1==9-1)begin
led2 <= 1 ;
end
end


接下来我们思考输出信号led3的变化。概括起来,led3有两种变化点:变0和变1。变0的原因都是计数到10秒时间,也就是add_cnt1 && cnt1==10-1时,led3变0。变1的原因,则是数到14秒时间时,即add_cnt1 && cnt1==14-1,也就是end_cnt1时,led3变1。所以led3信号的代码如下:

1
2
3
4
5
6
7
8
9
10
11
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led3 <= 1 ;
end
else if(add_cnt1 && cnt1==10-1)begin
led3 <= 0 ;
end
else if(end_cnt1)begin
led3 <= 1 ;
end
end


此次,主体程序已经完成。接下来是将module补充完整。

3.3 信号定义
接下来定义信号类型。
cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为500_000_000,需要用29根线表示,即位宽是29位。因此代码如下:
1
reg    [28:0]  cnt0  ;
add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:
1
2
wire     add_cnt0  ;
wire     end_cnt0  ;
cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为8,需要用4根线表示,即位宽是4位。因此代码如下:
1
reg    [ 3:0]  cnt1  ;
add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:
1
2
wire     add_cnt1  ;
wire     end_cnt1  ;
led0、led1、led2、led3是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下:
1
2
3
4
reg         led0     ;
reg         led1     ;
reg         led2     ;
reg         led3     ;
至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。
Sunlife 发表于 2019-9-5 16:04:52 | 显示全部楼层
                     
晓灰灰 发表于 2019-9-5 16:17:43 | 显示全部楼层
4位闪烁灯设计就这么简单
zxopenljx 发表于 2023-4-8 09:25:27 | 显示全部楼层
4位闪烁灯设计就这么简单
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2024-11-28 04:33 , Processed in 0.060339 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表