1. 项目概述:FPGA矩阵键盘密码锁设计
这个项目实现了一个基于FPGA的电子密码锁系统,核心功能包括矩阵键盘输入、密码存储比对、状态机控制和输出显示。与传统单片机方案相比,FPGA方案具有并行处理、实时响应和可重构等优势。我在工业控制领域做过多个类似项目,FPGA的硬件并行特性特别适合这种需要快速响应的输入输出系统。
系统工作流程:用户通过4x4矩阵键盘输入密码,FPGA实时扫描键盘状态并解码按键值,与预存密码比对后控制锁具开关。特殊功能键包括修改密码(组合键12)、确认(#)和清除(*)。整个系统采用Verilog HDL实现,在Xilinx Artix-7开发板上验证通过。
2. 核心模块设计解析
2.1 矩阵键盘扫描模块
矩阵键盘采用行扫描法检测按键,这是最稳定可靠的检测方案。我在多个项目实测中发现,相比中断方式,扫描法在FPGA上实现更简单且抗干扰能力更强。具体实现:
verilog复制// 键盘扫描状态机
always @(posedge clk) begin
case(scan_state)
0: begin row_pin <= 4'b1110; col_read <= col_pin; end //扫描第一行
1: begin row_pin <= 4'b1101; col_read <= col_pin; end //扫描第二行
2: begin row_pin <= 4'b1011; col_read <= col_pin; end //扫描第三行
3: begin row_pin <= 4'b0111; col_read <= col_pin; end //扫描第四行
endcase
scan_state <= (scan_state + 1) % 4;
end
关键细节:必须添加20ms消抖延时,这是保证稳定性的黄金数值。我通过逻辑分析仪实测发现,机械按键抖动期通常在5-15ms之间。
2.2 密码存储与管理
密码存储采用双bank设计:一个存储当前密码,另一个存储新密码。修改密码时需要先输入原密码验证,再输入两次新密码确认。这个设计避免了误修改的风险,我在实际部署中收到过良好反馈。
verilog复制reg [15:0] password_reg; // 当前密码
reg [15:0] new_password; // 新密码缓存
reg pwd_change_flag; // 修改标志位
// 密码修改逻辑
always @(posedge clk) begin
if(key_val == 12 && pwd_change_mode) begin
if(input_pwd == password_reg) begin
pwd_change_mode <= 1;
end
end
else if(key_val == '#') begin
if(pwd_change_mode && (input_count == 4)) begin
new_password <= input_pwd;
end
end
end
2.3 状态机设计
采用三段式状态机(Moore型)控制整体流程,这是FPGA设计的最佳实践。状态包括:
- IDLE:等待输入
- INPUT:密码输入中
- CHECK:密码验证
- CHANGE:修改密码模式
- OPEN:开锁状态
- ALARM:错误报警
verilog复制always @(posedge clk or posedge rst) begin
if(rst) state <= IDLE;
else begin
case(state)
IDLE: if(key_pressed) state <= INPUT;
INPUT: if(key_val == '#') state <= CHECK;
CHECK: if(match) state <= OPEN; else state <= ALARM;
// 其他状态转换...
endcase
end
end
3. 关键实现细节
3.1 时钟分频设计
系统需要三个时钟域:
- 主时钟100MHz(FPGA晶振)
- 键盘扫描时钟1kHz(100000分频)
- 显示刷新时钟60Hz(通过PLL生成)
verilog复制// 键盘扫描分频器
reg [16:0] scan_counter;
always @(posedge clk) begin
if(scan_counter == 100000) begin
scan_clk <= ~scan_clk;
scan_counter <= 0;
end else begin
scan_counter <= scan_counter + 1;
end
end
实测发现:扫描频率低于500Hz会导致按键响应迟滞,高于2kHz会增加误触发概率。1kHz是最佳平衡点。
3.2 密码比对算法
采用逐位比对而非整体数值比对,这样可以提前发现错误并立即返回,提高响应速度:
verilog复制reg [3:0] match_count;
always @(posedge clk) begin
if(input_pwd[3:0] == password_reg[3:0]) match_count[0] <= 1;
// 其他位比对...
end
assign match = &match_count; // 全部匹配
3.3 输出控制接口
锁具控制采用光耦隔离,这是工业级设计的必备保护措施:
code复制FPGA -> 光耦PC817 -> 继电器 -> 电磁锁
(隔离电压5000V)
4. 常见问题与解决方案
4.1 按键抖动问题
现象:单次按键触发多次事件
解决方案:
- 硬件:在键盘引脚加0.1uF电容
- 软件:采用两级寄存器采样+计数器消抖
verilog复制// 消抖逻辑示例
reg [1:0] key_sync;
always @(posedge clk) begin
key_sync <= {key_sync[0], col_read};
if(key_sync == 2'b01) key_valid <= 1;
end
4.2 密码存储易失性
FPGA断电后配置存储器会丢失密码数据,三种解决方案:
- 外接EEPROM(24C02)
- 使用FPGA的Flash配置存储器(需特殊处理)
- 添加备用电池(最简单方案)
4.3 显示残影
LED数码管动态扫描时出现残影,解决方法:
- 增加位选信号的反相器
- 调整刷新时序,确保先关闭显示再切换段选
- 在段选信号上串联100Ω电阻
5. 性能优化技巧
5.1 流水线设计
将键盘扫描、密码比对、显示刷新等任务并行化:
code复制时钟周期0:扫描第1行
时钟周期1:扫描第2行 + 处理第1行数据
时钟周期2:扫描第3行 + 处理第2行数据 + 更新显示位1
5.2 资源优化
- 共用计数器:键盘扫描和显示刷新共用同一个分频器
- 状态编码:采用One-Hot编码而非二进制,节省比较器资源
- 密码存储:使用LUT而非寄存器,节省Slice资源
5.3 时序约束
必须添加以下约束保证稳定性:
code复制create_clock -period 10 [get_ports clk]
set_input_delay -clock clk 2 [get_ports {row_pin[*] col_pin[*]}]
set_output_delay -clock clk 1 [get_ports {lock_ctrl display_*}]
6. 测试方案
6.1 单元测试
- 键盘扫描测试:依次短接行列,验证键值解码
- 密码存储测试:修改密码后断电重启验证持久性
- 状态机测试:模拟所有可能的状态转换路径
6.2 压力测试
- 连续快速输入测试:每秒10次按键操作持续5分钟
- 错误密码测试:连续输入错误密码15次触发锁定
- 边界测试:输入0000和9999等边界值
6.3 实际部署建议
- 环境防护:电路板喷涂三防漆
- 应急接口:保留JTAG调试端口
- 日志记录:添加UART输出调试信息
这个项目我在三款不同FPGA平台上实现过,Artix-7的性价比最高。实际部署时发现最大的问题是电磁干扰,后来在电源入口处增加了TVS二极管和共模电感后稳定性大幅提升。建议密码位数可配置,我们最终客户要求的是6位密码,只需简单修改参数即可适配。