FPGA实战-----点灯大师(1)led灯闪烁流水跑马+按键

【FPGA工程(1)-------点灯大师(1)led灯闪烁流水跑马+按键】

FPGA实战



前言

用verliog语言点亮FPGA开发板上的led灯是最最最最最基础的操作。
这里用的EP4CE6F17C8开发板,上边一共有四个led灯珠,可以实现简单的例如4个全亮,流水灯,跑马灯以及相比之下难了一点的呼吸灯等等等等效果。
本文最终目的就是实现用按键切换led灯的运动模式。做一个真正的“点灯大师1.0”。


一、认识led灯珠

开发板led原理图

四个led灯共阴极接地,所以需要高电平导通led灯。也就是代码中led=1,运用到电路中led才会亮。


二、工程模块设计

1.系统架构图

系统架构图
图中,时钟、复位、按键为输入信号,led为输出信号,这个图看起来简单没什么难度。


2.led闪烁

在上边已经说过了:

四个led灯共阴极接地,所以需要高电平导通led灯。也就是代码中led=1,运用到电路中led才会亮

所以对于代码中的led:1亮0灭。

*************************<led>**********************************************
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        led <= 4'b0001 ;    //复位时亮一个     
    end
    
-----------------------------<闪烁>-----------------------------------------
    else if(key_in[1]==0 && end_cnt_300ms)begin   //按下key_in[1]
        led <= ~led ;     //led[3:1]和led[0]分开闪烁
    end

复位时亮一个为了之后的流水灯设计方便,后边认真看就看懂了。
这里长按key[1]时四个led灯珠会在计数器每次结束计数后变成与当前亮灭状态相反的状态。从而实现了闪烁。


3.流水灯实现-----计数器

流水灯示意图
这里实现一个从左向右流动的流水灯。如图,四个灯珠将从左向右依次亮灭。而实现这一效果的方法需要在代码中用到“计数器”这样的东西。
计数是一种最简单基本的运算,计数器就是实现这种运算的逻辑电路,计数器在数字系统中主要是对脉冲的个数进行计数,以实现测量、计数和计时的功能。
计数器也是在 FPGA 设计中最常用的一种时序逻辑电路,根据计数器的计数值我们可以精确的计算出 FPGA 内部各种信号之间的时间关系,每个信号何时拉高、何时拉低、拉高多久、拉低多久都可以由计数器实现精确的控制。而让计数器计数的是由外部晶振产生的时钟,所以可以比较精准的控制具体需要计数的时间。计数器一般都是从 0 开始计数,计数到我们需要的值或者计数满溢出后清零,并可以进行不断的循环
下边是本篇代码中的计数器模版。

//------------------------<计时器>---------------------------
parameter  MAX_300MS = 24'd15_000_000 ;
reg		[23:0]	cnt_300ms	   	;//300ms计数器
wire			add_cnt_300ms	;
wire			end_cnt_300ms	;

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_300ms <= 'd0 ;
    end 
    else if(add_cnt_300ms)begin 
        if(end_cnt_300ms)begin 
            cnt_300ms <= 'd0 ;
        end
        else begin 
            cnt_300ms <= cnt_300ms + 1'b1 ;
        end 
    end
end 

assign add_cnt_300ms = key_in[1]==0 || key_in[2]==0 || key_in[3]==0 ;//按下三个按键开始计时
assign end_cnt_300ms = add_cnt_300ms && cnt_300ms == MAX_300MS - 1;

在这里 add_cnt_300msend_cnt_300ms 分别是这个计数器的开始条件和结束条件。这两个条件相当的重要。决定了这个计数器是否能用。
当长按三个按键时计数器会开始计数,且每经过一个周期就加一;当计数到MAX_300MS - 1时,计数器归零
我们计数的时钟就用系统时钟50MHz,换算成时间为**[1/(50_000_000)Hz]s = 0.000_000_02s**,也就是说50MHz频率的时钟一个周期的时间为20ns。
时钟算法
所以代码中计数器的最大值是14,999,999(因为从0开始计数,所以0也算一位)。
而最大值的位宽看图中的二进制有24位,所以MAX_300MS=24'd14,999,999(代码块end_cnt_300ms定义中用了“-1”,都一样,个人习惯) 。
以上是计数器的运用,知道了这些,接下来就是形成流水灯了。

*************************<led>**********************************************
 always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        led <= 4'b0001 ;//复位时亮一个     
    end
-----------------------------<流水灯>-----------------------------------------
 else if(key_in[2]==0  && end_cnt_300ms)begin
        led <= {led[0],led[3:1]};//长按key[2]从左到右的流水灯
    end

上述代码是工程中形成流水灯的代码,不要震惊,对,就是这么短。
代码中四个led在复位的情况下让其亮一个就是为了让流水灯的代码可以不那么繁琐。不亮也可以做出来,就是用case语句,不过下面的跑马灯中用了case代码,这里就不用了。
代码中“{led[0],led[3:1]}”运用了“拼接符{}”,这样写相当于让led[0]移到了led[3]的左边,led[0]变成了新的led[3],led[1]顶替了原来的led[0],而在换位过程中,led[0]一直亮着,所以就呈现出了流水的效果。


4.跑马灯实现------case语句

其实我也不知道跑马灯是啥,大体也就下面那么个跑法,俺也不知道为啥叫跑马灯,只是都这么叫(-_-')(图画得不好,献丑了)。
跑马灯示意图
要实现跑马灯,在这里使用了一个“case语句”
case语句检查给定的表达式是否与列表中的其他表达式之一相匹配,并据此进行分支。它通常用于实现一个多路复用器。
简单来说,就是定义一个数据,有一些不一样的数值,让led(需要改变的数)在这个数据相应的数值去改变灯珠的亮灭。也可以说随着这个数据的变化,去改变led,这个数据是什么值的时候,led必须是一个相对应的亮灭。

case(控制表达式/值)

分支表达式:执行语句

default:执行语句

endcase
以case关键字开始,以endcase关键字结束。在括弧内的表达式将被精确地评估一次,并按其编写顺序与备选方案列表进行比较,与给定表达式匹配的备选方案的语句将被执行。

//------------------------<state>---------------------------
reg   [2:0] state  ;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state <= 0 ;
    end
    else if(key_in[3]==0  && end_cnt_300ms)begin
            state <= state + 1 ;
        end
        else begin
            state <= state ;
        end
    end

***********************<led跑马>**********************************
else if(key_in[3]==0)begin//长按key[3]跑马灯
        case (state)
            3'd0 : led <= 4'b0001 ;
            3'd1 : led <= 4'b0011 ;
            3'd2 : led <= 4'b0111 ;
            3'd3 : led <= 4'b1111 ;
            3'd4 : led <= 4'b0111 ;
            3'd5 : led <= 4'b0011 ;
            default: led <= 4'b0001 ;
        endcase
    end

在代码中,state就是case中的“控制表达式/值”,将led亮灭情况(执行语句)与state的编写顺序一一对应并执行。
依靠这些代码,实现led跑马灯。


总体代码

/*
 * @Projcet: led
 * @Author: Yang.
 * @Date: 2023-08-22 18:59:46
 * @LastEditors: Yang.
 * @LastEditTime: 2023-09-29 21:58:06
 */
//---------<模块及端口声名>------------------------------------------------------
module led(  
    input             clk       ,
    input             rst_n     ,
    input      [3:1]  key_in    ,   //key_in[0]给到E15引脚复位
    output reg [3:0]  led  
);
//------------------------<参数定义>---------------------------
reg   [2:0] state  ;
parameter  MAX_300MS = 24'd15_000_000 ;
//------------------------<计时器>---------------------------

reg		[23:0]	cnt_300ms	   	;//300ms计数器
wire			add_cnt_300ms	;
wire			end_cnt_300ms	;

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_300ms <= 'd0 ;
    end 
    else if(add_cnt_300ms)begin 
        if(end_cnt_300ms)begin //计满归零
            cnt_300ms <= 'd0 ;
        end
        else begin 
            cnt_300ms <= cnt_300ms + 1'b1 ;
        end 
    end
end 

assign add_cnt_300ms = key_in[1]==0 || key_in[2]==0 || key_in[3]==0 ;//按下三个按键开始计时
assign end_cnt_300ms = add_cnt_300ms && cnt_300ms == MAX_300MS - 1;//计满数

//------------------------<state>---------------------------
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state <= 0 ;
    end
    else if(key_in[3]==0  && end_cnt_300ms)begin
            state <= state + 1 ;
        end
        else begin
            state <= state ;
        end
    end


//****************************************************************
//--按键点灯
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        led <= 4'b0001 ;//复位时亮一个     
    end
//闪烁
    else if(key_in[1]==0 && end_cnt_300ms)begin//按下key_in[1]
        led <= ~led ;//led[3:1]和led[0]分开闪烁
    end
//流水
    else if(key_in[2]==0  && end_cnt_300ms)begin
        led <= {led[0],led[3:1]};//长按key[2]从左到右的流水灯
    end
//跑马
    else if(key_in[3]==0)begin//长按key[3]跑马灯
        case (state)
            3'd0 : led <= 4'b0001 ;
            3'd1 : led <= 4'b0011 ;
            3'd2 : led <= 4'b0111 ;
            3'd3 : led <= 4'b1111 ;
            3'd4 : led <= 4'b0111 ;
            3'd5 : led <= 4'b0011 ;
            default: led <= 4'b0001 ;
        endcase
    end
//初始
    else if(key_in[1] != 0 && key_in[2] != 0 && key_in[3] != 0)begin//没有按键按下的时候led回初始状态
        led <= 4'b0001 ;
    end
end



endmodule

总结

这就是上板效果,仿真懒得写了,有空再填。

点灯大师(1)------按键控制led

博客上传的工程都在百度网盘,有压缩包也有整个文件,可以自行下载。

链接:https://pan.baidu.com/s/1lQqqWZXfb3i6XHwkKf52zg
提取码:yang

点灯虽然简单也有意思,下一章进阶到点灯大师2.0,各位,来做呼吸灯。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值