3. 按键控制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>
运行方式
- 安装Chisel环境(需Java 8+和sbt)
- 将上述代码保存为
KeyDebounce.scala - 在项目目录下创建
build.sbt,内容如下:
scalaVersion := "2.13.10"
libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.6.0"
- 运行
sbt run,控制台将输出Verilog代码 - 将生成的Verilog代码添加到Vivado工程,绑定引脚后综合烧录
验证方法
- 仿真验证:编写测试平台,输入带抖动的按键信号(高低电平交替变化约1ms),观察LED输出在约10ms后跟随按键稳定状态变化,且抖动期间LED不翻转
- 板级验证:烧录后按下按键,LED应立即点亮;松开后LED熄灭。快速连续按动按键,LED状态应稳定跟随,无闪烁
判定标准
仿真中LED输出在按键电平稳定后延迟约10ms才变化,且抖动期间输出保持不变;板级测试中按键按下/松开与LED亮灭一一对应,无异常闪烁即通过。