3. 按键控制LED

按键控制LED 学习卡片

任务目标

在FPGA开发板上实现按键控制LED:按下按键时LED点亮,松开时LED熄灭。核心学习点是异步输入的同步化处理与机械按键去抖。

硬件连接

  • 按键KEY0连接到开发板引脚(默认低有效,按下为低电平)
  • LED0连接到开发板引脚(高电平点亮)
  • 系统时钟50MHz,复位按键低有效

核心原理

机械按键在按下和释放瞬间会产生约5~20ms的电平抖动,直接采样会得到毛刺信号。本设计采用两级同步器消除亚稳态,再用计数器对稳定电平持续采样,只有电平连续保持约10ms(50MHz下计数500_000次)才判定为有效状态变化,从而实现去抖。

完整代码

<code>import chisel3._
import chisel3.util._

class KeyDebounce extends Module {
  val io = IO(new Bundle {
    val key_in  = Input(Bool())   // 按键输入,低有效
    val led_out = Output(Bool())  // LED输出,高电平点亮
  })

  // 两级同步器消除亚稳态
  val sync_0 = RegNext(io.key_in)
  val sync_1 = RegNext(sync_0)

  // 去抖计数器:稳定电平持续10ms(50MHz时钟)
  val CNT_MAX = 500000.U(20.W)
  val cnt = RegInit(0.U(20.W))
  val stable_level = RegInit(true.B)  // 初始为高(按键未按下)

  // 当输入电平与当前稳定电平不同时,开始计数
  val diff = sync_1 =/= stable_level

  when(diff) {
    cnt := cnt + 1.U
    when(cnt === CNT_MAX) {
      stable_level := sync_1   // 电平稳定,更新状态
      cnt := 0.U
    }
  } .otherwise {
    cnt := 0.U
  }

  // 按键低有效:按下时stable_level为false,LED点亮
  io.led_out := !stable_level
}

object KeyDebounceMain extends App {
  println(getVerilogString(new KeyDebounce))
}</precode>

运行方式

  1. 安装Chisel环境(需Java 8+和sbt)
  2. 将上述代码保存为 KeyDebounce.scala
  3. 在项目目录下创建 build.sbt,内容如下:
scalaVersion := "2.13.10"
libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.6.0"
  1. 运行 sbt run,控制台将输出Verilog代码
  2. 将生成的Verilog代码添加到Vivado工程,绑定引脚后综合烧录

验证方法

  • 仿真验证:编写测试平台,输入带抖动的按键信号(高低电平交替变化约1ms),观察LED输出在约10ms后跟随按键稳定状态变化,且抖动期间LED不翻转
  • 板级验证:烧录后按下按键,LED应立即点亮;松开后LED熄灭。快速连续按动按键,LED状态应稳定跟随,无闪烁

判定标准

仿真中LED输出在按键电平稳定后延迟约10ms才变化,且抖动期间输出保持不变;板级测试中按键按下/松开与LED亮灭一一对应,无异常闪烁即通过。