【FPGA学习】DE2-115实现LED流水灯

目录

一、引言

二、开发环境搭建

[1. 安装VSCode](#1. 安装VSCode)

三、模块化设计

[1. 顶层模块(LedBlink.v)](#1. 顶层模块(LedBlink.v))

[2. 分频模块(fenpin.v)](#2. 分频模块(fenpin.v))

[3. 显示模块(display.v)](#3. 显示模块(display.v))

四、代码实现

五、功能测试与验证

[1. 测试环境搭建](#1. 测试环境搭建)

[2. 测试过程](#2. 测试过程)

六、扩展功能(选做)

[1. 按键暂停与恢复功能](#1. 按键暂停与恢复功能)

​编辑

结果演示:

七、总结

八、参考链接


一、引言

又到了最常规的流水灯环节!通过在DE2-115开发板上实现一个LED流水灯实验,加深对Verilog HDL的理解,还能培养模块化设计的习惯。本文将详细介绍如何在DE2-115开发板上设计一个周期为1秒的6个LED跑马灯效果,并提供规范化的代码实现。

二、开发环境搭建

1. 安装VSCode

  • 在VSCode中安装Verilog-HDL/SystemVerilog插件,实现Verilog代码的语法高亮和自动补全功能。(安装这个功能的话在VScode中编程就不容易出错,很简单就可以实现修改、报错、提示等等!)亲测!!好用!!!

  • 文件芯片:EP4CE115F29C7

三、模块化设计

任务要求:

1)采用层次化设计,分别在VScdoe编程各代码文件,如top顶层模块(比如LedBlink.v)、分频模块(fenpin.v)、显示模块(display.v);

2)尽量采用计数器、状态机思想;

题目分析:

DE2-115 开发板搭载了一个50MHz 固定频率晶振,作为 FPGA 系统的核心时钟源。该晶振可产生稳定的周期性电信号,每秒震荡 50×10⁶次,为整个电路提供时序基准。

时钟周期(Clock Period)指时钟信号完成一个完整周期(如上升沿到下一个上升沿)的时间,计算公式为:

时钟周期=时钟频率1​

对于 50MHz 时钟:

周期=50×106Hz1​=20×10−9秒=20纳秒(ns)

功能目标 :1 秒内依次点亮 6 个 LED,即每个 LED 的点亮间隔为 1/6 秒(约 0.167 秒)

整体代码:

复制代码
module led_test(
    input wire clk,     // 50MHz时钟输入
    input wire rst_n,   // 复位信号,低电平有效
    
    output reg [5:0] led // 6个LED灯的状态
);
​
// 计数器,计数1s需要50_000_000个时钟周期
reg [25:0] cnt; 
​
// 计数器模块
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt <= 26'd0; // 复位时计数器归零
    end 
    else if (cnt == 50_000_000 - 1) begin // 计满1秒后复位
        cnt <= 26'd0;
    end 
    else begin
        cnt <= cnt + 1'd1; // 使用非阻塞赋值
    end 
end 
​
// LED状态更新逻辑:每次触发时先清零所有LED
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        led <= 6'b000000; // 复位时关闭所有LED
    end 
    else begin
        // 默认保持当前状态(非阻塞赋值)
        led <= led;
        
        if (cnt == 8_333_333 - 1) begin
            led <= 6'b000001; // 点亮第一个LED
        end
        else if (cnt == 16_666_666 - 1) begin
            led <= 6'b000010; // 点亮第二个LED
        end
        else if (cnt == 25_000_000 - 1) begin
            led <= 6'b000100; // 点亮第三个LED
        end
        else if (cnt == 33_333_333 - 1) begin
            led <= 6'b001000; // 点亮第四个LED
        end
        else if (cnt == 41_666_666 - 1) begin
            led <= 6'b010000; // 点亮第五个LED
        end
        else if (cnt == 50_000_000 - 1) begin
            led <= 6'b100000; // 点亮第六个LED
        end
    end 
end 
​
endmodule

1. 顶层模块(LedBlink.v)

  • 定义顶层模块,整合各个子模块,实现整体功能。

  • 顶层模块负责初始化和连接各个子模块,确保整个系统协调工作。

复制代码
  module LedBlink (
      input clk,        // 系统时钟信号
      input rst,        // 复位信号
      output [5:0] led  // 6个LED的输出信号
  );
      wire clk_1hz; // 1Hz的时钟信号

      // 实例化分频模块
      fenpin u_fenpin (
          .clk(clk),
          .rst(rst),
          .clk_out(clk_1hz)
      );

      // 实例化显示模块
      display u_display (
          .clk(clk_1hz),
          .rst(rst),
          .led(led)
      );
  endmodule

2. 分频模块(fenpin.v)

  • 设计分频模块,将系统时钟信号分频为1秒的时钟信号。

  • 使用计数器实现分频功能,确保时钟信号的稳定性和准确性。

复制代码
  module fenpin (
      input clk,          // 系统时钟信号
      input rst,          // 复位信号
      output reg clk_out  // 分频后的时钟信号
  );
      reg [24:0] counter; // 25位计数器,用于分频

      always @(posedge clk or posedge rst) begin
          if (rst) begin
              counter <= 0;   // 复位计数器
              clk_out <= 0;   // 复位输出时钟
          end else begin
              if (counter == 25'd49999999) begin
                  counter <= 0;       // 计数器溢出,重置计数器
                  clk_out <= ~clk_out; // 翻转输出时钟
              end else begin
                  counter <= counter + 1; // 计数器加1
              end
          end
      end
  endmodule

3. 显示模块(display.v)

  • 设计显示模块,控制6个LED的动态显示效果。

  • 使用状态机思想,实现LED的跑马灯效果。

    复制代码
    module display (
        input clk,        // 时钟信号
        input rst,        // 复位信号
        output reg [5:0] led // 6个LED的输出信号
    );
        reg [2:0] state; // 3位状态寄存器,用于状态机
    
        always @(posedge clk or posedge rst) begin
            if (rst) begin
                state <= 3'b000; // 复位状态
                led <= 6'b000001; // 初始状态,点亮第一个LED
            end else begin
                case (state)
                    3'b000: led <= 6'b000010; // 状态0,点亮第二个LED
                    3'b001: led <= 6'b000100; // 状态1,点亮第三个LED
                    3'b010: led <= 6'b001000; // 状态2,点亮第四个LED
                    3'b011: led <= 6'b010000; // 状态3,点亮第五个LED
                    3'b100: led <= 6'b100000; // 状态4,点亮第六个LED
                    3'b101: led <= 6'b000001; // 状态5,回到第一个LED
                    default: led <= 6'b000001; // 默认状态
                endcase
                state <= state + 1; // 状态机状态更新
            end
        end
    endmodule

四、代码实现

    • 使用一个25位计数器 counter,将50MHz的系统时钟分频为1Hz的时钟信号 clk_1hz

    • 每当计数器计数到49999999时,输出时钟信号翻转一次,实现1秒的周期。

  1. 显示模块

    • 使用一个3位状态寄存器 state,通过状态机控制6个LED的动态显示效果。

    • 每个状态对应一个LED的点亮,状态机在每个1Hz时钟周期更新状态。

    module FLED (
    input clk, // 系统时钟信号,通常为50MHz
    input rst, // 复位信号
    output reg [5:0] led // 6个LED的输出信号
    );
    // 分频模块:将50MHz的系统时钟分频为1Hz
    reg [24:0] counter; // 25位计数器,用于分频
    reg clk_1hz; // 1Hz的时钟信号,定义为reg类型

    复制代码
     always @(posedge clk or posedge rst) begin
         if (rst) begin
             counter <= 0;   // 复位计数器
             clk_1hz <= 0;   // 复位输出时钟
         end else begin
             if (counter == 25'd49999999) begin
                 counter <= 0;       // 计数器溢出,重置计数器
                 clk_1hz <= ~clk_1hz; // 翻转输出时钟
             end else begin
                 counter <= counter + 1; // 计数器加1
             end
         end
     end
    
     // 显示模块:控制6个LED的动态显示效果
     reg [2:0] state; // 3位状态寄存器,用于状态机
    
     always @(posedge clk_1hz or posedge rst) begin
         if (rst) begin
             state <= 3'b000; // 复位状态
             led <= 6'b000001; // 初始状态,点亮第一个LED
         end else begin
             case (state)
                 3'b000: led <= 6'b000010; // 状态0,点亮第二个LED
                 3'b001: led <= 6'b000100; // 状态1,点亮第三个LED
                 3'b010: led <= 6'b001000; // 状态2,点亮第四个LED
                 3'b011: led <= 6'b010000; // 状态3,点亮第五个LED
                 3'b100: led <= 6'b100000; // 状态4,点亮第六个LED
                 3'b101: led <= 6'b000001; // 状态5,回到第一个LED
                 default: led <= 6'b000001; // 默认状态
             endcase
             state <= state + 1; // 状态机状态更新
         end
     end

    endmodule
    //博主这个代码有点小瑕疵还待完善

代码成功跑通啦!!!

五、功能测试与验证

1. 测试环境搭建

  • 介绍如何在DE2-115开发板上搭建测试环境,包括硬件连接和软件配置。

  • 文件芯片:EP4CE115F29C7

  • 说明如何将编译后的代码下载到开发板上进行测试。

  • 配置芯片:

烧录,运行:

2. 测试过程

1s周期流水灯

运行视频

六、扩展功能(选做)

1. 按键暂停与恢复功能

  • 介绍如何在流水灯实验中增加按键暂停和恢复功能。

  • 提供扩展功能的代码实现,解释按键功能的逻辑和实现方法。

  • DE2-115 提供了四个按钮开关,每个按钮开关都通过一个施 密特触发器进行了去抖动处理。四个施密特触发器的输出信 号,分别为KEY0、KEY1、KEY2、KEY3,直接连接到了Cyclone

  • IV E FPGA。当按钮没有 被按下的时候,它的输出是高电平,按下去则给出一个低电平

KEY0:复位

KEY1:按下暂停

代码实现如下:

复制代码
module LedBlink(
    input wire clk,       // 50MHz时钟输入
    input wire rst_n,     // 复位信号,低电平有效
    input wire stop_n,      //停止信号
    output reg [5:0] led  // 6个LED灯输出
);
    reg [25:0] cnt;       // 26位计数器,用于计数1秒周期
    // 计数器模块
    always @(posedge clk or negedge rst_n or negedge stop_n) begin // posedge是指clk的上升沿 negedge是指rst_n的下降沿
        if (!rst_n) begin
            cnt <= 26'd0; // 复位时,计数器从0开始计数
            led <= 6'b000001; // 复位时,第一个LED亮
			end
		  else if (!stop_n) begin
				cnt <= cnt;
				led <= led;
			end 
		  else if (cnt == 50_000_000 - 1) begin // 计数到50,000,000 - 1
            cnt <= 26'd0; // 重置计数器
            led <= {led[4:0], led[5]}; // 循环右移一位,保持流水灯效果
        end 
		  else begin
            cnt <= cnt + 1; // 增加计数器
        end
    end 
endmodule

结果演示:

运行结果如视频:

按键控制流水灯暂停

七、总结

在本次流水灯模块设计实践中,我们系统掌握了从硬件资源分析到软件代码实现的全流程开发方法,深度理解了数字电路中时序控制与逻辑设计的核心原理。学习到很多内容,掌握了多种方式点亮LED,实现LED灯光管理和控制,按键控制。

八、参考链接

FPGA学习(二)------实现LED流水灯_de2-115引脚分配-CSDN博客

[FPGA基础学习]实现流水灯与按键暂停_上升沿fpga使用按键控制暂停与启动-CSDN博客

相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
ZPC82104 天前
docker 镜像备份
人工智能·算法·fpga开发·机器人
ZPC82104 天前
docker 使用GUI ROS2
人工智能·算法·fpga开发·机器人
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习