洗子の小窝 但行己路 无问山海
歌曲封面 未知作品
  • 歌曲封面Can’t Get It RightBowAsWell
  • 歌曲封面RockstarHYBS
  • 歌曲封面Ontario GothicFoxes in Fiction
  • 歌曲封面When She's GoneJosh Fudge
  • 歌曲封面All The Way HomeWIM
  • 歌曲封面Love Is Just a MemoryMichael Seyer
  • 歌曲封面The Best ThingElectric Youth
  • 歌曲封面Gentle Showerパイパー
  • 歌曲封面Here With Med4vd
  • 歌曲封面CarolineAHV
  • 歌曲封面i walk this earth all by myself - Reeperbahn Festival CollideREEPERBAHN FESTIVAL COLLIDE
  • 歌曲封面Bloody Mary GirlShe Her Her Hers
  • 歌曲封面ArrowsShe Her Her Hers
  • 歌曲封面Episode 33She Her Her Hers
  • 歌曲封面BikeADOY
  • 歌曲封面Was It Something I SaidMyKey
  • 歌曲封面霓虹甜心马赛克
  • 歌曲封面7 in the MorningJosh Fudge
  • 歌曲封面Dinner AloneJosh Fudge
  • 歌曲封面一如往常逃走鮑伯
Title

【FPGA】按键控制LED灯实验

洗子

·

📝学习录

·

Article
⚠️ 本文最后更新于2024年06月07日,已经过了220天没有更新,若内容或图片失效,请留言反馈
这是洗子的第二个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口,并烧录,最终结果如下:

现在已有 254 次阅读,0 条评论,1 人点赞
Comment:共0条
发表
搜 索 消 息 足 迹
你还不曾留言过..
你还不曾留下足迹..
博主 欢迎访问洗子的小站,常来访哦! 不再显示
博主