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.