/* p3_5.c: Matrix keypad scanning * * This program scans a 4x4 matrix keypad and returns a unique number * for each key pressed. LED0 is used to blink the returned number. * * PortB 07-04 are connected to the columns and PortB 03-00 are connected * to the rows of the keypad. The columns are used for inputs while * the rows are used for excitation outputs. * * Tested with Atmel Studio 7 v7.0.1006 and Keil MDK-ARM v5.21a */ #include "samd21.h" void delayMs(int n); void keypad_init(void); char keypad_getkey(void); void LED_blink(int value); int main(void) { unsigned char key; keypad_init(); REG_PORT_DIRSET1 = 0x40000000; /* make PB30 output for LED0 */ REG_PORT_OUTSET1 = 0x40000000; /* turn off LED0 */ while(1) { key = keypad_getkey(); /* read the keypad */ if (key != 0) LED_blink(key); /* blink LED0 according to the key code */ } } /* This function initializes PortB7-0 that is connected to the keypad. * All pins are configured as GPIO input pin. The column pins have * the pull-up resistors enabled. */ void keypad_init(void) { int i; unsigned char* ARRAY_PORT_PINCFG1 = (unsigned char*)®_PORT_PINCFG1; REG_PORT_DIRSET1 = 0x0000000F; /* PB03-00 output */ REG_PORT_DIRCLR1 = 0x000000F0; /* PB07-04 input */ for (i = 4; i < 8; i++) ARRAY_PORT_PINCFG1[i] |= 6; /* enable PB07-04 input buffer with pull */ REG_PORT_OUTSET1 = 0x000000F0; /* PB07-04 pull-up */ } /* * This is a non-blocking function to read the keypad. * If a key is pressed, it returns a unique code for the key. Otherwise, * a zero is returned. * PortB7-4 are used as inputs and connected to the columns. * Pull-up resistors are enabled so when the keys are not pressed, these pins * are pulled high. * PortB3-0 are used as excitation outputs that drive the keypad rows. * * First, all rows are driven low and the input pins are read. If no key is pressed, * they are read as all one because of the pull-ups. If they are not all one, * some key is pressed. * When some key is pressed, the program proceeds to drive only one row low at a time * and leave the rest of the rows inactive (float) then read the input pins. * Knowing which row is active and which column is active, the program can decide * which key is pressed. * * Only one row is driven so that if multiple keys are pressed and row pins are * shorted, the microcontroller will not be damaged. When the row is being * deactivated, it is driven high first otherwise the stray capacitance may keep * the inactive row low for some time. */ char keypad_getkey(void) { int row, col; const char row_select[] = {0x01, 0x02, 0x04, 0x08}; /* one row is active */ /* check to see any key pressed */ REG_PORT_DIRSET1 = 0x0F; /* make all row pins output */ REG_PORT_OUTCLR1 = 0x0F; /* drive all row pins low */ delayMs(0); /* wait for signals to settle */ col = REG_PORT_IN1 & 0xF0; /* read all column pins */ REG_PORT_OUTSET1 = 0x0F; /* drive all rows high before disable them */ REG_PORT_DIRCLR1 = 0x0F; /* make all row pins input */ if (col == 0xF0) /* if all columns are high */ return 0; /* no key pressed */ /* If a key is pressed, it comes here to find out which key. * It activates one row at a time and read the inputs to see * which column is active. */ for (row = 0; row < 4; row++) { REG_PORT_DIRCLR1 = 0x0F; /* disable all rows */ REG_PORT_DIRSET1 = row_select[row]; /* enable one row at a time */ REG_PORT_OUTCLR1 = row_select[row]; /* drive the active row low */ delayMs(0); /* wait for signal to settle */ col = REG_PORT_IN1 & 0xF0; /* read all columns */ REG_PORT_OUTSET1 = row_select[row]; /* drive the active row high */ if (col != 0xF0) break; /* if one of the input is low, some key is pressed. */ } REG_PORT_OUTSET1 = 0x0F; /* drive all rows high before disable them */ REG_PORT_DIRCLR1 = 0x0F; /* make all row pins input */ if (row == 4) return 0; /* if we get here, no key is pressed */ /* gets here when one of the rows has key pressed, check which column it is */ if (col == 0xE0) return row * 4 + 1; /* key in column 0 */ if (col == 0xD0) return row * 4 + 2; /* key in column 1 */ if (col == 0xB0) return row * 4 + 3; /* key in column 2 */ if (col == 0x70) return row * 4 + 4; /* key in column 3 */ return 0; /* just to be safe */ } /* turn on or off the LEDs according to the value */ void LED_blink(int value) { value &= 0x1F; /* only bit 4-0 are used */ for (; value > 0; value--) { REG_PORT_OUTCLR1 = 0x40000000; /* turn on LED0 */ delayMs(200); REG_PORT_OUTSET1 = 0x40000000; /* turn off LED0 */ delayMs(200); } delayMs(800); } /* millisecond delay based on 1 MHz system clock */ void delayMs(int n) { int i; for (; n > 0; n--) for (i = 0; i < 199; i++) __asm("nop"); }