洗子の小窝 但行己路 无问山海
歌曲封面 未知作品
  • 歌曲封面普通人生海洋Bo
  • 歌曲封面想想就烦Mikey-18
  • 歌曲封面卑微角色SevenJ李百万
  • 歌曲封面唉 (pt.5)河南说唱之神
  • 歌曲封面真心话就是大冒险河南说唱之神
  • 歌曲封面解决海底时光机
  • 歌曲封面Bloody Mary GirlShe Her Her Hers
  • 歌曲封面天天余佳运
  • 歌曲封面你(正式版)郑润泽
  • 歌曲封面小心温差Matt吕彦良
  • 歌曲封面于是郑润泽
  • 歌曲封面彻夜郑润泽

本网站由提供CDN加速服务

本网站由腾讯COS提供云存储服务

粤ICP备2022146502号-1

萌ICP备20240178号

Copyright © 2024 洗子の小窝

网站已运行 47 天 4 小时 5 分

Powered by Typecho & Sunny

2 online · 110 ms

Title

【FPGA】按键控制LED灯实验

洗子

·

Article
这是洗子的第二个FPGA实验啦!本节的实验任务是使用开发板上的两个按键控制两个LED灯的亮灭。按下不同的按键,LED灯呈现不同的效果。以下是本次实验要实现的效果,其中LED流水或闪烁的间隔时间为0.5s。
按键状态LED显示效果
无按键按下两个LED灯全灭
按下PL_KEY0两个LED灯交替闪烁
按下PL_KEY1两个LED灯同闪同灭

一、项目总体思路的构建

我们查阅开发板图,可以看到初始状态下,按键是没有闭合的,即该支路不成回路。我们想要当按键闭合时灯LED可以点亮,就需要获取按键的状态,因此这里定义一个key[1:0]来表示按键状态。当按键没有按下时,PL_KEY端口会被+3.3v上拉到电阻值为1,所以当key=11时表示按键都没有被按下;当key=10时表示PL_KEY1没有被按下,按键PL_KEY0被按下;当key=01时表示PL_KEY0没有被按下,按键PL_KEY1被按下。

接下来,我们绘制一个思维导图,确定这个按键控制LED灯模块所需要的内容。与上个实验(流水灯)相比这里的输入端口就是多了上述将的KEY。现在我们可以实现识别LED的显示效果,但是每一种显示效果都各有两种状态。那么如何分辨两种状态呢?我们新增加一个标志位led_flag,当KEY=10,led_flag=0,PL_KEY0不亮,PL_KEY1亮;当KEY=10,led_flag=1,PL_KEY0亮,PL_KEY1不亮。当KEY=01时同理。

我们根据上述的思路绘制图像来模拟流水灯功能的实现,以下图像包含:LED灯的变化情况,架构的设计图如下:

按键控制LED模块思维导图

波形图如下:

二、编写代码

进入rtl文件夹,在此文件夹内建立key_led.v文件开始编写程序。

  1. 建立key_led模块,定义输入输出变量
♾️ verilog 代码:
module key_led(
    input                sys_clk, 
    input                sys_rst_n,
    input        [1:0]    key,
    output    reg    [1:0]    led
);
endmodule
  1. 确定系统时钟代码块

我们使用计算器可以计算出系统时钟cnt为25位二进制数,因此位宽为[24:0]。我们建立一个循环当系统时钟处于上升沿或者复位信号处于下降沿时,执行代码块。如果复位信号为低电平时,系统时钟清零。如果系统时钟处于1~249999ns时,系统时钟一次累加1ns。

♾️ verilog 代码:
reg    [24:0]    cnt;
parameter CNT_Max = 25'd25000000;//计数器计时0.5s

//计数器计时0.5s
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        cnt <= 25'd0;
    else if(cnt < (CNT_Max - 25'd1))
    //else if(cnt < (25'd25 - 25'd1))    
        cnt <= cnt + 25'd1;
    else
        cnt <= 25'd0;
end
  1. 确定LED状态标志位的切换代码块

首先先初始化led_flag为0,当系统时钟为最大值时,就取反,这样便可实现闪烁时间为0.5s,同时得到LED状态的标志位。

♾️ verilog 代码:
reg            led_flag;

//LED状态标志位的切换        
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        led_flag <= 1'b0;
    else if(cnt == (CNT_Max - 25'd1))
        led_flag <= ~led_flag;
    else
        led_flag <= led_flag;
end      
  1. 确定LED控制代码块(根据KEY和led_flag的值,对led进行控制)
KEYled_flagLED灯状态
11/00
10001
10110
01000
01111
♾️ verilog 代码:
//LED控制(根据KEY和led_flag的值,对led进行控制)
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        led <= 2'b00;
    else begin
        case(key)
            2'b11 : led <= 2'b00;
            2'b10 : 
            if(led_flag == 1'b0)
                led <= 2'b01;
            else
                led <= 2'b10;
            2'b01 :     
            if(led_flag == 1'b0)
                led <= 2'b00;
            else
                led <= 2'b11;
            default :     ; 
        endcase
    end
end
  1. 完整代码
♾️ verilog 代码:
module key_led(
    input                sys_clk, 
    input                sys_rst_n,
    input        [1:0]    key,
    output    reg    [1:0]    led
);

reg    [24:0]    cnt;
reg            led_flag;
parameter CNT_Max = 25'd25000000;//计数器计时0.5s
//parameter CNT_Max = 25'd25;//计数器计时500ns


//计数器计时0.5s
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        cnt <= 25'd0;
    else if(cnt < (CNT_Max - 25'd1))
    //else if(cnt < (25'd25 - 25'd1))    
        cnt <= cnt + 25'd1;
    else
        cnt <= 25'd0;
end

//LED状态标志位的切换        
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        led_flag <= 1'b0;
    else if(cnt == (CNT_Max - 25'd1))
        led_flag <= ~led_flag;
    else
        led_flag <= led_flag;
end  
    
//LED控制(根据KEY和led_flag的值,对led进行控制)
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        led <= 2'b00;
    else begin
        case(key)
            2'b11 : led <= 2'b00;
            2'b10 : 
            if(led_flag == 1'b0)
                led <= 2'b01;
            else
                led <= 2'b10;
            2'b01 :     
            if(led_flag == 1'b0)
                led <= 2'b00;
            else
                led <= 2'b11;
            default :     ; 
        endcase
    end
end

endmodule

三、验证仿真

在sim文件夹建立tb_key_led.v文件,用来编辑仿真文件。tb文件与上一个实验(流水灯)大同小异这里就不多做赘述。代码如下:

♾️ verilog 代码:
`timescale 1ns/1ns  //仿真单位/仿真精度
module tb_key_led();

parameter CLK_PERIOD = 20;
reg         sys_clk;
reg         sys_rst_n;
reg [1:0]   key;

wire [1:0]  led;

initial begin
    sys_clk <= 1'b0;
    sys_rst_n <= 1'b0;
    key <= 2'b11;
    #200
    sys_rst_n <= 1'b1;
    #2000
    key <= 2'b11;    //按键没有按下
    #2000
    key <= 2'b10;    //按下KEY0
    #2000
    key <= 2'b01;    //按下KEY1
    #2000
    key <= 2'b11;    //按键没有按下
end

always #(CLK_PERIOD/2) sys_clk =~ sys_clk;
    

key_led   u_key_led(
    .sys_clk        (sys_clk    ),
    .sys_rst_n      (sys_rst_n  ),
    .key            (key        ),
    .led            (led        )
    );
endmodule

由仿真图可看出,当KEY=11时,两个LED灯都不亮;当KEY=10时,LED以流水灯形式闪烁,流水间隔为0.5s(仿真为了提高效率采用500ns)。

由仿真图可看出,当KEY=01时,两个LED灯同闪同灭,时间间隔为0.5s(仿真为了提高效率采用500ns)。综上所述,RTL代码无误。

四、上板验证

查询开发板资料,设置好IO口,并烧录,最终结果如下:

现在已有 0 条评论,1 人点赞
Comment
发表
搜 索 消 息 足 迹
你还不曾留言过..
你还不曾留下足迹..
博主