1. 项目概述:FPGA密码锁系统设计
这个基于Verilog HDL开发的FPGA密码锁系统,是我在嵌入式安全领域的一个实践项目。它采用模块化设计思路,通过状态机实现密码验证、修改和清除等核心功能,特别适合作为FPGA初学者的进阶练手项目。系统默认使用4位数字密码(初始值为1234),但架构设计上可以轻松扩展为更长或更复杂的密码组合。
在实际应用中,这套系统可以直接驱动电子门锁、保险柜或智能储物柜等设备。我选择Xilinx Artix-7(xc7a100t)和Altera Cyclone IV(EP4CE6)两款主流FPGA作为硬件平台,分别提供了Vivado和Quartus两个开发环境的完整工程,方便不同硬件背景的开发者快速上手。
硬件选型提示:xc7a100t属于Xilinx Artix-7系列,具有101,440个逻辑单元;EP4CE6则是Altera Cyclone IV E系列,包含6,272个逻辑单元。对于这种中等复杂度的数字系统,两款芯片都绰绰有余。
2. 系统架构与模块设计
2.1 整体系统框图
系统采用典型的FPGA分层设计架构:
code复制┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 矩阵键盘驱动 │───▶│ 密码锁主控 │───▶│ 外设控制单元 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
(key_board.v) (mimasuo.v) (继电器/蜂鸣器/LED)
2.2 核心模块功能分解
2.2.1 矩阵键盘驱动模块(key_board.v)
这个模块我采用了行列扫描法实现4x4矩阵键盘的检测,主要解决两个关键问题:
- 按键消抖处理:通过参数化的延时计数器(默认10,000个时钟周期)消除机械抖动。实际测试发现,对于大多数薄膜键盘,50-100ms的消抖时间最为可靠。
verilog复制parameter T = 32'd10000; // 消抖周期参数
always @(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 0;
else if(key_pressed) cnt <= (cnt == T) ? 0 : cnt + 1;
end
- 状态机设计:采用10状态有限状态机实现稳定扫描:
- 状态0-2:按键按下检测与消抖确认
- 状态3-7:逐列扫描(输出低电平到每一列,检测行输入)
- 状态8-10:按键释放检测
开发心得:在初期版本中,我曾尝试简化状态机到5个状态,但发现扫描稳定性明显下降。现在的10状态设计虽然复杂些,但在各种按键操作下都表现稳定。
2.2.2 密码锁主控模块(mimasuo.v)
这是系统的核心逻辑单元,我将其设计为双状态机结构:
- 密码验证状态机:
mermaid复制stateDiagram-v2
[*] --> NO_QKEY_PRESSED
NO_QKEY_PRESSED --> IPUT_CODE: 按键14(确认)
IPUT_CODE --> WAIT_END: 输入4位密码
WAIT_END --> CONFIG_GOUT: 自动验证
CONFIG_GOUT --> NO_QKEY_PRESSED: 完成
- 密码修改状态机:
mermaid复制stateDiagram-v2
[*] --> NO_QKEY_PRESSED
NO_QKEY_PRESSED --> IPUT_CODE: 按键12(修改)
IPUT_CODE --> CONFIG_GOUT: 输入原密码
CONFIG_GOUT --> BOXSEL_OUT: 验证通过
BOXSEL_OUT --> NO_QKEY_PRESSED: 按键15(保存)
关键信号说明:
relay_box:继电器控制,高电平有效sys_led:系统状态指示灯beep:蜂鸣器控制,不同频率表示不同状态
3. 关键实现细节
3.1 数码管动态扫描
系统采用共阳数码管,通过时分复用技术实现4位数码管的动态显示。这里分享一个调试时发现的技巧:
verilog复制// 数码管扫描时钟生成(1kHz)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) cnt_1ms <= 0;
else cnt_1ms <= (cnt_1ms == T1) ? 0 : cnt_1ms + 1;
end
assign count1ms_en = (cnt_1ms == T1);
注意事项:扫描频率不宜过低(会导致闪烁)也不宜过高(会导致亮度不足)。实测1kHz扫描频率下,每个数码管每秒刷新250次,既无闪烁感又能保持足够亮度。
3.2 密码存储与验证
密码存储采用32位寄存器实现,虽然当前只用到了16位(4位BCD码),但为后续扩展预留了空间:
verilog复制reg [31:0] store_code = 32'd1234; // 默认密码
验证逻辑采用逐位比较:
verilog复制always @(*) begin
pass_correct = (iput_xcode[3:0] == store_code[3:0]) &&
(iput_xcode[7:4] == store_code[7:4]) &&
(iput_xcode[11:8] == store_code[11:8]) &&
(iput_xcode[15:12] == store_code[15:12]);
end
4. 仿真与测试
4.1 测试用例设计
我设计了以下几组典型测试场景:
- 正确密码验证(1234)
- 错误密码验证(0000)
- 密码修改流程
- 异常操作测试(中途取消等)
4.2 Vivado仿真波形分析

波形解读:
- 在200ns复位结束后,系统进入待机状态(sys_led=1)
- 按下按键14后,依次输入1、2、3、4
- 系统自动验证通过,relay_box置1,蜂鸣器短鸣
5. 工程移植与扩展
5.1 跨平台移植要点
两个版本的主要差异在于:
- 时钟管理:Vivado使用MMCM,Quartus使用PLL
- 仿真脚本:Vivado用TCL,Quartus用DO文件
- 约束文件:Vivado用XDC,Quartus用QSF
5.2 功能扩展建议
- 增加密码长度:
verilog复制parameter CODE_LENGTH = 8; // 扩展为8位密码
reg [CODE_LENGTH*4-1:0] store_code;
- 添加指纹模块:
verilog复制module fingerprint(
input clk,
input rst_n,
input fp_data,
output reg fp_match
);
// 指纹识别逻辑
endmodule
- 无线控制接口:
verilog复制wire [15:0] wireless_code;
assign iput_xcode = key_enable ? keyboard_value : wireless_code;
6. 常见问题与解决方法
6.1 按键响应不灵敏
现象:有时需要多次按键才能识别
解决方法:
- 调整消抖参数T
- 检查键盘硬件连接
- 确保时钟频率稳定
6.2 数码管显示异常
现象:显示数字缺笔画或全亮
排查步骤:
- 确认段选和位选信号极性
- 检查动态扫描时序
- 测量数码管供电电压
6.3 密码验证失败
现象:输入正确密码但验证不通过
调试方法:
- 仿真观察iput_xcode和store_code的值
- 检查状态机转换条件
- 确认时钟域同步
7. 性能优化建议
- 时序优化:
verilog复制(* OPTIMIZE = "SPEED" *)
module mimasuo(...);
- 资源优化:
- 使用LUT替代乘法器
- 共享部分计算逻辑
- 功耗优化:
- 增加时钟门控
- 待机时降低扫描频率
这个项目从最初的概念到最终实现,我花了约3周时间,期间最大的收获是对状态机设计和时序控制有了更深的理解。特别是在按键消抖和数码管扫描的平衡上,经过多次调整才找到最优参数。建议初学者可以先用仿真验证基本功能,再上板调试,能节省大量时间。