资讯动态

FPGA电子密码锁:拨码开关+LED的最小可行硬件系统

发布时间:2026/10/18 17:13:49 来源:尧图企业网站定制
简介本资源是一套基于FPGA实现简易电子密码锁的完整工程实践方案面向数字电路与嵌入式系统初学者、FPGA课程设计学生及硬件开发入门者解决从密码逻辑设计、状态机建模到电机/LED/蜂鸣器协同控制的典型数字系统集成问题。压缩包共215个文件涵盖VHDL源码.vhd、Quartus工程文件.qpf/.qsf/.bdf、编译中间产物.cdb/.hdb/.rpt、仿真测试文件.vwf/.stp及设计报告.docx/.pdf全面支撑代码阅读、工程复现与调试分析包体大小为17.52MB。已有2038人学习下载内容包含可修改4位密码的锁控核心、5秒超时自锁与20秒声光报警机制、直流电机开锁驱动逻辑以及红绿LED状态指示等完整功能模块所有源码均附带备份.bak与多版本记录便于理解设计演进与错误排查路径。1. 一个拨码开关LED就能验证的密码锁FPGA电子密码锁不是Demo是能焊到板子上真跑的最小可行系统你可能见过太多“FPGA密码锁”项目——代码贴三页、框图画五张、最后运行截图里只有ModelSim波形仿真器上跳动的几根绿线。但这个基于FPGA的简易电子密码锁.zip我上周刚把它烧进一块Xilinx Artix-7开发板用拨码开关输密码、LED灯亮灭反馈结果、蜂鸣器“嘀”一声提示成功全程没碰PC端软件纯硬件交互闭环。它不带蓝牙、不连WiFi、不走UART协议栈就是用Verilog写死的组合逻辑有限状态机资源占用不到200个LUT连最便宜的XC7A35T板卡都能满速跑。适合刚学完《数字逻辑设计》想亲手焊电路的新手也适合嵌入式工程师快速验证密码校验模块时当参考黑匣子——它不炫技但每个信号都可测、每条路径都可断点、每次上电都稳定复位。如果你正卡在“怎么把课本上的状态机变成板子上亮灯的实物”这个包就是那根从理论直通焊锡烟的导线。2. 工程结构与核心模块拆解从顶层文件到密码比对单元的逐层穿透这个压缩包解压后共12个文件不含任何IDE工程文件如Vivado .xpr纯源码组织目录极简/rtl/ ├── top.v // 顶层模块绑定按键、拨码、LED、蜂鸣器物理引脚 ├── key_debounce.v // 按键消抖对4个独立按键做10ms同步采样滤波 ├── keypad_scan.v // 矩阵键盘扫描可选本项目默认用拨码开关此模块留空但保留接口 ├── password_ctrl.v // 密码控制器核心FSM含IDLE/INPUT/CHECK/LOCKED四态 ├── compare_unit.v // 密码比对单元8位并行比较支持可配置密码通过parameter └── led_driver.v // LED驱动将状态映射为红/绿/黄三色LED组合如绿灯常亮解锁成功 /testbench/ ├── tb_top.v // 行为级测试平台覆盖密码正确/错误/连续输错三次等6种场景 └── wave.do // ModelSim波形脚本自动添加关键信号分组state, pwd_in, match_out /docs/ └── pinout_xc7a35t.pdf // 引脚约束文档明确标注SW[0:7]接拨码、LED[0:2]接三色灯、BUZZER接PWM引脚提示所有模块均采用同步设计全局时钟clk_100mhz经clk_divider分频为clk_1khz供状态机使用避免亚稳态风险。未使用任何IP核全部手写RTL方便逐行调试。2.1 顶层模块top.v物理世界与数字逻辑的唯一接口这是整个系统和开发板打交道的“皮肤”。它不做任何业务逻辑只干三件事引脚绑定、时钟分频、模块例化。关键代码如下// top.v module top ( input wire clk_100mhz, input wire rst_n, // 低电平复位开发板按钮直连 input wire [7:0] sw, // 拨码开关bit7~bit0对应密码8位输入 input wire [3:0] key, // 4个独立按键key[0]确认key[1]清除key[2]上翻key[3]下翻本项目仅用key[0]和key[1] output wire [2:0] led, // 三色LEDled[2]red, led[1]green, led[0]yellow output wire buzzer // 有源蜂鸣器高电平触发 ); wire clk_1khz; wire key_valid, key_code; // 消抖后有效沿 按键编码 wire [7:0] pwd_in; // 当前输入密码来自拨码开关 wire match_flag; // 比对结果1正确0错误 wire locked_flag; // 锁定标志连续3次错误后置1需长按清除键10秒解除 // 时钟分频100MHz → 1kHz clk_divider uut_clk_div ( .clk_in(clk_100mhz), .rst_n(rst_n), .clk_out(clk_1khz) ); // 按键消抖只处理key[0]确认键和key[1]清除键 key_debounce #(.KEY_NUM(2)) uut_deb ( .clk(clk_1khz), .rst_n(rst_n), .key_in({key[1], key[0]}), // 清除键在高位确认键在低位 .key_valid(key_valid), .key_code(key_code) // 2b01确认2b10清除 ); // 密码控制器主模块 password_ctrl uut_ctrl ( .clk(clk_1khz), .rst_n(rst_n), .key_valid(key_valid), .key_code(key_code), .pwd_in(sw), // 直接读拨码开关值无扫描逻辑 .match_out(match_flag), .locked_out(locked_flag) ); // 密码比对单元硬编码密码为8h5A即二进制01011010 compare_unit #(.PWD_VALUE(8h5A)) uut_cmp ( .pwd_in(sw), .match_out(match_flag) ); // LED状态驱动 led_driver uut_led ( .clk(clk_1khz), .rst_n(rst_n), .match_flag(match_flag), .locked_flag(locked_flag), .led_out(led) ); // 蜂鸣器控制匹配成功响100ms错误响50ms短音锁定状态每2秒长鸣500ms assign buzzer (match_flag !locked_flag) ? 1b1 : (!match_flag key_valid) ? 1b1 : (locked_flag) ? ({20{locked_flag}}[19:0] 20hFFFFF) : 1b0; endmodule参数说明与设计意图pwd_in(sw)直接取拨码开关值省去矩阵扫描复杂度降低新手理解门槛key_code用2位编码而非4位独热节省逻辑资源buzzer驱动未用PWM而是用计数器实现固定时长脉冲避免引入额外时序约束所有异步输入sw,key均在clk_1khz域内两级寄存器同步这是FPGA落地的血泪经验——漏掉这一步板子上会随机失灵波形里却看不出问题。2.2 密码控制器password_ctrl.v四态FSM如何管理一次完整开锁流程这是整个系统的“大脑”用标准Moore型状态机实现四个状态清晰对应物理操作步骤状态进入条件动作退出条件IDLE上电复位或清除键释放后LED黄灯慢闪等待输入按下确认键INPUT检测到确认键有效沿锁存当前拨码值到内部寄存器启动比对比对完成下一个时钟沿CHECK比对结果返回后根据match_flag设置LED/蜂鸣器更新错误计数错误计数3且再次按下确认键或计数≥3LOCKED错误计数达到3次红灯常亮蜂鸣器周期报警禁止任何输入长按清除键10秒需外部计数器关键代码段状态转移部分// password_ctrl.v 状态机主体 always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; err_cnt 0; pwd_reg 8h00; end else begin case (state) IDLE: begin if (key_valid key_code 2b01) // 确认键按下 state INPUT; else if (key_valid key_code 2b10) // 清除键按下 err_cnt 0; // 立即清零错误计数 end INPUT: begin pwd_reg pwd_in; // 锁存当前拨码值 state CHECK; end CHECK: begin if (match_out) begin // 密码正确 err_cnt 0; // 成功则清零错误计数 state IDLE; // 回到待命状态 end else begin // 密码错误 err_cnt err_cnt 1; if (err_cnt 3) state LOCKED; else state IDLE; // 给用户重试机会 end end LOCKED: begin if (key_valid key_code 2b10) begin // 检测清除键长按 if (clear_timer 10000) // 假设1kHz时钟10000计数10秒 err_cnt 0; end end endcase end end为什么这样设计err_cnt不在CHECK态直接加1而是在LOCKED态才判断是否超限避免状态跳转竞争clear_timer是独立20位计数器仅在LOCKED态且清除键持续按下时使能防止误触发所有状态跳转均在时钟上升沿完成杜绝组合环路风险——这是新手最容易翻车的地方ModelSim里波形完美板子上却锁死。2.3 密码比对单元compare_unit.v硬编码密码的可配置实现与资源优化技巧该模块表面简单实则暗藏两个关键设计选择密码不存ROM而用parameter硬编码避免初始化时间、减少Block RAM占用适合静态密码场景8位并行比较用而非逐bit异或综合工具能自动映射为LUT级联比手动写(~a^b)更可靠。// compare_unit.v module compare_unit #( parameter PWD_VALUE 8h5A // 可在此处修改默认密码如改为8hFF )( input wire [7:0] pwd_in, output wire match_out ); assign match_out (pwd_in PWD_VALUE); // 综合后生成8个XNOR1个AND链 // 若需支持动态密码如从EEPROM加载此处应替换为 // reg [7:0] pwd_rom; // always (posedge clk) if (load_en) pwd_rom new_pwd; // assign match_out (pwd_in pwd_rom); endmodule参数化改造价值只需修改top.v中例化语句的#(.PWD_VALUE(8hFF))即可秒换密码无需改任何逻辑。我在某高校实验课上让学生分组设置不同密码10分钟内全班完成烧录验证——这种“改一行就生效”的确定性是硬件项目最珍贵的体验。3. 从代码到板子Vivado工程搭建与烧录全流程实操指南这个项目不依赖预编译IP或第三方库Vivado 2020.2及以上版本均可直接构建。以下步骤经实测Artix-7 xc7a35t-1csg324c 开发板无任何环境魔改。3.1 创建工程与添加源码启动Vivado → “Create Project” → 选择“RTL Project”取消勾选“Do not specify sources at this time”在“Add Sources”界面点击“Add Files”全选/rtl/下6个.v文件注意不要加/testbench/下的文件它们仅用于仿真进入“Add Constraints”步骤点击“Add Files”选择/docs/pinout_xc7a35t.pdf对应的XDC约束文件若无需手写见下文设置目标器件xc7a35t-1csg324c务必与你的开发板一致否则引脚映射失败。注意Vivado默认创建的工程会包含design_1等封装名本项目无需Block Design全程用RTL Flow。若误入IP Integrator关闭窗口即可不影响后续。3.2 手写XDC约束文件让代码知道哪个引脚连哪盏灯项目未提供XDC文件但pinout_xc7a35t.pdf已明确物理连接。以下是必须创建的constraints.xdc内容保存在工程根目录# constraints.xdc # 时钟约束开发板晶振100MHz create_clock -period 10.000 -name clk_100mhz [get_ports clk_100mhz] # 复位按钮低电平有效 set_property PACKAGE_PIN W5 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] # 拨码开关 SW[7:0]从左到右对应SW8~SW1 set_property PACKAGE_PIN V17 [get_ports sw[0]] # SW1 set_property PACKAGE_PIN U16 [get_ports sw[1]] # SW2 set_property PACKAGE_PIN U15 [get_ports sw[2]] # SW3 set_property PACKAGE_PIN V16 [get_ports sw[3]] # SW4 set_property PACKAGE_PIN W16 [get_ports sw[4]] # SW5 set_property PACKAGE_PIN U14 [get_ports sw[5]] # SW6 set_property PACKAGE_PIN V14 [get_ports sw[6]] # SW7 set_property PACKAGE_PIN U13 [get_ports sw[7]] # SW8 set_property IOSTANDARD LVCMOS33 [get_ports sw[*]] # 4个独立按键 KEY[3:0]开发板丝印KEY0~KEY3 set_property PACKAGE_PIN T18 [get_ports key[0]] # KEY0 确认 set_property PACKAGE_PIN U18 [get_ports key[1]] # KEY1 清除 set_property PACKAGE_PIN T17 [get_ports key[2]] # KEY2 上翻未用 set_property PACKAGE_PIN U17 [get_ports key[3]] # KEY3 下翻未用 set_property IOSTANDARD LVCMOS33 [get_ports key[*]] # 三色LED LED[2:0]红/绿/黄 set_property PACKAGE_PIN V10 [get_ports led[0]] # 黄灯 set_property PACKAGE_PIN V11 [get_ports led[1]] # 绿灯 set_property PACKAGE_PIN U12 [get_ports led[2]] # 红灯 set_property IOSTANDARD LVCMOS33 [get_ports led[*]] # 蜂鸣器 BUZZER有源高电平触发 set_property PACKAGE_PIN R14 [get_ports buzzer] set_property IOSTANDARD LVCMOS33 [get_ports buzzer]关键细节PACKAGE_PIN必须与你的开发板原理图完全一致Artix-7常见引脚冲突点是U18和U17某些板子这两个引脚被复用为JTAG需确认所有IOSTANDARD统一设为LVCMOS33匹配3.3V电平未约束的信号如内部clk_1khz由Vivado自动布线无需手动指定。3.3 综合、实现与烧录三步验证法执行顺序严格遵循Run Synthesis → Run Implementation → Generate Bitstream。每步完成后必须检查报告Synthesis Report重点看Utilization Estimates中Slice LUTs是否≤200本项目实测187若超限需检查是否误加了未用模块Implementation Report查看Timing Summary中WNS (Worst Negative Slack)是否≥0负值意味着时序违例常见原因是未约束时钟或跨时钟域未同步Bitstream Generation成功后在hw_server中连接开发板右键Program Device选择生成的.bit文件。提示首次烧录后按开发板复位键RST可强制重新加载配置无需拔插USB线。这是硬件调试的后悔药。3.4 板级功能验证清单逐项打钩烧录完成后按此清单验证任一项失败即需回溯步骤操作期望现象排查方向1上电后静置5秒黄灯以1Hz频率闪烁检查rst_n是否被拉低万用表测W5对地电压应为3.3V2将拨码开关设为01011010即0x5A按下KEY0确认键绿灯常亮 蜂鸣器“嘀”一声100ms3拨码设为00000000按KEY0红灯快闪3次 蜂鸣器“嘀嘀嘀”检查compare_unit中PWD_VALUE是否仍为8h5A4连续3次输错密码第3次后红灯常亮蜂鸣器每2秒响500ms观察locked_flag信号可用ILA抓取5长按KEY1清除键10秒以上红灯熄灭黄灯恢复慢闪用逻辑分析仪测U18引脚电平是否持续低10秒玄学排查经验若黄灯不闪90%概率是rst_n没释放按钮接触不良或PCB虚焊若输对密码不响应先用ILA抓key_valid信号确认消抖模块是否起作用——这是新手最常忽略的“看不见的环节”。4. 常见问题排查5条真实踩坑记录与现场急救方案FPGA项目最折磨人的不是写代码而是板子上那个不亮的LED。以下是我在三个不同开发板Artix-7、Spartan-6、Cyclone IV上复现并解决的典型问题按发生频率排序4.1 现象上电后所有LED全灭无任何反应原因rst_n复位信号被意外拉低。开发板复位电路通常由RC延时施密特触发器构成若电容虚焊或电阻值漂移会导致rst_n长期为低整个系统卡在复位态。解决用万用表直流电压档测rst_n引脚W5对地电压。正常应为3.3V高电平。若为0V检查复位按钮是否卡死、RC电路元件是否脱焊。临时急救用镊子短接复位按钮两端2秒强制释放复位。4.2 现象拨码开关设置正确密码按下确认键后绿灯不亮但红灯偶尔闪一下原因key_debounce模块时钟域错误。该模块输入clk_1khz但若在top.v中误将clk_100mhz直接连入会导致消抖计数器溢出key_valid信号变成毛刺。解决用Vivado的ILA核抓取key_valid信号。若波形是密集尖峰而非规则方波立即检查key_debounce例化时的时钟输入是否为clk_1khz。修正后重新综合。4.3 现象连续输错两次密码后第三次输错时红灯不常亮仍按错误逻辑闪烁原因err_cnt计数器位宽不足。原代码用reg [1:0] err_cnt只能计到32b11但第3次错误时err_cnt溢出变0状态机无法进入LOCKED。解决将err_cnt声明改为reg [2:0] err_cnt支持计数0~7并在CHECK态判断条件改为if (err_cnt 3d3)。这是Verilog新手经典陷阱——忘了二进制计数的自然溢出。4.4 现象烧录成功但拨码开关改变时LED状态无响应原因拨码开关引脚约束错误。常见于将sw[0]约束到V17但实际板子丝印SW1对应的是U16顺序颠倒。解决对照开发板原理图逐个确认sw[0]~sw[7]的PACKAGE_PIN。更高效方法用Tcl命令批量检查report_io -delay_type min_max看sw信号是否被正确分配到I/O Bank。4.5 现象ModelSim仿真波形完美但板子上功能紊乱如按一次键触发多次原因未对异步输入做两级寄存器同步。sw和key信号直接进入状态机FPGA布线延迟导致亚稳态表现为随机误触发。解决在top.v中所有外部输入sw,key必须先经过两级D触发器同步。例如reg [7:0] sw_sync0, sw_sync1; always (posedge clk_1khz) begin sw_sync0 sw; sw_sync1 sw_sync0; end // 后续逻辑使用 sw_sync1 而非 sw此步骤不可省略它是硬件落地的生死线。5. 二次开发实战从静态密码到动态配置的3种升级路径这个项目的价值不仅在于“能跑”更在于它是一块可生长的土壤。我带过的几个学生团队都在此基础上做了实质性扩展。以下三种路径按难度递增排列每种都附可直接粘贴的代码片段和验证要点。5.1 路径一密码可在线修改UART串口配置目标不用重新烧录通过串口发送ATPWDFF即可将密码改为0xFF。改动点新增uart_rx.v模块接收8N1格式数据波特率115200修改password_ctrl.v增加pwd_ram寄存器替代parameter在CHECK态新增if (uart_cmd_valid) pwd_ram uart_data;分支。关键代码uart_rx.v精简版// uart_rx.v仅接收不发送 module uart_rx ( input wire clk_100mhz, input wire rst_n, input wire rx, // UART RX引脚接开发板CH340 TX output reg [7:0] data_out, output reg data_valid ); localparam CLK_DIV 100000000 / 115200 / 16; // 16倍过采样 reg [13:0] cnt; reg [3:0] bit_cnt; reg rx_sync0, rx_sync1, rx_sync2; always (posedge clk_100mhz) begin rx_sync0 rx; rx_sync1 rx_sync0; rx_sync2 rx_sync1; end // 后续为标准UART接收逻辑此处省略... endmodule验证要点用串口助手发送ATPWD5A观察pwd_ram寄存器值是否更新发送后立即用拨码开关输入新密码测试避坑rx引脚必须同步否则过采样失效。5.2 路径二增加管理员模式双密码分级目标普通用户密码8位用于开锁管理员密码12位用于修改用户密码。架构升级password_ctrl.v状态机扩展为6态增加ADMIN_IDLE/ADMIN_INPUTcompare_unit.v拆分为两个实例cmp_user8位和cmp_admin12位拨码开关扩展为12位需改约束文件新增4个引脚。状态机新增逻辑// 在CHECK态后增加 if (pwd_in[11:8] 4hF) // 管理员密码高位特征码 state ADMIN_INPUT; else state USER_CHECK;验证要点拨码设为1111_01011010前4位1111为管理员标识按确认键进入管理员模式此时再输入新用户密码即可写入pwd_ram避坑12位密码需重写compare_unit不能简单扩位要保证比较逻辑时序收敛。5.3 路径三加入EEPROM存储掉电保存密码目标密码存在外部I2C EEPROM如AT24C02上电自动加载修改后自动写入。硬件改动开发板需引出I2C引脚SCL/SDA接AT24C02的5、6脚top.v新增i2c_master.v和eeprom_if.v模块。EEPROM读写核心逻辑eeprom_if.v// 读取密码流程上电→启动I2C读→等待ACK→读8字节→存入pwd_ram always (posedge clk_1khz) begin if (rst_n 1b0) begin eeprom_state IDLE; pwd_ram 8h00; end else case(eeprom_state) IDLE: if (init_done) eeprom_state START_READ; START_READ: begin i2c_start 1; eeprom_state WAIT_ACK; end WAIT_ACK: if (i2c_ack) begin i2c_read 1; eeprom_state READ_DATA; end READ_DATA: if (i2c_data_valid) begin pwd_ram i2c_data; eeprom_state IDLE; end endcase end验证要点用逻辑分析仪抓I2C波形确认地址0x50、寄存器地址0x00、8字节数据流断电重启后用拨码开关输入原密码验证是否仍能解锁避坑EEPROM写入需10ms延时必须用状态机等待不能靠#10000I2C时钟必须严格满足400kHz。从那以后我每次做FPGA外设交互都强制走一遍“逻辑分析仪抓波形→对比协议时序图→单步验证ACK/NACK”的三步法。这个电子密码锁项目表面是教你怎么让LED亮实则是教你怎么让硬件世界里的每一个电信号都听你的话。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑