当前位置: 首页 > article >正文

【FPGA实战】Verilog实现DE2-115的流水灯控制

【FPGA实战】Verilog实现DE2-115的流水灯控制

  • 一、实验要求
  • 二、实现步骤
    • 2.1 环境配置
      • 2.1.1 VScode与Quartus的联动
        • step1 下载Verilog插件
        • step2 Quartus绑定VScode
    • 2.2 实现思路
      • 2.2.1 LedBlink.v
      • 2.2.2 fenpin.v
        • fenpin.v代码
      • 2.2.1 display.v
        • 1)对于流水灯:
        • 2)重置按钮
        • 3)暂停键:
        • display.v代码
        • LedBlink.v代码
    • 2.3 烧录实验
      • 2.3.1 编译以及一些问题
      • 2.3.2 烧录及一些问题
      • 2.3.3 实验效果
  • 三、实验总结
  • 四、参考博客

一、实验要求

在DE2-115开发板上,用Verilog设计一个LED流水灯实验:用6个LED完成周期为1秒的跑马灯效果。

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

增加流水灯的按键暂停、按键恢复功能,使得当按暂停键时流水灯暂停,再按一下继续流水灯。

1)安装VScode,在其中下载安装Verilog-HDL/SystemVerilog插件(实现Verilog代码的语法高亮/自动补全);

二、实现步骤

2.1 环境配置

工欲善其事必先利其器,在Quartus自带的文本编辑器上编写代码不太适应而且也不美观,于是我们可以引入程序员神器——VScode。

2.1.1 VScode与Quartus的联动

step1 下载Verilog插件

这里假设你已经下载了VScode,首先我们要下载Verilog插件。
在这里插入图片描述
安装扩展后,可以尝试创建新的文本文件,如下图
在这里插入图片描述
点击选择语言,搜索Verilog
在这里插入图片描述
选择第一个,之后我们写代码就很方便了~
在这里插入图片描述

step2 Quartus绑定VScode

打开Quartus,在菜单栏里找到
tools->Options->Preferred Text Editor
在这里插入图片描述
选择Custom,在下面输入

"E:\Microsoft VS Code\Code.exe" -g %f:%l

当然,引号内是你VScode的.exe的文件地址,找不到怎么办?
右键快捷键点击打开文件所在位置就能找到了!
这之后我们用Quartus操作Verilog文件都会自动弹出VScode编辑了。

(当然,这里也有个bug,比如你新建一个Verilog文件的话可能没办法命名,假如你知道怎么解决的话请在评论告诉我。)

2.2 实现思路

一开始拿到这个题目是不是没有思路?那我们就先AI!
AI给出的思路是——

要分成顶层模块分频模块显示模块顶层模块连接各个子模块,分频模块负责将系统时钟分频到合适的频率,显示模块负责控制LED的状态变化

2.2.1 LedBlink.v

因此,top顶层模块(LedBlink.v)需要实现各模块的连接;

具体代码我们先按下不表,后面再展示。

2.2.2 fenpin.v

分频模块(fenpin.v)需要将系统时钟分频到合适的频率。

晶振频率为50MHz,则一个时钟周期 T = 1 ( 50 × 1 0 6 ) = 0.02 u s T= {1 \over (50×10^6)} = 0.02us T=(50×106)1=0.02us

而我们要使得1s闪六个等就相当于 1 6 s {1\over6}s 61s闪一个灯,约166.666ms,于是,闪烁一次我们需要 166666 0.02 = 83333 {166 666\over0.02}=83333 0.02166666=83333个周期,也就是计数83,333次。

因此我们需要一个counter能够存储从0到83332的整数,也就是 2 23 − 1 2^{23}-1 2231(即8388607)。

因此,我们需要一个寄存器cnt作计数器

reg [22:0] cnt;

在复位信号rst_n的控制下可以回到初始状态,在正常工作时,计数器cnt会随着时钟信号clk不断增加,当cnt数到8333332时,会发出一个高电平的使能信号en,然后cnt重新开始计数,如此循环。这个使能信号en就可以用来控制其他模块按照这个特定的周期进行工作。

fenpin.v代码
module fenpin(
    input   clk,
    input   rst_n,
    output  reg en
);

reg [22:0] cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt<=23'd0;
        en<=1'b0;
    end else begin
        if(cnt==23'd8_333_332) begin
            cnt<=23'd0;
            en<=1'b1;
        end else begin
            cnt<=cnt+1'b1;
            en<=1'b0;
        end
    end
end

endmodule

2.2.1 display.v

显示模块的主要功能是实现走马灯也就是流水灯的闪烁控制,暂停键的控制逻辑以及复位键的逻辑。

1)对于流水灯:

我们可以有两种方法,一种是case语句列举六个灯的逻辑语句。

case (cnt1)
	3'b000 : led <= 6'b000001;
	3'b001 : led <= 6'b000010;
	3'b010 : led <= 6'b000100;
	3'b011 : led <= 6'b001000;
	3'b100 : led <= 6'b010000;
	3'b101 : led <= 6'b100000;
	default: led <= led;
endcase

第二种就是用位移实现闪烁。

	if(!rst_n) begin
        led <= 6'b000001;
    end else if(en_ls && !pause_reg) begin
        led <= {led[0], led[5:1]};
    end
2)重置按钮

在我们单片机学习过程中,对于按键我们一般是要采取消抖的,因此我们还要引入一些变量。

刚开始(复位):如果按下了 “重置按钮”(rst_n 是低电平),所有的信号都会被清空,回到初始状态。

每次秒表滴答一下(时钟上升沿),就把按键信号放到 sync_key 里,同时把 sync_key 之前的状态记录到 key_delay 里。

如果 sync_key 和 key_delay 不一样,说明按键状态变了,这时候 “消抖计数器” 就开始工作,把计数设为 999999,然后慢慢减 1;等 “消抖计数器” 减到 0 了,就看看按键是按下去了(sync_key[1] 是 0)还是松开了(sync_key[1] 是 1)。

3)暂停键:

要实现暂停,我们可以加入 key_pressed 用于记录按键状态看是否按下。
按键从按下状态变为释放状态时,翻转暂停状态。

display.v代码

因此,对于顶层模块display.v的代码如下:

module display(
    input   clk,
    input   rst_n,
    input   en_ls,
    input   pause_key,
    output reg pause_reg,
    output reg [5:0] led
);

reg [1:0] sync_key;
reg  key_delay;
reg [19:0] debounce_cnt;
reg key_pressed; 

// 按键消抖逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sync_key <= 2'b11;
        key_delay <= 1'b1;
        debounce_cnt <= 20'd0;
        key_pressed <= 1'b0;
    end  else begin
        sync_key <= {sync_key[0], pause_key};
        key_delay <= sync_key[1];
        if(sync_key[1] != key_delay)
            debounce_cnt <= 20'd999_999;
        else if (debounce_cnt != 0)
            debounce_cnt <= debounce_cnt - 1;
        if(debounce_cnt == 0 && sync_key[1] == 1'b0)
            key_pressed <= 1'b1;
        if(debounce_cnt == 0 && sync_key[1] == 1'b1)
            key_pressed <= 1'b0;
    end
end

reg key_release_flag;  // 记录按键释放事件
always @(posedge  clk or negedge rst_n) begin
    if(!rst_n) begin
        pause_reg <= 1'b0;
        key_release_flag <= 1'b0;
    end else begin
        // 当按键从按下状态变为释放状态时,翻转暂停状态
        if(key_pressed == 1'b1 && sync_key[1] == 1'b1)
            key_release_flag <= 1'b1;
        else
            key_release_flag <= 1'b0;
        
        if(key_release_flag)
            pause_reg <= ~pause_reg;
    end
end

// LED闪烁控制逻辑
always @(posedge  clk or negedge rst_n) begin
    if(!rst_n) begin
        led <= 6'b000001;
    end else if(en_ls && !pause_reg) begin
        led <= {led[0], led[5:1]};
    end
end

endmodule
LedBlink.v代码

因此,对于顶层模块LedBlink.v的代码如下:

module LedBlink(
    input   clk,//50Mhz
    input   rst_n,//复位信号
    input   pause_key,
    output [5:0] led
);

//模块间连接信号
wire en_ls;
wire pause_reg;//暂停状态信号

fenpin u_fenpin(
    .clk    (clk),
    .rst_n  (rst_n),
    .en     (en_ls)
);

display u_display(
        .clk    (clk),
    .rst_n  (rst_n),
    .en_ls  (en_ls),
    .pause_key  (pause_key),
    .pause_reg  (pause_reg),
    .led  (led)
);

endmodule

2.3 烧录实验

2.3.1 编译以及一些问题

代码都写得差不多了,现在就到了编译的时候了。
在编译的时候,会发现VScode写代码后,找不到文件,这怎么编译啊?
因此,我们需要再做:
在这里插入图片描述
双击Files,点击Add All
在这里插入图片描述
然后,右键点击我们的top文件LedBlink,设置为顶层文件,编译
在这里插入图片描述
没有问题后,分配引脚
在这里插入图片描述
暂停键是KEY2,复位键是KEY1

2.3.2 烧录及一些问题

烧录的时候也出了一点小情况,就是发现效果不是我代码预计的效果。
经过搜索发现,错误是因为我用的上一次的文件,因此,还需要将之前的文件给delete,然后add新的.sof文件
在这里插入图片描述
在这里插入图片描述
然后就烧录成功了

2.3.3 实验效果

【FPGA入门】DE2-115按键控制流水灯

三、实验总结

以上是我最终的实验内容,其实一开始并没有分层,只是简单地在一个模块中实现流水灯,分层次设计后也遇到了一些问题,比如一开始我的暂停键是只有一直按着暂停键流水灯才能停止,折腾半天最后引入了key_pressed变量得以解决。

这次实验的突破在于学会了规范化分层设计,这无疑对以后的开发是有益处的。

同时,在实现的过程中,从一开始的毫无头绪到最终比较好的实现效果,AI工具起到了很大的作用。利用DS深度思考打开思路学习其中的逻辑思维,再一步步地进行理解和改良优化。

四、参考博客

VSCode配置verilog环境(代码提示+自动例化+格式化)
Nios实验入门——用Verilog编程方式完成LED流水灯显示并使用串口输出“Hello Nios-II”字符到笔记本电脑
【整理】DE2-115引脚列表 word版


http://www.kler.cn/a/586565.html

相关文章:

  • 四、Jmeter工具接口脚本编写
  • 用Python实现持续集成与部署(CI/CD)流程:自动化测试、构建与部署
  • 什么是强哈希算法pbkdf2(Password-Based Key Derivation Function)
  • 向量数据库:A Brief Introduction
  • 04_Linux驱动_05_pinctrl子系统
  • 阿里云oss开发实践:大文件分片、断点续传、实时进度 React+Node+Socket.IO
  • 【接口封装】——22、读写文件
  • iOS底层原理系列03-Objective-C运行时机制
  • ubuntu24.04执行nvidia-smi报错,实际生产报错,处理过程
  • Docker部署Laravel项目
  • 数据结构与算法-图论-二分图
  • Unity3D手游内存深度优化指南
  • PL/SQL语言的神经网络
  • Python语言的代码重构
  • ubuntu20.04装nv驱动的一些坑
  • 《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(54)落宝金钱寻最优 - 跳跃游戏(贪心策略)
  • 洛谷 P1068 [NOIP 2009 普及组] 分数线划定 python
  • 【Kubernets】Deployment 和 StatefulSet 有什么区别?什么时候用 StatefulSet?
  • 内存泄漏的防范:检测与预防
  • 稳定运行的以Oracle数据库为数据源和目标的ETL性能变差时提高性能方法和步骤