Ads

Get STM32 tutorial using HAL at $10 for a limited time!

Monday, February 29, 2016

ARM Cortex-M3 (STM32F103) Tutorial - 4x4 Matrix Keypad

Embedded system applications usually require large number of buttons connected (ex. calculator, cell phone). Keypad is one of the input device that commonly used in embedded system that require large number of buttons. 4x4 matrix keypad have 16 buttons. We can connect this buttons directly to GPIO pin, but it will eat up precious GPIO pin (If we have 16 buttons, then we need 16 GPIO pins). To avoid this trouble, keypad use technique that will save GPIO pin. In this technique, buttons are connected in a matrix (row/column) style.


The internal connection of the buttons in 4x4 matrix keypad is like this:


From image above, for example if S8 is pressed, then Col 1 and Row 2 is connected. We can detect this by using a microcontroller. The microcontroller must scan every columns then on each column, read the rows value.

To connect  this keypad to the microcontroller, we need 8 GPIO pins. The column pins is connected to the GPIO pins as open drain output. The row pins is connected to the GPIO pins as input with internal pull-up resistor. Every each column scan, the column pin is set to logic 0 (GND), the other column pins is in HI-Z (floating) condition. So, when no button pressed in this column, then all row pins will receive logic 1 because of pull-up resistor. When there is a button pressed on this column, then the corresponding row pin for that button will receive logic 0, because the current from pull-up resistor will flow to column pin.

This is the function for read key press:
uint8_t KeypadGetKey()
{
    // Scan column 0 (column 0 pin is grounded, other column pins 
    // is open drain)
    GPIO_ResetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL0);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL1);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL2);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL3);
    DelayUs(1);
    // Read rows
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW0))
        return '1';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW1))
        return '4';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW2))
        return '7';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW3))
        return '*';
  
    // Scan column 1 (column 1 pin is grounded, other column pins 
    // is open drain)
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL0);
    GPIO_ResetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL1);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL2);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL3);
    DelayUs(1);
    // Read rows
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW0))
        return '2';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW1))
        return '5';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW2))
        return '8';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW3))
        return '0';
  
    // Scan column 2 (column 2 pin is grounded, other column pins 
    // is open drain)
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL0);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL1);
    GPIO_ResetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL2);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL3);
    DelayUs(1);
    // Read rows
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW0))
        return '3';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW1))
        return '6';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW2))
        return '9';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW3))
        return '#';
  
    // Scan column 3 (column 3 pin is grounded, other column pins 
    // is open drain)
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL0);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL1);
    GPIO_SetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL2);
    GPIO_ResetBits(KEYPAD_GPIO_COL, KEYPAD_PIN_COL3);
    DelayUs(1);
    // Read rows
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW0))
        return 'A';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW1))
        return 'B';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW2))
        return 'C';
    if (!GPIO_ReadInputDataBit(KEYPAD_GPIO_ROW, KEYPAD_PIN_ROW3))
        return 'D';

    return KEYPAD_NO_PRESSED;
}
The simple way to use this function is with polling method until a key is pressed. This is a simple program to get a key pressed and display that character to the LCD 16x2:
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "delay.h"
#include "lcd16x2.h"
#include "keypad4x4-scanning.h"

uint8_t key;

int main(void)
{
    DelayInit();
    lcd16x2_init(LCD16X2_DISPLAY_ON_CURSOR_OFF_BLINK_OFF);
    KeypadInit();

    while (1)
    {
        // Get key pressed
        key = KeypadGetKey();

        // Display pressed char to LCD
        if (key != KEYPAD_NO_PRESSED)
        {
            lcd16x2_gotoxy(0, 0);
            lcd16x2_putc(key);
            DelayMs(250);
        }
    }
}
You can get the full code on my GitHub repository.

5 comments :

  1. Very helpful blogging. Already I’ve visited several posts about membrane keypad on the web. All the posts are very effective. I’d like to see more such beautiful posts from you.

    ReplyDelete
  2. Thanks for explaining membrane keypad with coding.Sure it will helpful for a using matrix keypads.

    ReplyDelete
  3. I want to ask, how to program the stm32f4 discovery, 4x4 keypad and I2C with LCD? I tried it but there are always lots of bugs

    ReplyDelete
  4. Thanks for sharing,get an best membrane keypadfor affordable prices.

    ReplyDelete