Debouncing calculations for Keypad
We have alluded to the problem of bouncing in mechanical switches in chap. ??. As the keypad is constructed out of many mechanical switches, it also suffers from the debouncing problem.
Typical keypress duration is 100-200 ms. To be on the safe side, we will assume that the keypress duration is 50 ms.
Debounce time of a mechanical switch is 20ms. This means that after we detect a keypress, we must wait 20ms for the keypad output to stabilize, before we attempt to read the data at the start of the keypad output.. Also because of bouncing, it may take 20 ms after pressing down the key to discover that the key is pressed. This leaves 10ms to read the key.
If we scan the keypad with 1 KHz clock, we scan a row for every 1 ms. Hence a single scan of a 4×4 keypad will take 4 ms. The scan of a 2×2 keypad will take 2 ms.
After the keypad interface detects a keypress, it is required to wait for the keypad output to be stabilized before it exports the keypress data to the rest of hardware. In a 4×4 keyboard, this is 6 scans (24 ms). In a 2×2 keypad, that is 11 scans (22 ms).
Basic schematic of the 4×4 Keypad


Connecting Keypad to the Processor


The sequence 1110 -> 1101 -> 1011 -> 011 -> 1110 -> 1101 -> etc is sent to the port X1-X4 by the CPU. Keypresses is read from the port Y1-Y4 by the processor.
Some References
https://stackoverflow.com/questions/22505698/what-is-a-typical-keypress-duration
https://appcodelabs.com/read-matrix-keypad-using-arduino
https://my.eng.utah.edu/~cs5780/debouncing.pdf
Keypad interface in Verilog
We will design a keypad interface for a 4×4 keypad.
This module sends 4 bit pressed key and ready signal to main module and reads acknowledgement signal from main module.
module ctrl_keypad4x4
(input logic i_clk,
input logic i_ack,
input logic i_rselect,
input logic [3:0] i_col_data,
output logic [3:0] o_row_data,
output logic [15:0] o_data);
localparam SCANNER_CKE_DIVISOR = 100000;
logic ready;
logic [3:0] data;
logic scanner_tick;
logic [23:0] scanner_tick_counter;
logic [3:0] key;
logic key_pressed;
logic [1:0] key_pressed_buffer;
logic [3:0] col_data [4];
logic row_active [4];
logic [3:0] row_active_debounced;
logic [11:0] row_active_buffer [4];
logic consecutive_0s [4];
logic consecutive_1s [4];
always_ff @(posedge i_clk)
begin: SCANNER_SCAN
if (scanner_tick) begin
o_row_data <= {o_row_data[2:0], o_row_data[3]};
case (o_row_data)
4'b1110: {row_active[0], col_data[0]} <= {~(&i_col_data), i_col_data};
4'b1101: {row_active[1], col_data[1]} <= {~(&i_col_data), i_col_data};
4'b1011: {row_active[2], col_data[2]} <= {~(&i_col_data), i_col_data};
4'b0111: {row_active[3], col_data[3]} <= {~(&i_col_data), i_col_data};
endcase
end
end
always_ff @(posedge i_clk)
begin: SCANNER_DEBOUNCE
if (scanner_tick) begin
for (int i = 0; i < 4; i++) begin
row_active_buffer[i] <= {row_active_buffer[i][10:0], row_active[i]};
if (consecutive_1s[i]) begin
row_active_debounced[i] <= 1;
end
if (consecutive_0s[i]) begin
row_active_debounced[i] <= 0;
end
end
end
end
always_ff @(posedge i_clk)
begin: INTERRUPT
key_pressed_buffer <= {key_pressed_buffer[0], key_pressed};
if ((key_pressed_buffer == 2'b01) && !ready) begin
data <= key;
ready <= 1;
end else if (i_ack && ready) begin
ready <= 0;
end
end
always_ff @(posedge i_clk)
begin: SCANNER_CKE
if (scanner_tick_counter == SCANNER_CKE_DIVISOR) begin
scanner_tick <= 1;
scanner_tick_counter <= 0;
end else begin
scanner_tick <= 0;
scanner_tick_counter <= scanner_tick_counter + 1;
end
end
always_comb
begin: SCANNER_DECODE
if (row_active_debounced[0]) begin
case (col_data[0])
4'b1110: key = 4'h1;
4'b1101: key = 4'h2;
4'b1011: key = 4'h3;
4'b0111: key = 4'hA;
default: key = 4'h0;
endcase
end else if (row_active_debounced[1]) begin
case (col_data[1])
4'b1110: key = 4'h4;
4'b1101: key = 4'h5;
4'b1011: key = 4'h6;
4'b0111: key = 4'hB;
default: key = 4'h0;
endcase
end else if (row_active_debounced[2]) begin
case (col_data[2])
4'b1110: key = 4'h7;
4'b1101: key = 4'h8;
4'b1011: key = 4'h9;
4'b0111: key = 4'hC;
default: key = 4'h0;
endcase
end else if (row_active_debounced[3]) begin
case (col_data[3])
4'b1110: key = 4'hE;
4'b1101: key = 4'h0;
4'b1011: key = 4'hF;
4'b0111: key = 4'hD;
default: key = 4'h0;
endcase
end else begin
key = 4'b0000;
end
end
always_comb
begin
for (int i = 0; i < 4; i++) begin
consecutive_0s[i] = ~|row_active_buffer[i];
consecutive_1s[i] = &row_active_buffer[i];
end
end
always_comb
begin
unique case (i_rselect)
1'b0: o_data = 16'(data);
1'b1: o_data = 16'(ready);
endcase
end
assign key_pressed = |row_active_debounced;
initial
begin
o_row_data = 4'b1110;
ready = 0;
end
endmodule
Main Module for Keypad
This module reads pressed key and ready signal from keypad and sent it to seven segment display
module top
(input logic i_clk,
input logic [3:0] i_keypad4x4_col_data,
output logic [3:0] o_keypad4x4_row_data,
output logic [3:0] o_grounds,
output logic [6:0] o_display);
logic [15:0] keypad4x4_data;
logic [15:0] data;
logic rselect;
logic ack;
sevensegment sevensegment_0
(.clk(i_clk),
.datain(data),
.grounds(o_grounds),
.display(o_display));
ctrl_keypad4x4 ctrl_keypad4x4_0
(.i_clk(i_clk),
.i_ack(ack),
.i_rselect(rselect),
.i_col_data(i_keypad4x4_col_data),
.o_row_data(o_keypad4x4_row_data),
.o_data(keypad4x4_data));
always_ff @(posedge i_clk)
begin
if ((keypad4x4_data[0] == 1'b1) && (rselect == 1)) begin
ack <= 1;
rselect <= 0;
end else if (rselect == 0) begin
data <= keypad4x4_data;
ack <= 0;
rselect <= 1;
end else begin
ack <= 0;
rselect <= 1;
end
end
initial
begin
data = 0;
ack = 0;
rselect = 1;
end
endmodule
EXERCISES
- Design a system in which the output of keypad is directly fed into the 4-digit seven segment display. When somebody presses a key that key will be displayed in 7-seg display. When the key is released, nothing will be displayed..
- Redesign the system above in such a way that a keypress will continue to be displayed until a new keypress.