/* p3_5.c: Matrix keypad scanning * * This program scans a 4x4 matrix keypad and returns a unique * number for each key pressed. * LD2 (green LED) is used to blink the returned number. * * PC0-3 are connected to the columns and PC4-7 are * connected to the rows of the keypad. * * This program was tested with Keil uVision v5.23 with DFP v2.0.0 */ #include "stm32f0xx.h" void delay(void); void delayMs(int n); void keypad_init(void); char keypad_getkey(void); void LED_init(void); void LED_blink(int value); int main(void) { unsigned char key; keypad_init(); LED_init(); while(1) { key = keypad_getkey(); /* read the keypad */ if (key != 0) LED_blink(key); /* set LEDs according to the key code */ } } /* this function initializes PC0-3 (column) and PC4-7 (row). * The column pins need to have the pull-up resistors enabled. */ void keypad_init(void) { RCC->AHBENR |= 0x00080000; /* enable GPIOC clock */ GPIOC->MODER &= ~0x0000FFFF; /* clear pin mode to input */ GPIOC->PUPDR = 0x00000055; /* enable pull up resistors for column pins */ } /* * This is a non-blocking function to read the keypad. * If a key is pressed, it returns a unique code for the key. * Otherwise, zero is returned. * PC6-9 are used as input and connected to the columns. Pull-up resistors are * enabled so when the keys are not pressed, these pins are pulled high. * PC4-7 are used as output that drives the keypad rows. * First, all rows are driven low and the input pins are read. If no key is * pressed, they will read as all one because of the pull-up resistors. * If they are not all one, some key is pressed. * If 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 int row_mode[] = {0x00000100, 0x00000400, 0x00001000, 0x00004000}; /* one row is output */ const int row_low[] = {0x00100000, 0x00200000, 0x00400000, 0x00800000}; /* one row is low */ const int row_high[] = {0x00000010, 0x00000020, 0x00000040, 0x00000080}; /* one row is high */ /* check to see any key pressed */ GPIOC->MODER = 0x00005500; /* make all row pins output */ GPIOC->BSRR = 0x00F00000; /* drive all row pins low */ delay(); /* wait for signals to settle */ col = GPIOC->IDR & 0x000F; /* read all column pins */ GPIOC->MODER &= ~0x0000FF00; /* disable all row pins drive */ if (col == 0x000F) /* if all columns are high */ return 0; /* no key pressed */ /* If a key is pressed, it gets here to find out which key. * It activates one row at a time and read the input to see * which column is active. */ for (row = 0; row < 4; row++) { GPIOC->MODER &= ~0x0000FF00; /* disable all row pins drive */ GPIOC->MODER |= row_mode[row]; /* enable one row at a time */ GPIOC->BSRR = row_low[row]; /* drive the active row low */ delay(); /* wait for signal to settle */ col = GPIOC->IDR & 0x000F; /* read all columns */ GPIOC->BSRR = row_high[row]; /* drive the active row high */ if (col != 0x000F) break; /* if one of the input is low, some key is pressed. */ } GPIOC->BSRR = 0x000000F0; /* drive all rows high before disable them */ GPIOC->MODER &= ~0x0000FF00; /* disable all rows */ 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 == 0x000E) return row * 4 + 1; /* key in column 0 */ if (col == 0x000D) return row * 4 + 2; /* key in column 1 */ if (col == 0x000B) return row * 4 + 3; /* key in column 2 */ if (col == 0x0007) return row * 4 + 4; /* key in column 3 */ return 0; /* just to be safe */ } void LED_init(void) { // enable PA5 for green LED RCC->AHBENR |= 0x00020000; /* enable GPIOA clock */ GPIOA->MODER &= ~0xC0000C00; /* clear pin mode */ GPIOA->MODER |= 0x00000400; /* set pin output mode */ } /* turn on or off the LEDs according to the value */ void LED_blink(int value) { value %= 17; /* cap the max count at 16 */ for (; value > 0; value--) { GPIOA->BSRR = 0x00000020; /* turn on LED */ delayMs(200); GPIOA->BSRR = 0x00200000; /* turn off LED */ delayMs(200); } delayMs(200); } /* make a small delay */ void delay(void) { int i; for (i = 0; i < 20; i++) ; } /* 8 MHz SYSCLK */ void delayMs(int n) { int i; for (; n > 0; n--) for (i = 0; i < 1142; i++) ; }