Akademia Górniczo-Hutnicza w Krakowie Katedra Elektroniki WIET Laboratorium mikrokontrolerów Ćwiczenie 4A Klawiatura matrycowa - projekt Autor: Paweł Russek http://www.fpga.agh.edu.pl/pm ver. 23.10.16 1/8
1. Cel ćwiczenia Celem ćwiczenia jest wykorzystanie przez student wiedzy w z zakresu programowania układów GPIO w celu stworzenia aplikacji wykorzystującej klawiaturę matrycową. W ćwiczeniu zastosowano mikrokontroler z rodziny Kinetis L firmy NXP. Literatura: W opisach wykorzystanych w tym ćwiczeniu posłużono się opisami zaczerpniętymi z książki: Muhammad Ali; Chen, Shujen; Naimi, Sarmad; Naimi, Sepehr. Freescale ARM Cortex-M Embedded Programming: Using C Language (ARM books Book 3) 2. Klawiatura matrycowa budowa i opis działania 2.1. Podłączenie klawiatury matrycowej do mikrokontrolera W celu ograniczenia liczby końcówek mikrokontrolera koniecznych do podłączenia klawiatury, przyciski tzw. klawiatury matrycowej są zorganizowane w wierszach i kolumnach. Mikrokontroler odczytuje odpowiednie wiersze i kolumny za pomocą swoich portów GPIO i dlatego, dla przykładu, osiem pinów mikrokontrolera jest wystarczające do podłączenia klawiatury 4 4 składającej się z 16 przycisków. Kiedy odpowiedni przycisk jest wciśnięty para linii wiersz-kolumna są ze sobą zwierane. W innym przypadku linie kolumn i wierszy są rozłączone. W tym ćwiczeniu zaprezentujemy w jaki sposób mikrokontroler może skanować połączenia klawiatury w celu identyfikacji wciśniętego przycisku. 2.2. Identyfikacja wciśniętego klawisza Rysunek pokazuje podłączenie klawiatury matrycowej mikrokontrolera. 4 4 do dwóch portów 2/8
Wiersze są podłączone do portu Wyjściowego (Out), a kolumny do portu Wejściowego (In). Piny portu wejściowego maja podłączone rezystory podciągające (pull-up resistors) ponieważ w innym przypadku wejścia byłyby niespolaryzowane przy braku wciśniętego przycisku. Kiedy żaden przycisk nie jest wciśnięty odczyt stanów portu In zwróci same jedynki. Jeżeli wszystkie wyjścia portu Out są wysterowane na zero, to wciśnięcie przycisku wymusi stan zero na odpowiedniej linii portu In. Zadaniem mikroprocesora jest ciągłe przemiatanie wierszy klawiatury matrycowej i odczytywanie stanu kolumn w celu identyfikacji wcisniętego przyciku. Dalej wyjaśnimy szczegółowo jak jest to realizowane. 2.3. Detekcja wciśnięcia przycisku Aby wykryć, że któryś z przycisków klawiatury został wciśnięty należy wysterować wszystkie linie portu wierszy Out na zero. Jeżeli po przeczytaniu linii kolumn portu In odczytamy D7 D4 = 1111, oznacza to, że żaden z przycisków nie został wybrany. Jednak jeżeli jakakolwiek linia kolumn była zero, to oznacza, że któryś przycisk został wciśnięty. Przykładowo, jeżeli D7 D4 = 1101, to oznacza, że któryś przycisk w kolumnie D5 został wybrany. 2.4. Identyfikacja wciśniętego przycisku Po wykryciu wciśnięcia przycisku, mikrokontroler rozpoczyna procedurę jego identyfikacji. W tym celu, zaczynając od pierwszego wiersza, mikrokontroler wystawia zero na pierwszej linii portu wierszy Out, a następnie odczytuje stan kolumn. Jeżeli stan bitów na porcie kolumn In to same jedynki, oznacza to, że wciśnięty przycisk nie znajduje się w aktualnie aktywnym pierwszym wierszu. Następnie mikrokontroler, wystawia zero na drugiej linii portu wierszy Out, a następnie znów odczytuje stan kolumn sprawdzając występowanie zer. Proces jest powtarzany dla kolenych kolumn tak długo dopóki na linii In nie zostanie odczytane jakieś zero. Pozycja zera w porcie kolumn In determinuje pozycję przycisku w kolumnach, a aktualnie aktywny wiersz w wierszach. Przykład: Zgodnie z rysunkiem powyzej: Jeżeli D3 D0 = 1110 dla wierszy i D7 D4 = 1011 dla kolumn, to wciśnięty przycisk w wierszu D0 i kolumnie D6. Dlatego wybrany przycisk to przycisk 2. Jeżeli D3 D0 = 1101 dla wierszy i D7 D4 = 0111 dla kolumn, to wciśnięty przycisk w wierszu D1 i kolumnie D7. Dlatego wybrany przycisk to przycisk 4. Poniżej przedstawiono diagram algorytmu detekcji i identyfikacji przycisku. 3/8
Przykładowy program /** Matrix keypad scanning * This program scans a 4x4 matrix keypad and returns a unique code for each key pressed. * The number is displayed on the tri-color LEDs using the code table * PortC 7-4 are connected to the columns and PortC 3-0 are connected to the rows. #include <MKL25Z4.H> void delayms(int n); void delayus(int n); void keypad_init(void); char keypad_getkey(void); void LED_init(void); void LED_set(int value); int main(void) 4/8
unsigned char key; keypad_init(); LED_init(); while(1) key = keypad_getkey(); LED_set(key); /* set LEDs according to the key code /* This function initializes PortC that is connected to the keypad. * All pins are configured as GPIO input pin with pull-up enabled. void keypad_init(void) SIM->SCGC5 = SIM_SCGC5_PORTC(1); /* enable clock to Port C /* make PTC pins 0-7 as GPIO and enable pullup PORTC->PCR[0] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[1] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[2] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[3] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[4] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[5] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[6] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); PORTC->PCR[7] = PORT_PCR_MUX(1) PORT_PCR_PE(1) PORT_PCR_PS(1); /* make PTC3-0 outputs and PTC4-7 inputs PTC->PDDR = 0b00001111; /* initialize all three LEDs on the FRDM board void LED_init(void) 5/8
SIM->SCGC5 = SIM_SCGC5_PORTB(1); /* enable clock to Port B SIM->SCGC5 = SIM_SCGC5_PORTD(1); /* enable clock to Port D PORTB->PCR[18] = PORT_PCR_MUX(1); /* make PTB18 pin as GPIO PTB->PDDR = 0x40000; /* make PTB18 as output pin PTB->PSOR = 0x40000; /* turn off red LED PORTB->PCR[19] = PORT_PCR_MUX(1); /* make PTB19 pin as GPIO PTB->PDDR = 0x80000; /* make PTB19 as output pin PTB->PSOR = 0x80000; /* turn off green LED PORTD->PCR[1] = PORT_PCR_MUX(1); /* make PTD1 pin as GPIO PTD->PDDR = 0x02; /* make PTD1 as output pin PTD->PSOR = 0x02; /* turn off blue LED /* * This is a non-blocking function to read the keypad. * If a key is pressed, it returns a key code. Otherwise, a zero is returned * The upper nibble of Port C is used as input. Pull-ups are enabled * when the keys are not pressed, these pins are pull up high. * The lower nibble of Port C is 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, it will read as all ones. Otherwise, some key is pressed. * If any key is pressed, the program drives 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. 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 PTC->PDDR = 0x0F; /* enable all rows 6/8
PTC->PCOR = 0x0F; delayus(2); /* wait for signal return col = PTC->PDIR & 0xF0; /* read all columns PTC->PDDR = 0; /* disable all rows if (col == 0xF0) 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++) PTC->PDDR = 0; /* disable all rows PTC->PDDR = row_select[row]; /* enable one row PTC->PCOR = row_select[row]; /* drive the active row low delayus(2); /* wait for signal to settle col = PTC->PDIR & 0xF0; /* read all columns if (col!= 0xF0) break; /* if one of the input is low, some key is pressed. PTC->PDDR = 0; /* disable all rows - set as inputs 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 bit 2-0 of the value void LED_set(int value) if (value & 1) /* use bit 0 of value to control red LED 7/8
PTB->PCOR = 0x40000; /* turn on red LED else PTB->PSOR = 0x40000; /* turn off red LED if (value & 2) /* use bit 1 of value to control green LED PTB->PCOR = 0x80000; /* turn on green LED else PTB->PSOR = 0x80000; /* turn off green LED if (value & 4) /* use bit 2 of value to control blue LED PTD->PCOR = 0x02; /* turn on blue LED else PTD->PSOR = 0x02; /* turn off blue LED /** delay n microseconds * The CPU core clock is set to MCGFLLCLK at 41.94 MHz in SystemInit(). void delayus(int n) int i; int j; for(i = 0 ; i < n; i++) for(j = 0; j < 5; j++) ; Ćwiczenie 2.2 Napisz program, który czyta klawiaturę matrycową 4x4 i wykorzystuje kod klawisza do sterowania trójkolorową diodą. Numer kolumny steruje jasnością diody CZERWONEJ w zakresie 0-3. Numer wiersza steruje jasnością diody ZIELONEJ w zakresie 0-3. Liczba przyciśnięć klawisza steruje intensywnością diody NIEBIESKIEJ w zakresie 0-3 (POZIOM = LICZBA_WCIŚNIĘĆ mod 4). 8/8