news 2026/5/2 16:55:08

基于Vivado与Verilog的智能密码锁设计及EGO1开发板实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Vivado与Verilog的智能密码锁设计及EGO1开发板实现

1. 智能密码锁设计概述

用FPGA开发板实现密码锁听起来很高大上?其实没那么复杂。我去年用EGO1开发板做过一个完整的密码锁项目,实测下来效果很稳。这个设计核心就是用Verilog在Vivado里写状态机,控制按键输入、密码比对和显示逻辑。相比单片机方案,FPGA的并行处理特性让响应速度直接起飞,按下按键的瞬间就能得到反馈。

先说说整体架构。这个密码锁需要实现几个核心功能:4位密码输入(支持修改密码)、10秒倒计时、数码管状态显示、LED开锁指示。我在EGO1上实际测试时,发现最关键的三个技术点:状态机设计要够健壮(防止死锁)、按键消抖必须做好(机械按键的物理特性你懂的)、数码管动态扫描要流畅(不然会有闪烁感)。下面我会结合代码详细拆解每个模块的实现技巧。

2. 硬件平台与开发环境

2.1 EGO1开发板配置

EGO1这块板子对初学者特别友好,板载资源完全够用:Artix-7 FPGA芯片(XC7A35T-1CPG236C)、8个拨码开关、5个按钮、8位数码管、LED指示灯。我实测用到的资源包括:

  • 4个拨码开关模拟密码输入(实际项目可以用矩阵键盘)
  • 3个独立按钮(确认、修改、上锁)
  • 2个LED(F1开锁指示、F2关锁指示)
  • 4位数码管显示倒计时和状态

开发环境用Vivado 2020.1版本,新建工程时注意两点:器件型号选xc7a35tcsg324-1(EGO1的FPGA型号),语言选Verilog。有个坑要注意:EGO1的时钟是100MHz,但数码管扫描频率建议降到1kHz左右,否则会有残影。

2.2 管脚分配技巧

在xdc约束文件里,我是这样定义管脚的(关键部分):

set_property PACKAGE_PIN T17 [get_ports clk] # 100MHz时钟 set_property PACKAGE_PIN M17 [get_ports key_0] # 数字键0 set_property PACKAGE_PIN M18 [get_ports key_1] # 数字键1 ... set_property PACKAGE_PIN U16 [get_ports led_open] # F1开锁灯

建议先用板子的原理图核对引脚编号,我一开始把LED极性搞反了,调试时发现灯是反着亮的。输出信号记得加Pullup,防止初始状态不确定:

set_property PULLUP true [get_ports {led_open}]

3. 核心模块实现

3.1 状态机设计

密码锁的灵魂就是状态机。我设计了6个状态(用parameter定义):

parameter S_LOCK = 0; // 锁定状态 parameter S_INPUT = 1; // 输入密码 parameter S_COMPARE = 2; // 密码比对 parameter S_OPEN = 3; // 开锁状态 parameter S_MODIFY = 4; // 修改密码 parameter S_ERROR = 5; // 密码错误

状态转移逻辑用always块实现,注意非阻塞赋值(用<=):

always@(posedge clk) begin case(current_state) S_LOCK: if(key_pressed) begin next_state <= S_INPUT; timer <= 10; // 启动10秒倒计时 end S_INPUT: if(confirm_pressed) next_state <= S_COMPARE; else if(timer == 0) next_state <= S_LOCK; // 超时复位 // 其他状态转移... endcase end

实测发现状态机要加异步复位,否则上电时会卡在未知状态。我在工程里专门加了复位按钮:

always@(posedge clk or posedge reset) if(reset) current_state <= S_LOCK; else current_state <= next_state;

3.2 按键消抖模块

机械按键的抖动问题必须解决,否则会误触发。我的消抖方案是20ms延迟检测

module debounce( input clk, input button_in, output reg button_out ); reg [19:0] counter; always@(posedge clk) begin if(button_in != button_out) counter <= counter + 1; else counter <= 0; if(&counter) button_out <= button_in; // 计数器满时更新 end endmodule

在仿真时发现,如果直接用系统时钟(100MHz),计数器会很大。我做了个分频器,先降到1MHz再计数,这样counter计到20,000就够20ms。实际测试时,这个方案能稳定过滤掉按键抖动。

3.3 数码管动态显示

EGO1的8位数码管是共阳极的,需要动态扫描。我的方案是:

  1. 段选信号(dig_led)控制显示内容
  2. 位选信号(wei_led)轮流激活数码管

关键代码如下:

reg [2:0] scan_cnt; // 扫描计数器 always@(posedge clk_div) begin scan_cnt <= scan_cnt + 1; case(scan_cnt) 0: begin wei_led <= 8'b11111110; dig_led = seg_data[0]; end 1: begin wei_led <= 8'b11111101; dig_led = seg_data[1]; end // ...其他位 endcase end

刷新率要控制在1kHz左右(每位数码管显示时间约1ms)。我实测发现,如果直接用100MHz时钟扫描会有明显闪烁,后来用分频器生成1kHz时钟就流畅了。显示内容编码用查表法:

case(num) 0: seg = 8'b11000000; // 0 1: seg = 8'b11111001; // 1 // ... 'hA: seg = 8'b10001000; // A(显示OP中的O) 'hB: seg = 8'b10000011; // P endcase

4. 功能优化与调试

4.1 密码存储安全

初始密码我存在寄存器里:

reg [15:0] password_reg = 16'h3210; // 默认密码3210

但这样会有安全隐患——断电就恢复默认。后来我改用FPGA的Block RAM存储密码,并加了写保护逻辑:

always@(posedge clk) begin if(modify_mode && confirm_pressed) password_reg <= new_password; end

在EGO1上测试时,发现修改密码功能正常,但断电还是会丢失。如果要做产品级设计,建议外接EEPROM。

4.2 倒计时精度问题

10秒倒计时最初用系统时钟直接计数:

if(timer_en) counter <= counter + 1; if(counter == 100_000_000) begin // 100MHz时钟 timer <= timer - 1; counter <= 0; end

实测发现有两个问题:精度误差大(实际用了10.3秒)、占用资源多。优化方案是先用分频器产生1Hz时钟:

// 1Hz分频 always@(posedge clk) begin if(cnt_1hz == 50_000_000) begin clk_1hz <= ~clk_1hz; cnt_1hz <= 0; end else cnt_1hz <= cnt_1hz + 1; end // 倒计时逻辑 always@(posedge clk_1hz) if(timer > 0) timer <= timer - 1;

4.3 仿真测试技巧

在Vivado里做仿真时,我建了专门的testbench:

initial begin reset = 1; #100; reset = 0; // 复位 key_in = 4'b0011; #20; // 输入3 confirm = 1; #20; confirm = 0; // ... $finish; end

重点测试了三个场景:

  1. 正常开锁流程:输入正确密码->LED亮
  2. 密码错误流程:输入错误密码->数码管显示LC
  3. 修改密码流程:进入修改模式->设置新密码->用新密码开锁

仿真波形里要特别注意状态机跳转时机倒计时信号。有个bug我调试了很久:修改密码后状态机没返回开锁状态,后来发现是confirm信号消抖没做好。

5. 完整代码结构

顶层模块这样组织:

module password_lock( input clk, input [3:0] key_in, // 4位密码输入 input confirm, // 确认键 input modify, // 修改键 output led_open, // 开锁LED output [7:0] dig_led, // 数码管段选 output [7:0] wei_led // 数码管位选 ); // 实例化各子模块 debounce deb_confirm(.clk(clk), .button_in(confirm), ...); state_machine fsm(.clk(clk), .key_in(key_debounced), ...); display disp(.clk(clk), .value(display_data), ...); endmodule

关键信号连接关系:

  • 按键输入 -> 消抖模块 -> 状态机
  • 状态机 -> 密码比对逻辑 -> LED控制
  • 倒计时器 -> 数码管显示模块

在EGO1上验证时,下载bitstream文件后要注意:

  1. 先按复位键初始化系统
  2. 用拨码开关输入密码(比如0011对应数字3)
  3. 按确认键触发比对
  4. 成功时F1灯亮,数码管显示"OP"

6. 常见问题解决

问题1:数码管显示模糊

  • 检查位选信号频率(建议1kHz)
  • 确认段选信号驱动能力足够(可加74HC245缓冲)

问题2:按键反应迟钝

  • 调整消抖时间(20ms-50ms)
  • 确保时钟分频正确

问题3:状态机卡死

  • 添加看门狗定时器
  • 检查所有状态转移条件是否完备

有个坑我踩过:修改密码功能测试时,发现新密码不生效。原因是状态机在修改模式没有正确更新密码寄存器。通过仿真发现是confirm信号在修改状态下被意外触发。建议关键信号都加边沿检测

reg confirm_dly; always@(posedge clk) confirm_dly <= confirm; wire confirm_pulse = ~confirm_dly & confirm;

7. 扩展功能建议

如果想进一步提升项目,可以考虑:

  1. 增加错误次数限制:连续错误3次锁定1分钟
  2. 添加蜂鸣器提示:密码正确/错误时发声
  3. 改用矩阵键盘:节省IO口(需要扫描逻辑)
  4. 无线开锁功能:通过蓝牙模块控制

我在原型阶段试过用PMOD接口接蓝牙模块,用手机APP发送密码。实测发现需要解决串口通信同步问题,后来加了起始位/停止位校验。如果大家有兴趣,我可以另开一篇讲无线集成。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 23:06:42

从零到一:如何用WindTerm打造你的高效开发终端环境

从零到一&#xff1a;如何用WindTerm打造你的高效开发终端环境 1. 为什么开发者需要专业终端工具 在软件开发的世界里&#xff0c;终端是开发者与计算机系统对话的桥梁。无论是本地开发环境搭建、服务器管理&#xff0c;还是版本控制操作&#xff0c;一个功能强大且高效的终端…

作者头像 李华
网站建设 2026/5/1 16:37:10

AI辅助开发实战:如何用claudecode提示词提升代码生成效率

背景与痛点&#xff1a;AI 写代码&#xff0c;为什么总“掉链子”&#xff1f; 过去一年&#xff0c;我把 GitHub Copilot、CodeWhisperer、ChatGPT 挨个试了个遍&#xff0c;省了不少敲键盘的功夫&#xff0c;却也踩出一串坑&#xff1a; 上下文丢失&#xff1a;多文件项目里…

作者头像 李华
网站建设 2026/5/1 15:19:45

ComfyUI图生视频模型实战:从效率瓶颈到性能优化

背景痛点&#xff1a;原生 ComfyUI 在视频生成中的效率瓶颈 ComfyUI 的节点式工作流虽然灵活&#xff0c;但在图生视频&#xff08;Image-to-Video, I2V&#xff09;场景下暴露出三大硬伤&#xff1a; 节点级串行&#xff1a;Latent Diffusion 去噪、VAE 解码、光流补帧等阶段…

作者头像 李华
网站建设 2026/5/1 17:28:48

YOLO毕设项目实战:从模型部署到工程化落地的完整链路

YOLO毕设项目实战&#xff1a;从模型部署到工程化落地的完整链路 背景痛点&#xff1a;跑通≠落地 做毕设时&#xff0c;很多同学把官方仓库 clone 下来&#xff0c;跑通 python detect.py --source 0 就以为大功告成。结果一到答辩现场&#xff1a; 笔记本风扇狂转&#xf…

作者头像 李华