內容目錄
Key-Comb Locker
1. keypad_scan.sv
test keypad, with debouncing.
I got two kind of 4×4 keypad, one needs to swap row & column pin sequence, one needn’t.
Validation Checklist
| Feature | Status | Notes |
|---|---|---|
| Tri-state row control | ✅ Implemented | Uses generate block for clean row drive logic |
| Keypad decoding | ✅ Implemented | Covers full 4×4 matrix with layout inversion |
| Debounce (1ms sampling) | ✅ Implemented | stable_cnt logic at 50MHz |
| Debounce (3-cycle stable) | ✅ Implemented | key_valid_buf shift register |
| Valid signal extension | ✅ Implemented | io_valid_shift prolongs io[12] |
| Debug output via LEDs | ✅ Implemented | LEDR[9:5] = io[12:8] shows keycode and valid flag |
keypad_scan.sv
module keypad_scan (
input logic clk, // System clock input
input logic n_rst, // Active-low asynchronous reset
inout tri [3:0] row_out, // Row outputs to keypad (tri-state controlled)
input logic [3:0] col, // Column inputs from keypad
output logic [12:8] io, // Output: io[11:8] = keycode, io[12] = key_valid
output logic [9:0] LEDR // Debug LEDs
);
// === Internal Signals ===
logic [1:0] row_idx; // Current row index for scanning (0–3)
logic [3:0] row_drive; // Active-low row drive control
logic [3:0] keycode; // Raw decoded key value from keypad
logic key_valid; // Indicates a keypress was detected
// === Debounce Buffers ===
logic [2:0] key_valid_buf; // Shift register for key_valid debounce
logic [3:0] keycode_buf; // Stable keycode output after debounce
// === Output Valid Extension ===
logic [4:0] io_valid_shift; // Extends io[12] signal for multiple cycles
assign io[12] = |io_valid_shift; // Valid if any bit in shift register is high
assign io[11:8] = keycode_buf; // Output stable keycode
assign LEDR[9:5] = io[12:8]; // Debug: show keycode and valid flag
// === Tri-State Row Output Control ===
// Drive one row low at a time, others remain high-Z
assign row_out[0] = (row_drive[0]) ? 1'bz : 1'b0;
assign row_out[1] = (row_drive[1]) ? 1'bz : 1'b0;
assign row_out[2] = (row_drive[2]) ? 1'bz : 1'b0;
assign row_out[3] = (row_drive[3]) ? 1'bz : 1'b0;
// === Row Scanning Logic ===
// Rotates row index every 1ms to scan keypad
always_ff @(posedge clk or negedge n_rst) begin
if (!n_rst)
row_idx <= 2'd3; // Start from row 3
else if (is_stable)
row_idx <= (row_idx == 0) ? 2'd3 : row_idx - 1; // Rotate rows
end
// === Row Drive Control ===
// Only one row is actively driven low at a time
always_comb begin
row_drive = 4'b1111; // Default: all rows high-Z
row_drive[row_idx] = 1'b0; // Drive current row low
end
// === Key Detection and Decoding ===
// Detects keypress and maps row/column pair to keycode
always_ff @(posedge clk or negedge n_rst) begin
if (!n_rst) begin
keycode <= 4'h0;
key_valid <= 1'b0;
end else if (is_stable) begin
key_valid <= 1'b0;
for (int j = 3; j >= 0; j--) begin
if (!col[j]) begin // Active-low column detected
// Decode row/column pair into keycode
case ({row_idx, j[1:0]})
4'b1111: keycode <= 4'h1;
4'b1110: keycode <= 4'h2;
4'b1101: keycode <= 4'h3;
4'b1100: keycode <= 4'hA;
4'b1011: keycode <= 4'h4;
4'b1010: keycode <= 4'h5;
4'b1001: keycode <= 4'h6;
4'b1000: keycode <= 4'hB;
4'b0111: keycode <= 4'h7;
4'b0110: keycode <= 4'h8;
4'b0101: keycode <= 4'h9;
4'b0100: keycode <= 4'hC;
4'b0011: keycode <= 4'hE;
4'b0010: keycode <= 4'h0;
4'b0001: keycode <= 4'hF;
4'b0000: keycode <= 4'hD;
default: keycode <= 4'h0;
endcase
key_valid <= 1'b1; // Mark key as valid
end
end
end
end
// === Debounce Timer ===
// Waits 1ms before sampling key state (50MHz clock → 50,000 cycles)
logic [15:0] stable_cnt;
logic is_stable;
always_ff @(posedge clk or negedge n_rst) begin
if (!n_rst) begin
stable_cnt <= 16'd0;
is_stable <= 1'b0;
end else if (stable_cnt == 16'd49999) begin
is_stable <= 1'b1; // 1ms elapsed
stable_cnt <= 16'd0;
end else begin
stable_cnt <= stable_cnt + 1;
is_stable <= 1'b0;
end
end
// === Debounce and Output Latching ===
// Latches keycode only if key_valid is high for 3 consecutive samples
always_ff @(posedge clk or negedge n_rst) begin
if (!n_rst) begin
key_valid_buf <= 3'b000;
keycode_buf <= 4'h0;
io_valid_shift <= 5'b00000;
end else begin
// Shift in current key_valid
key_valid_buf <= {key_valid_buf[1:0], key_valid};
// If key_valid is stable for 3 cycles, latch keycode
if (key_valid_buf == 3'b111)
keycode_buf <= keycode;
// Extend io[12] signal for visibility (5-cycle pulse)
io_valid_shift <= {io_valid_shift[3:0], (key_valid_buf == 3'b111)};
end
end
endmodule
keypad_scan.qsf
########## FPGA INFO
set_global_assignment -name FAMILY "MAX 10"
set_global_assignment -name DEVICE 10M50DAF484C7G
########## top module
set_global_assignment -name TOP_LEVEL_ENTITY keypad_scan
########## CLOCK 10MHz ADC, and 2 50MHz clock source
#set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk_adc
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk
#set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk2
#set_location_assignment PIN_N5 -to clk_adc
set_location_assignment PIN_P11 -to clk
set_location_assignment PIN_N14 -to clk2
########## RESET button & unused button (n_rst, KEY1)
set_instance_assignment -name IO_STANDARD "3.3 V SCHMITT TRIGGER" -to n_rst
set_instance_assignment -name IO_STANDARD "3.3 V SCHMITT TRIGGER" -to key1
set_location_assignment PIN_B8 -to n_rst
set_location_assignment PIN_A7 -to key1
#===========================================================================
################### Keypad USed Pins ###########
### keypad scan row: row[3:0], tri-state output as OC
# row scan signal out, row[3:0](tri-state io, set 'z' as OC cut-off)
set_location_assignment PIN_AA12 -to row_out[3]
set_location_assignment PIN_AA11 -to row_out[2]
set_location_assignment PIN_Y10 -to row_out[1]
set_location_assignment PIN_AB9 -to row_out[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to row_out[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to row_out[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to row_out[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to row_out[0]
# keypad scan column: col[3:0](input with internal pull-high)
set_location_assignment PIN_AB8 -to col[3]
set_location_assignment PIN_AB7 -to col[2]
set_location_assignment PIN_AB6 -to col[1]
set_location_assignment PIN_AB5 -to col[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to col[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to col[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to col[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to col[0]
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[3]
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[2]
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[1]
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[0]
#### key value output for keyboard
# io[8]–io[11] as keycode(4-bit)
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to io[8]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to io[9]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to io[10]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to io[11]
set_location_assignment PIN_A11 -to io[8]
set_location_assignment PIN_B11 -to io[9]
set_location_assignment PIN_D13 -to io[10]
set_location_assignment PIN_C13 -to io[11]
# io[12] as key_valid
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to io[12]
set_location_assignment PIN_E14 -to io[12]
===========================================================================
### debug LEDS
# LEDR[0]–LEDR[9] IO_STANDARD & PIN assignment
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[4]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[5]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[6]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[8]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[9]
set_location_assignment PIN_A8 -to LEDR[0]
set_location_assignment PIN_A9 -to LEDR[1]
set_location_assignment PIN_A10 -to LEDR[2]
set_location_assignment PIN_B10 -to LEDR[3]
set_location_assignment PIN_D13 -to LEDR[4]
set_location_assignment PIN_C13 -to LEDR[5]
set_location_assignment PIN_E14 -to LEDR[6]
set_location_assignment PIN_D14 -to LEDR[7]
set_location_assignment PIN_A11 -to LEDR[8]
set_location_assignment PIN_B11 -to LEDR[9]
keypad_scan.qpf
QUARTUS_VERSION = "24.1"
DATE = "13:15:49 October 11, 2025"
# Revisions
PROJECT_REVISION = "keypad_scan"
keypad_scan.sdc
create_clock -name clk -period 20.0 [get_ports clk]
留言