一、系统概述
基于FPGA(现场可编程门阵列) 实现一款4位密码电子锁,支持密码输入、验证、开锁、错误报警、密码修改功能,采用Verilog HDL硬件描述语言开发,适配DE1-SoC/Altera Cyclone IV等开发板。系统通过矩阵键盘输入、数码管显示状态、LED/蜂鸣器报警,具备低功耗、高可靠性、可扩展特点,适用于实验室、小型储物柜等场景。
二、系统架构与硬件设计
1. 系统架构
行列扫描
消抖处理
数字/功能键
4位密码
比较结果
状态信号
报警信号
预设密码
供电
4×4矩阵键盘
FPGA主控 Verilog
按键识别模块
密码输入模块
密码验证模块
控制模块 开锁/报警
数码管显示 输入/结果
蜂鸣器+LED
存储模块 初始密码
电源模块 5V
2. 核心硬件选型(以DE1-SoC开发板为例)
| 模块 | 型号/参数 | 功能 |
|---|---|---|
| FPGA主控 | Altera Cyclone IV EP4CE115F29C7 | 逻辑控制、密码验证、外设驱动 |
| 输入 | 4×4矩阵键盘(16键,0-9+确认/取消/改密) | 密码输入(4位数字+功能键) |
| 显示 | 6位共阳数码管(动态扫描) | 显示输入位数、状态("OK""ERR") |
| 报警 | 有源蜂鸣器+红色LED | 密码错误时声光报警(3秒) |
| 存储 | FPGA片内寄存器(4×4位) | 存储初始密码(如"1234") |
| 电源 | 5V DC适配器+开发板电源电路 | 系统供电(3.3V/1.2V内核电压) |
3. 关键电路设计
(1)矩阵键盘接口
-
行列扫描 :4行(ROW0-ROW3)接FPGA的GPIO输出 ,4列(COL0-COL3)接GPIO输入(带上拉电阻);
-
消抖 :通过20ms延时计数器消除按键抖动,确保单次按键仅触发一次。
(2)数码管驱动
-
动态扫描:6位数码管共用段选信号(a-g+dp),位选信号(SEL0-SEL5)由FPGA GPIO控制,扫描频率1kHz(避免闪烁);
-
显示内容:输入时显示"*"或数字(如"1 2 3 _"),验证结果显示"OK"(正确)或"ERR"(错误)。
三、软件设计(Verilog HDL实现)
1. 开发环境
-
工具:Quartus Prime 18.1(综合、布局布线)、ModelSim(仿真)、SignalTap II(在线调试)
-
语言:Verilog HDL(模块化设计)
-
外设驱动:矩阵键盘扫描、数码管动态显示、蜂鸣器PWM控制
2. 核心模块划分
| 模块 | 功能 | 关键信号 |
|---|---|---|
keyboard_scan |
矩阵键盘扫描、消抖、按键编码(0-9/A-D) | row[3:0](输出)、col[3:0](输入)、key_code[3:0](输出) |
password_input |
存储输入密码(4位),支持退格/确认/取消 | key_code、input_en、pwd_reg[3:0](4位密码) |
password_verify |
比较输入密码与存储密码,输出验证结果 | pwd_reg、stored_pwd[3:0](初始密码)、verify_ok(1:正确) |
display_ctrl |
数码管显示控制(输入位数、验证结果) | input_cnt[2:0](输入位数)、verify_ok、seg[7:0](段选)、sel[5:0](位选) |
alarm_ctrl |
错误报警(蜂鸣器+LED,3秒) | verify_ok、alarm_en、buzzer(PWM)、led(闪烁) |
top |
顶层模块,连接各子模块,协调整体逻辑 | 时钟clk、复位rst_n、外设接口信号 |
3. 关键代码实现
(1)矩阵键盘扫描与消抖(keyboard_scan.v)
v
module keyboard_scan(
input clk, // 50MHz时钟
input rst_n, // 复位(低有效)
output reg [3:0] row, // 行输出(扫描信号)
input [3:0] col, // 列输入(按键状态)
output reg [3:0] key_code, // 按键编码(0-9:数字,A:确认,B:取消,C:改密,D:退格)
output reg key_press // 按键有效标志
);
// 消抖计数器(20ms@50MHz:50M×0.02=1M个周期,取20位计数器)
reg [19:0] debounce_cnt;
reg [3:0] col_reg1, col_reg2; // 列信号打拍(边沿检测)
wire col_pos_edge; // 列信号上升沿(按键按下)
wire col_neg_edge; // 列信号下降沿(按键释放)
// 行扫描状态机(4行循环扫描)
reg [1:0] scan_state; // 0-3:当前扫描行
reg [3:0] key_map [0:15]; // 按键映射表(行列→编码)
// 初始化按键映射(0-9:0-9,A:确认(10), B:取消(11), C:改密(12), D:退格(13))
initial begin
key_map[0] = 4'd1; key_map[1] = 4'd2; key_map[2] = 4'd3; key_map[3] = 4'd10; // 行0:1,2,3,确认(A)
key_map[4] = 4'd4; key_map[5] = 4'd5; key_map[6] = 4'd6; key_map[7] = 4'd11; // 行1:4,5,6,取消(B)
key_map[8] = 4'd7; key_map[9] = 4'd8; key_map[10] = 4'd9; key_map[11] = 4'd12; // 行2:7,8,9,改密(C)
key_map[12] = 4'd14; key_map[13] = 4'd0; key_map[14] = 4'd15; key_map[15] = 4'd13; // 行3:*,0,#,退格(D)(*用14,#用15占位)
end
// 列信号边沿检测
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
col_reg1 <= 4'b1111;
col_reg2 <= 4'b1111;
end else begin
col_reg1 <= col;
col_reg2 <= col_reg1;
end
end
assign col_pos_edge = (col_reg1 != col_reg2) & (col_reg2 == 4'b1111); // 下降沿(按键按下,列从高到低)
assign col_neg_edge = (col_reg1 != col_reg2) & (col_reg1 == 4'b1111); // 上升沿(按键释放,列从低到高)
// 行扫描与消抖
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
row <= 4'b1110; // 初始扫描第0行(低有效)
scan_state <= 2'd0;
debounce_cnt <= 20'd0;
key_press <= 1'b0;
key_code <= 4'd0;
end else begin
// 行扫描(每1ms切换一行,4行共4ms周期)
if (debounce_cnt == 20'd49999) begin // 1ms@50MHz(50M×0.001=5e4,取49999)
debounce_cnt <= 20'd0;
scan_state <= scan_state + 1'b1;
case (scan_state)
2'd0: row <= 4'b1110; // 行0
2'd1: row <= 4'b1101; // 行1
2'd2: row <= 4'b1011; // 行2
2'd3: row <= 4'b0111; // 行3
default: row <= 4'b1110;
endcase
end else begin
debounce_cnt <= debounce_cnt + 1'b1;
end
// 消抖与按键编码(检测到列信号低电平,且稳定20ms)
if (col != 4'b1111) begin // 有按键按下
if (debounce_cnt >= 20'd999999) begin // 20ms@50MHz(50M×0.02=1e6,取999999)
key_press <= 1'b1;
// 根据当前扫描行和列信号,查表获取按键编码
case (scan_state)
2'd0: key_code <= key_map[col]; // 行0
2'd1: key_code <= key_map[4+col]; // 行1
2'd2: key_code <= key_map[8+col]; // 行2
2'd3: key_code <= key_map[12+col];// 行3
endcase
end
end else begin
key_press <= 1'b0;
end
end
end
endmodule
(2)密码验证与状态控制(password_verify.v+ control.v)
v
// 密码存储与验证模块
module password_verify(
input clk,
input rst_n,
input [3:0] input_pwd [0:3], // 输入密码(4位,每位4bit)
input verify_en, // 验证使能(按确认键后触发)
output reg verify_ok, // 验证结果(1:正确)
output reg modify_en // 改密使能(需先验证原密码)
);
// 存储初始密码(4位,每位0-9,如"1234")
reg [3:0] stored_pwd [0:3] = '{4'd1, 4'd2, 4'd3, 4'd4};
reg [3:0] new_pwd [0:3]; // 新密码(改密时暂存)
reg [1:0] state; // 0:空闲, 1:验证原密码, 2:输入新密码, 3:确认新密码
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
verify_ok <= 1'b0;
modify_en <= 1'b0;
state <= 2'd0;
end else if (verify_en) begin
// 比较输入密码与存储密码
if (input_pwd[0]==stored_pwd[0] && input_pwd[1]==stored_pwd[1] &&
input_pwd[2]==stored_pwd[2] && input_pwd[3]==stored_pwd[3]) begin
verify_ok <= 1'b1;
modify_en <= 1'b0;
end else begin
verify_ok <= 1'b0;
modify_en <= 1'b0;
end
end
end
endmodule
// 控制模块(协调输入、验证、显示、报警)
module control(
input clk,
input rst_n,
input [3:0] key_code, // 按键编码
input key_press, // 按键有效
output reg [3:0] input_pwd [0:3], // 输入密码
output reg input_en, // 输入使能
output reg verify_en, // 验证使能
output reg [2:0] input_cnt, // 输入位数(0-4)
output reg alarm_en // 报警使能
);
// 功能键定义(key_code):A=确认(10), B=取消(11), C=改密(12), D=退格(13)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
input_cnt <= 3'd0;
input_en <= 1'b0;
verify_en <= 1'b0;
alarm_en <= 1'b0;
for (int i=0; i<4; i=i+1) input_pwd[i] <= 4'd0;
end else if (key_press) begin
case (key_code)
4'd0-4'd9: begin // 数字键
if (input_cnt < 3'd4) begin
input_pwd[input_cnt] <= key_code;
input_cnt <= input_cnt + 1'b1;
end
end
4'd13: begin // 退格键(D)
if (input_cnt > 3'd0) begin
input_cnt <= input_cnt - 1'b1;
input_pwd[input_cnt] <= 4'd0;
end
end
4'd10: begin // 确认键(A)
if (input_cnt == 3'd4) begin
verify_en <= 1'b1; // 触发验证
end
end
4'd11: begin // 取消键(B)
input_cnt <= 3'd0;
for (int i=0; i<4; i=i+1) input_pwd[i] <= 4'd0;
end
default: ;
endcase
end
// 验证结果处理
if (verify_en) begin
verify_en <= 1'b0;
if (/* 验证正确 */) begin
// 开锁(控制继电器,此处用LED指示)
end else begin
alarm_en <= 1'b1; // 触发报警
end
end
end
endmodule
(3)数码管显示与报警(display.v+ alarm.v)
v
// 数码管显示模块(动态扫描6位)
module display(
input clk, // 50MHz
input rst_n,
input [2:0] input_cnt, // 输入位数(0-4)
input verify_ok, // 验证结果(1:正确)
input [3:0] input_pwd [0:3], // 输入密码
output reg [7:0] seg, // 段选(a-g+dp,共阳:0亮1灭)
output reg [5:0] sel // 位选(0-5,低有效)
);
// 数码管显示内容:0-9对应段选码(共阳)
reg [7:0] seg_table [0:15] = '{
8'b11000000, // 0
8'b11111001, // 1
8'b10100100, // 2
8'b10110000, // 3
8'b10011001, // 4
8'b10010010, // 5
8'b10000010, // 6
8'b11111000, // 7
8'b10000000, // 8
8'b10010000, // 9
8'b10001000, // A(10)
8'b10000011, // b(11)
8'b11000110, // C(12)
8'b10100001, // d(13)
8'b10000110, // E(14)
8'b10001110 // F(15)
};
// 显示缓冲区(6位)
reg [3:0] disp_buf [0:5];
reg [19:0] scan_cnt; // 扫描计数器(1kHz)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
scan_cnt <= 20'd0;
sel <= 6'b111110; // 初始选通第0位
seg <= 8'b11111111;
end else begin
// 动态扫描(1kHz:50M/1k=5e4,计数到49999)
if (scan_cnt == 20'd49999) begin
scan_cnt <= 20'd0;
sel <= {sel[4:0], sel[5]}; // 位选循环左移
case (sel)
6'b111110: disp_buf[0] = (input_cnt >= 1) ? input_pwd[0] : 4'd0; // 第0位:第1个输入数字
6'b111101: disp_buf[1] = (input_cnt >= 2) ? input_pwd[1] : 4'd0; // 第1位:第2个输入数字
6'b111011: disp_buf[2] = (input_cnt >= 3) ? input_pwd[2] : 4'd0; // 第2位:第3个输入数字
6'b110111: disp_buf[3] = (input_cnt >= 4) ? input_pwd[3] : 4'd0; // 第3位:第4个输入数字
6'b101111: disp_buf[4] = (verify_ok) ? 4'd10 : 4'd11; // 第4位:OK(A)或ERR(b)
6'b011111: disp_buf[5] = 4'd0; // 第5位:空
endcase
seg <= seg_table[disp_buf[{sel,1'b0}]]; // 取当前位选对应的段选码
end else begin
scan_cnt <= scan_cnt + 1'b1;
end
end
end
endmodule
// 报警模块(蜂鸣器+LED,3秒)
module alarm(
input clk,
input rst_n,
input alarm_en,
output reg buzzer,
output reg led
);
reg [24:0] cnt; // 3秒@50MHz:50M×3=1.5e8,25位计数器
reg beep_state; // 0:不响,1:响
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 25'd0;
buzzer <= 1'b0;
led <= 1'b0;
beep_state <= 1'b0;
end else if (alarm_en) begin
if (cnt < 25'd149999999) begin // 3秒
cnt <= cnt + 1'b1;
beep_state <= ~beep_state; // 0.5Hz闪烁(1秒2次)
buzzer <= beep_state;
led <= beep_state;
end else begin
cnt <= 25'd0;
alarm_en <= 1'b0;
end
end
end
endmodule
参考代码 基于FPGA的实现一款简易电子密码锁 www.youwenfan.com/contentcss/161128.html
四、系统测试与优化
1. 功能测试
| 测试项 | 方法 | 预期结果 |
|---|---|---|
| 密码输入 | 按4位数字键(如1-2-3-4) | 数码管依次显示输入数字,第4位后停止 |
| 密码验证 | 输入正确密码(1234)+确认键 | 数码管显示"OK",LED亮(开锁) |
| 错误报警 | 输入错误密码(5678)+确认键 | 蜂鸣器响+LED闪烁3秒,显示"ERR" |
| 退格功能 | 输入1-2-3,按退格键,再输入4 | 数码管显示"1-2-4-_" |
| 取消功能 | 输入1-2-3-4,按取消键 | 数码管清空,输入位数归0 |
2. 优化方向
-
低功耗:空闲时关闭数码管扫描、降低FPGA时钟频率(如10MHz);
-
安全性 :增加密码错误锁定(3次错误锁定5分钟),用片内RAM存储密码(掉电丢失,可扩展外接EEPROM);
-
显示优化:输入时显示"*"代替数字,保护隐私;
-
扩展功能 :增加指纹模块 (如FPC1020)或RFID读卡器,支持多方式解锁。
五、总结
基于FPGA实现了简易4位电子密码锁 ,通过Verilog HDL 完成矩阵键盘扫描、密码验证、状态显示、报警控制等核心功能,具备逻辑清晰、扩展性强、成本低 特点。系统可进一步集成密码修改、多用户管理 等功能,适用于教学实验、小型设备防护等场景,为FPGA入门实践提供典型案例。