FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)

一、典型基础入门,小试牛刀 : 1.不同按键控制不同LED亮灭 大家不妨回忆下从上学到工作,有趣的是仿佛在学习任何一款嵌入式芯片的编程时,几乎不约而同地都是先从按键、发光二极管、蜂鸣器入门的,这些外设虽然看上去很基础但也具有很强的代表性,所以第一章里笔者通过两个基础例程,帮助大家自然而然地入门FPGA设计,闲话不多说我们直接步入正题。
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

按键和发光二极管可以说是嵌入式设备中最为常见的外设了,本身应用场景有很多,例如发光二极管从电路上通过CPU控制高低电平的输出可以用来指示通信传输速度、设备运行状态等,按键从电路上分为独立式和扫描式可以用来控制少数普通按键、编码器以及行列式矩阵按键。
如图1和图2所示,分别是独立式和扫描式按键电路设计,大家可以清楚地看到对CPU而言,每个独立式按键都需要一个IO口对其进行连接,而扫描式按键则不需要IO口在数量上和按键进行逐一匹配,只用在同一时刻通过对按键矩阵进行扫描,即可判断出当前时刻哪个按键被按下,硬件上节约了主CPU的IO口,软件上也简化了代码逻辑,在这里我们不对扫描式的代码设计展开详细的叙述,例程中只对项目中用得比较多的独立式按键进行分析讲解。
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图1 独立式的按键电路
【FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)】FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图2 扫描式的按键电路
通过FPGA基础知识专栏的实践学习,其实大家已经掌握了按键消抖处理设计,如果忘记的话,那么我们在这里再回过头简单地复习一下,如图3所示是豌豆开发板Artix7上按键电路,非常普遍的硬件设计,按键一端接地,而另外一端接一个上拉电阻再连到Artix7芯片的引脚上,所以当按键断开时是高电平,闭合时是低电平,但是因为按键具有机械特性,在断开和闭合的时候都会有抖动有误触发的情况,这个时间通常是小于10ms的,如图4所示是按键闭合、断开以及稳定时的电平变化示意图,所以不管是MCU还是FPGA程序设计里都会对按键检测去做20ms的消抖处理。
如表1为这个模块的信号列表,本模块的输入信号有:clk系统时钟信号、rst_n系统复位信号、外部4个按键key_in引脚输入信号;输出信号有:key _vld按键消抖有效信号、key_value按键键值信号,在整体工程的设计中,通过例化key_vld和key_value两个输出信号,即可得到外部按键哪个按键被按下了,其他模块再据此去做后续的处理。
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图3 豌豆开发板Artix7上按键电路FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图4 按键闭合和断开抖动电平变换示意图

信号列表
信号名
I/O
位宽
clk
I
1
rst_n
I
1
key_in
I
4
key_vld
O
1
key_value
O
4
表1 key_scan模块信号列表
为了方便理解,我们可以把这个模块的整体流程用一个流程示意图来表示,如图5所示,大家再根据这个流程示意图去思考下,我们应该如何去合理划分这个模块中状态机的状态,以及不同状态之间应该怎么去跳转。
首先把整个流程提取出来,可以分为四个状态,即IDLE空闲状态、CHECK_KEYIN检测按键闭合状态、KEEP_KEYIN按键持续闭合状态、CHECK_KEYOUT检测按键断开状态。然后设计debounce_cnt消抖的计数器,用来计时20ms的按键闭合断开的消抖时间,显然在状态CHECK_KEYIN和CHECK_KEYOUT的时候,消抖计数器再去计时20ms时间,所以把debounce_cnt的加一条件设置为:state_c == CHECK_KEYIN || state_c == CHECK_KEYOUT,把结束条件设置为add_debounce_cnt && debounce_cnt== TIM_20MS-1。
接着再去考虑状态跳转,根据图5的按键消抖处理设计流程图,IDLE状态可以跳转到CHECK_KEYIN状态;CHECK_KEYIN状态在消抖20ms后通过判断key_in的输入值,则可以跳转到IDLE状态或者KEEP_KEYIN状态;KEEP_KEYIN状态在检测到key_in的输入值不为4'b1111时便会跳转到CHECK_KEYOUT状态;CHECK_KEYOUT状态在消抖20ms后通过判断key_in的输入值,则可以跳转到IDLE状态或者KEEP_KEYIN状态。
最后我们需要去注意输出信号key_vld以及key_value的设计,大家想一想这两个关键输出信号应该什么时候被置位和赋值,通过图5的设计流程图可以看出来,我们应该在CHECK_KEYIN状态跳转到KEEP_KEYIN状态的时刻,把key_value用key_in的逐位取反来赋值,这时key_value记录下当前哪个按键被按下,当用户断开按键时即在CHECK_KEYIN跳转到IDLE状态的时刻,再去置高一个周期的key_vld把key_value的值送到其他处理模块,key_vld和key_value都用时序逻辑去产生,在搞清楚整个模块的逻辑以后,把代码写漂亮已经不困难了,如图6所示是其对应的代码设计供参考,当然也可以有不同的状态机划分方法,只有符合预期设计要求即可,答案完全不唯一。
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图5 按键消抖处理设计流程图

如图7所示,在把key_scan模块产生的key_value和key_vld信号例化到顶层模块即可,通过key_value和key_vld再对LED进行点灯操作。
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图6 按键消抖处理模块的代码设计
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片
FPGA20个例程|FPGA 20个例程篇(1.不同按键控制不同LED亮灭)
文章图片

图7 按键控制LED亮灭顶层文件的例化


源工程代码下载链接:
链接:https://pan.baidu.com/s/1oSgM-EDwN2Lt23LfaUTLBQ
提取码:58ah

    推荐阅读