基于EP4CE6F17C8的FPGA流水灯实例

一、电路模块

 1、LED

开发板板载了4个用户LED发光二极管,其原理图如下所示,当 FPGA的引脚输出为逻辑 0时,LED会熄灭。输出为逻辑1时,LED被点亮。

其实物图如下所示。

LED的引脚分配见下表。

2、时钟晶振

开发板板载了一个50MHz的有源晶振,为系统提供时钟。

其实物图如下所示。

时钟输出引脚分配见下表。

3、按键

开发板板载了4个独立按键,其中有3个用户按键(KEY1~KEY3),1个功能按键(RESET)。按键按下为低电平(0),释放为高电平(1),4个按键的原理图如下图所示。本例中只使用了RESET键。

其实物图如下所示。

按键的引脚分配见下表。

 

二、实验代码

本例实现4位LED的流水灯,输出使用4个发光管led1~led4。模块名称为led4_shift,文件名称为led4_shift.v,设置为顶层模块,代码如下。

module led4_shift( 
    input        clk,          //板载50HMz系统时钟
    input        rst_n,        //复位按键
    output reg[3:0] led        //4个LED
 );
 
reg [27:0] timer;                            //定义时钟计数器   
//4秒循环计数
always@(posedge clk or negedge rst_n)        //敏感信号为时钟上沿或复位下沿
begin
    if(rst_n == 1'b0)                        //低电平复位
      timer <= 28'd0;                        //复位时时钟计数器清零
    else if(timer == 28'd199_999_999)        //时钟计数器到达4秒时
      timer <= 28'd0;                        //时钟计数器清零
    else
      timer <= timer + 28'd1;                //时钟计数器加1,即来一次时钟脉冲加一次
end

//LED控制
always@(posedge clk or negedge rst_n)        //敏感信号为时钟上沿或复位下沿
begin
    if(rst_n == 1'b0)                        //低电平复位时LED全灭
      led <= 4'b0000;
    else if(timer == 28'd49_999_999)         //时钟到1秒时,LED0点亮
      led <= 4'b0001; 
    else if(timer == 28'd99_999_999)         //时钟到2秒时,LED1点亮
      led <= 4'b0010;
    else if(timer == 28'd149_999_999)        //时钟到3秒时,LED2点亮
      led <= 4'b0100; 
    else if(timer == 28'd199_999_999)        //时钟到4秒时,LED3点亮
      led <= 4'b1000;
end
endmodule

代码也可以写成如下的方式。

module led4_shift( 
    input        clk,          //板载50HMz系统时钟
    input        rst_n,        //复位按键
    output reg[3:0] led        //4个LED
 );
 
reg [25:0] timer;              //定义时钟计数器
reg [2:0] sec;                 //定义秒计数器
    
//4秒循环计数
always@(posedge clk or negedge rst_n)     //敏感信号为时钟上沿或复位下沿
begin
    if(rst_n == 1'b0)                     //低电平复位
    begin
        timer <= 26'd0;                   //复位时时钟计数器及秒计数器清零
        sec <= 3'd0;
    end
    else if(timer == 26'd49_999_999)      //时钟计数器到达1秒时
    begin
        timer <= 26'd0;                   //时钟计数器清零
        sec <= sec + 3'd1;                //秒计数器加1
    end
    else if(sec == 3'd4)                  //秒计数器加到4时恢复零
        sec <= 3'd0;
    else
        timer <= timer + 26'd1;           //时钟计数器加1,即来一次时钟脉冲加一次
end

//LED控制
always@(posedge clk or negedge rst_n)     //敏感信号为时钟上沿或复位下沿
begin
    if(rst_n == 1'b0)                     //低电平复位时LED全灭
       led <= 4'b0000;
    else if(sec == 3'd1)                  //时钟到1秒时,LED0点亮
       led <= 4'b0001; 
    else if(sec == 3'd2)                  //时钟到2秒时,LED1点亮
       led <= 4'b0010;
    else if(sec == 3'd3)                  //时钟到3秒时,LED2点亮
       led <= 4'b0100; 
    else if(sec == 3'd4)                  //时钟到4秒时,LED3点亮
       led <= 4'b1000;
end
endmodule

三、代码说明

1、always语句为过程语句,用于引导顺序语句,设计模块中的任何顺序语句都必须放在过程语句结构中。在@之后的括号中,列出所有输入敏感信号。在always语句中申明的变量必须为reg类型,不能使用wire类型。
begin与end之间为顺序块语句,仅限于用在always引导的过程语句结构中(assign中不能使用块语句)。
2、“<=”属于非阻塞赋值符号(“=”属于阻塞型赋值),只能用于顺序语句中,不能在assign引导的并行语句中。即assign中只能使用“=”赋值,而always中可以两种("="和"<=")都使用,但在同一过程中对同一变量赋值,两种不允许混用。而且,在许多情况下,不同的赋值符号将导致不同的电路结构和逻辑功能的综合结果。注意,对同一个变量的赋值不能出现在两个以上的always语句中。
3、条件判断if语句必须放在由always引导的顺序结构中。数字常量要指明位宽及使用的进制方式,如4'b0001,4表示位宽为4,b表示使用二进制,后面的数字要符合位宽。
4、同一结果可以有多种程序设计形式,以消耗逻辑器件最少为佳。

四、实验步骤

FPGA开发的详细步骤请参见“基于EP4CE6F17C8的FPGA开发流程(以半加器为例)”一文,本例只对一同之处进行说明。

本例工程放在D:\EDA_FPGA\Exam_2文件夹下,工程名称为Exam_2。模块文件名称为led4_shift.v,并设置为顶层实体。其余步骤与“基于EP4CE6F17C8的FPGA开发流程”中的一样。

接下来看管脚约束,本例中不仅使用了4个LED,还引入了时钟晶振和复位按钮,具体的端口分配如下图所示。

对于未用到的引脚设置为三态输入方式,多用用途引脚全部做为普通I/O端口,电压设置为3.3-V LVTTL(与”基于EP4CE6F17C8的FPGA开发流程“中的一样)。需要注意,程序中的每个端口都必须为其分配管脚,如果系统中存在未分配的I/O,软件可能会进行随机分配,这将造成不可预料的后果,存在烧坏FPGA芯片的风险。

接下来对工程进行编译,编译完成后,可查看一下逻辑器件的消耗情况,第一种代码的消耗如下图所示。

第二种代码的消耗如下图所示。

可以看到,第二种方式要消耗掉更多的器件。另外,还可以点击菜单Tools->Netlist Viewers->RTL Viewer,查看一下生成的RTL电路图。

最后进行程序下载,并查看结果。

根据前面的程序,初始化时4个led均为熄灭状态,1秒后led0点亮其余熄灭,再过1秒后led1点亮其余熄灭,再过1秒后led2点亮其余熄灭,再过1秒后led3点亮其余熄灭, 再过1秒后led0再次被点亮其余熄灭,如此循环,如下面4张图片所示。

当按下RESET键时,所示LED均熄灭。如下图所示。

posted @ 2024-03-09 23:53  fxzq  阅读(355)  评论(0编辑  收藏  举报