Skip to content

Basic I/O-3: Keypad

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

  1. 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..
  2. Redesign the system above in such a way that a keypress will continue to be displayed until a new keypress.