基于FPGA的简易电子密码锁设计(Verilog实现)

一、系统概述

基于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_codeinput_enpwd_reg[3:0](4位密码)
password_verify 比较输入密码与存储密码,输出验证结果 pwd_regstored_pwd[3:0](初始密码)、verify_ok(1:正确)
display_ctrl 数码管显示控制(输入位数、验证结果) input_cnt[2:0](输入位数)、verify_okseg[7:0](段选)、sel[5:0](位选)
alarm_ctrl 错误报警(蜂鸣器+LED,3秒) verify_okalarm_enbuzzer(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入门实践提供典型案例。

相关推荐
hoiii1874 小时前
104键PS2接口标准键盘C语言驱动程序
c语言·fpga开发·计算机外设
jllllyuz13 小时前
基于FPGA的通信信号源设计
fpga开发
Saniffer_SH17 小时前
【每日一题】一台可编程的PCIe 6.0主机 + 一套自动化CTS验证平台 + 一个轻量级链路分析系统
运维·服务器·测试工具·fpga开发·自动化·计算机外设·硬件架构
9523621 小时前
计算机组成原理 - 主存储器
单片机·嵌入式硬件·学习·fpga开发
简简单单做算法1 天前
【第2章>第1节】基于FPGA的图像放大和插值处理概述
计算机视觉·fpga开发·双线性插值·线性插值·图像放大·均值插值·最邻近插值
木心术11 天前
OpenClaw FPGA资源利用率优化深度指南
人工智能·fpga开发
发光的沙子1 天前
FPGA----zynq 7000与zynqMP内存区域保留方法
fpga开发
minglie11 天前
c和hdl对偶关系
fpga开发
verse_armour1 天前
【FPGA】在PYNQ开发板上搭建卷积神经网络实现交通标志识别
fpga开发