实现LED的流水灯效果是入门的基础操作,所谓流水灯就是要让LED按照固定的频率依次闪烁,说到可能可能之前我们编写的计数器程序(实现LED翻转)能够帮助我们实现LED的流水灯效果。可以参考文章:fpga学习day3 LED灯翻转(计数器)-CSDN博客文章浏览阅读297次,点赞12次,收藏6次。在代码实现上,需要先定义LED用到的输入输出端口(对LED_flash模块进行定义),需要用到系统时钟,低电平复位信号(ResetL=0时,所有操作复位)。我们可以采用计数器的方式,对系统的时钟周期进行计数。一个系统时钟周期为20ns,LED端口的信号周期为1ms(0.5ms进行翻转一次),也就是当系统时钟经过25000个周期后,LED端口的信号需要翻转一次。LED闪烁的本质就是输入电平的翻转,因此想要让LED灯按照我们期待的频率闪烁,需要根据板子的系统时钟和LED电平翻转频率之间的关系来实现。https://blog.csdn.net/crrrd/article/details/135704967?spm=1001.2014.3001.5502
这篇文章中使用计数器来使LED灯按照1khz的频率反转。在此基础上,我们可以让扩展板上的8个LED灯按照1Khz的频率一次点亮。下面将介绍三种方法:
一、使用移位方式实现流水灯效果
首先,能想到的最简单和直观的方式就是使用移位运算符<<来实现流水效果。但此时需要注意:当第八个LED点亮时,如果再进行<<操作,会导致8个LED对应的位全为0,图示如下:
我们需要的是当LED7被点亮后,下一次的操作可以让LED0点亮,因此需要使用条件语句来区别不同的情况,当出现上述情况是,下一步让LED变为00000001,其他情况则使用<<进行移位操作,代码如下:
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
LED <= 8'b00000001;
else if(counter ==25000000-1)begin
if((LED==8'b10000000)|LED==8'b00000000)
LED<= 8'b00000001;
else
LED<=LED<<1;
end
配合之前的计数器部分代码,实现流水灯的整体代码为:
module led_flow(
CLK,
Reset_n,
LED
);
input CLK;
input Reset_n;
output reg[7:0] LED;
reg [24:0] counter;
//计数器部分代码
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter ==25000000-1)
counter <= 0;
else
counter <= counter+1'd1;
//移位操作,分情况讨论
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
LED <= 8'b00000001;
else if(counter ==25000000-1)begin
if((LED==8'b10000000)|LED==8'b00000000)
LED<= 8'b00000001;
else
LED<=LED<<1;
end
endmodule
二、使用循环位移简化方法一的代码
方法一中需要对特殊情况进行讨论,使得代码变得复杂且不直观,在这里我们可以使用拼接方法,直接采用循环位移的方式实现流水灯的效果。
通过图示,我们可以使用{LED[6:0],LED[7]}的拼接语句来替代方法一中的条件分类讨论,使用一行代码解决问题:
/*if((LED==8'b10000000)|LED==8'b00000000)
LED<= 8'b00000001;
else
LED<=LED<<1;*/
LED <= {LED[6:0],LED[7]};
整体实现代码为:
module led_flow_for(
CLK,
Reset_n,
LED
);
input CLK;
input Reset_n;
output reg[7:0] LED;//8个灯
reg [24:0] counter;
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)//复位端口必须要单独写
counter <= 0;
else if(counter == 25000000-1)
counter <= 0;
else
counter <= counter+1'd1;
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
LED <= 8'b0000_0001;
else if(counter == 25000000-1)
LED <= {LED[6:0],LED[7]};//位拼接
else
LED <= LED;
endmodule
三、参考3-8译码器实现流水灯操作
扩展板上提供了8个LED灯,将这8个LED灯依次点亮,需要的驱动信号与3-8译码器的输出信号相同:
因此可以使用之前编写好的3-8译码器的代码,在工程中进行引用实现对LED的激励,这需要我们对3-8译码器进行模块实例化的操作,具体过程如下:
1.在新建立的工程中导入之前3-8译码器的.v文件,其他操作与先前相同,只是这里需要选择Add
File而不是Create File。
下面找到实现3-8译码器的代码,后缀为.srcs的文件中包含了我们再vivado中编写的设计、仿真的代码内容,点击进入:
进入文件后,看到如下界面,选择sources_1,这里面存储了design file:
然后我们就可以找到名为decode3_8.v的文件,点击这个文件前面会出现一个蓝色的圆圈,代表我们选中了它,再选择OK,这个文件就成功加入了工程:
接下来,我们再新建立一个design file,稍后将在这个文件中引用译码器的内容,此时工程中会出现两个文件:
2.引用3-8译码器的实例:
3-8译码器的代码如下:
module decode3_8(
input a,
input b,
input c,
output reg[7:0] out
);
// reg [7:0] out;
always@(*) begin //always@(a,b,c)
case({a,b,c})//{a,b,c}实现位拼接
3'b000:out = 8'b0000_0001;
3'b001:out = 8'b0000_0010;
3'b010:out = 8'b0000_0100;
3'b011:out = 8'b0000_1000;
3'b100:out = 8'b0001_0000;
3'b101:out = 8'b0010_0000;
3'b110:out = 8'b0100_0000;
3'b111:out = 8'b1000_0000;
endcase
end
endmodule
因此在引用时我们需要确定a,b,c和[7:0]out分别对应什么信号,根据之前的分析,LED[7:0]接收out[7:0]输出的8个信号,又因为要求LED灯以1Khz闪烁,因此译码器的输入信号a,b,c需要根据计数器counter的数值进行变化,也就是说当计数器counter为24999时,编码器的计数器decoder_cnt对输入信号进行+1操作,代码实现:
reg [2:0]decoder_cnt;
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
decoder_cnt<=0;
else if(counter == 25000000-1)
decoder_cnt<=decoder_cnt+1'd1;
此时,a对用计数器decoder_cnt的高位,c对应计数器decoder_cnt的低位。因此在实现流水灯的操作中,3-8译码器的输入信号为计数器的输出信号,输出信号与LED相连接,实例化代码为:
//例化
decode3_8 decoder0(
.a(decoder_cnt[2]),
.b(decoder_cnt[1]),
.c(decoder_cnt[0]),
.out(LED)
);
此时需要注意将LED的数据形式由reg更改为wire类型,因为在这个方法下LED信号是由译码器的输出决定的,是由其他激励控制的信号,因此需要更改为wire数据类型。
整体代码为:
module led_flow_decoder(
CLK,
Reset_n,
LED
);
input CLK;
input Reset_n;
output wire[7:0]LED;
//因为此时的led被3-8译码器驱动,因此在代码中应为wire型数据
//因为led_flow_decoder中的LED模块是被译码器驱动的
//可以理解为定义的7个管脚作为引线起到传递译码器结果的作用
reg [24:0] counter;
//使用3-8译码器 LED视为3-8译码器的输出端
//input:a b c, OUTPUT:LED0-LED7
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter ==25000000-1)
counter <= 0;
else
counter <= counter+1'd1;
//编码器输入信号每0.5ms变化一次
reg [2:0]decoder_cnt;
always@(posedge CLK or negedge Reset_n)
if(!Reset_n)
decoder_cnt <= 0;
else if(counter == 25000000-1)
decoder_cnt <= decoder_cnt+1'd1;
//例化
decode3_8 decoder0(
.a(decoder_cnt[2]),
.b(decoder_cnt[1]),
.c(decoder_cnt[0]),
.out(LED)
);
endmodule