czwartek, 12 lipca 2018

[7] ATxmega - Klawiatura

W tym poście chciałem przedstawić sposób implementacji klawiatury matrycowej.

[Źródło: http://www.atmel.com/images/Atmel-8135-8-and-16-bit-AVR-microcontroller-ATxmega16D4-32D4-64D4-128D4_datasheet.pdf]

Program:


Poniżej opiszę wszystkie potrzebne funkcje dla obsługi klawiatury z 12 przyciskami 3x4.

Na samym początku należy zdefiniować następujące parametry:

  1. #define KEYPAD_BLOCK_SAME_BTN_CLICKED
  2.  
  3. #define KEYPAD_NO_PRESSED           (uint8_t)0xFF
  4. #define KEYPAD_READ_INTERVAL        100

Pierwsza wartość oznacza, że kliknięcie przycisku i przytrzymanie będzie blokowane aż do jego puszczenia. Nie nastąpi ponowny odczyt wybranego przycisku po odczekaniu interwału czasowego. Druga wartość określa wartość przycisku, gdy żaden nie został wybrany.

Następnym elementem jest definicja numeru dla klikniętego klawisza:

  1. typedef enum {
  2.     Btn_0 = 0x00,
  3.     Btn_1 = 0x01,
  4.     Btn_2 = 0x02,
  5.     Btn_3 = 0x03,
  6.     Btn_4 = 0x04,
  7.     Btn_5 = 0x05,
  8.     Btn_6 = 0x06,
  9.     Btn_7 = 0x07,
  10.     Btn_8 = 0x08,
  11.     Btn_9 = 0x09,
  12.     Btn_Star = 0x0A,
  13.     Btn_Hash = 0x0B,
  14.     Btn_None = KEYPAD_NO_PRESSED
  15. } Keyboard_Btn_Typedef;

Dalej znajduje się struktura przechowująca ostatnie dwa kliknięte przyciski:

  1. typedef struct {
  2.     volatile Keyboard_Btn_Typedef clickedBtn;
  3.     #ifdef KEYPAD_BLOCK_SAME_BTN_CLICKED
  4.     volatile Keyboard_Btn_Typedef prevClickedBtn;
  5.     #endif
  6. }keypad_TypeDef_Struct;

Tablica z klawiaturą, oraz inicjalizacja struktury:

  1. uint8_t keypadBtn[4][3] = {
  2.                             {Btn_1, Btn_2, Btn_3},
  3.                             {Btn_4, Btn_5, Btn_6},
  4.                             {Btn_7, Btn_8, Btn_9},
  5.                             {Btn_Star, Btn_0, Btn_Hash}
  6.                           };
  7.  
  8. static volatile keypad_TypeDef_Struct keypadStatusClicked = {
  9.     .clickedBtn = Btn_None,
  10.     .prevClickedBtn = Btn_None
  11. };

Inicjalizacja pinów:

  1. static inline void ioport_configure_pin(port_pin_t pin, port_pin_flags_t flags)
  2. {
  3.     ioport_configure_port_pin(arch_ioport_pin_to_base(pin),
  4.             arch_ioport_pin_to_mask(pin), flags);
  5. }
  6.  
  7. void KeypadInit(void)
  8. {
  9.     //KLAW_V3        - IOPORT_CREATE_PIN(PORTB, 0)
  10.     //KLAW_V3_CONFIG - IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW
  11.     ioport_configure_pin(KLAW_V1, KLAW_V3_CONFIG);
  12.     ioport_configure_pin(KLAW_V2, KLAW_V2_CONFIG);
  13.     ioport_configure_pin(KLAW_V3, KLAW_V1_CONFIG);
  14.    
  15.     //KLAW_H4        - IOPORT_CREATE_PIN(PORTA, 4)
  16.     //KLAW_H4_CONFIG - IOPORT_DIR_INPUT | IOPORT_PULL_UP
  17.     ioport_configure_pin(KLAW_H1, KLAW_H4_CONFIG);
  18.     ioport_configure_pin(KLAW_H2, KLAW_H3_CONFIG);
  19.     ioport_configure_pin(KLAW_H3, KLAW_H2_CONFIG);
  20.     ioport_configure_pin(KLAW_H4, KLAW_H1_CONFIG);
  21. }

Odczytanie stanu klawisza wygląda następująco:

  1. Keyboard_Btn_Typedef KeyPad_ReadBtn(void)
  2. {
  3.     Keyboard_Btn_Typedef tempRead;
  4.  
  5.     tempRead = keypadStatusClicked.clickedBtn;
  6.  
  7.     keypadStatusClicked.clickedBtn = KEYPAD_NO_PRESSED;
  8.  
  9.     return tempRead;
  10. }

Sprawdzenie stanu linii i wpisanie wprowadzanego klawisza:

  1. void KeyPad_Update(void)
  2. {
  3.     static uint16_t timeCounter = 0;
  4.     #ifdef KEYPAD_BLOCK_SAME_BTN_CLICKED
  5.     volatile Keyboard_Btn_Typedef tmpClickedBtn;
  6.     #endif
  7.  
  8.     timeCounter++;
  9.  
  10.     if (timeCounter >= KEYPAD_READ_INTERVAL && keypadStatusClicked.clickedBtn == KEYPAD_NO_PRESSED)
  11.     {
  12.         timeCounter = 0;
  13.        
  14.         #ifdef KEYPAD_BLOCK_SAME_BTN_CLICKED
  15.        
  16.         tmpClickedBtn = (Keyboard_Btn_Typedef)keyPad_Read();
  17.        
  18.         if(tmpClickedBtn == keypadStatusClicked.prevClickedBtn)
  19.         {
  20.             keypadStatusClicked.clickedBtn = KEYPAD_NO_PRESSED;
  21.         }
  22.         else
  23.         {
  24.             keypadStatusClicked.clickedBtn = tmpClickedBtn;
  25.             keypadStatusClicked.prevClickedBtn = tmpClickedBtn;
  26.             tmpClickedBtn = KEYPAD_NO_PRESSED;
  27.         }
  28.        
  29.         #else
  30.        
  31.         keypadStatusClicked.clickedBtn = (Keyboard_Btn_Typedef)keyPad_Read();
  32.        
  33.         #endif
  34.     }
  35. }

Funkcja KeyPad_Update aby działa poprawnie i sprawnie musi być wywoływana w przerwaniu od timera. Dzięki temu nie doświadczy się opóźnień w jej działaniu.

Wcześniejsza funkcja Keypad_ReadBtn może być wywoływana w pętli głównej przed obsłużeniem klikniętego przycisku.

Ustawienie stanu kolumn:

  1. static void keyPad_SetColumn(uint8_t column)
  2. {
  3.     gpio_set_pin_high(KLAW_V1);
  4.     gpio_set_pin_high(KLAW_V2);
  5.     gpio_set_pin_high(KLAW_V3);
  6.  
  7.     /* Set column low */
  8.     if (column == 1) {
  9.         gpio_set_pin_low(KLAW_V1);
  10.     }
  11.     if (column == 2) {
  12.         gpio_set_pin_low(KLAW_V2);
  13.     }
  14.     if (column == 3) {
  15.         gpio_set_pin_low(KLAW_V3);
  16.     }
  17. }

Sprawdzenie stanu na wierszach:

  1. static uint8_t keyPad_CheckRow(uint8_t column)
  2. {
  3.     if (gpio_pin_is_low(KLAW_H1)) { return keypadBtn[0][column - 1]; }          /* Scan row 1 */
  4.  
  5.     if (gpio_pin_is_low(KLAW_H2)) { return keypadBtn[1][column - 1]; }          /* Scan row 2 */
  6.  
  7.     if (gpio_pin_is_low(KLAW_H3)) { return keypadBtn[2][column - 1]; }          /* Scan row 3 */
  8.        
  9.     if (gpio_pin_is_low(KLAW_H4)) { return keypadBtn[3][column - 1]; }          /* Scan row 4 */
  10.  
  11.     return KEYPAD_NO_PRESSED;
  12. }

Funkcja odczytująca który klawisz został kliknięty:

  1. static uint8_t keyPad_Read(void)
  2. {
  3.     uint8_t check;
  4.  
  5.     for(volatile uint8_t i = 1; i<=3; i++)
  6.     {
  7.         keyPad_SetColumn(i);
  8.  
  9.         check = keyPad_CheckRow(i);
  10.         if (check != KEYPAD_NO_PRESSED) {   return check;   }
  11.     }
  12.  
  13.     return KEYPAD_NO_PRESSED;
  14. }

Obsługa wciśniętego klawisza:

  1. void KeyToWiegand(keypad_TypeDef_Struct *btnStruct)
  2. {
  3.     if(btnStruct->clickedBtn == (Btn_Hash+1))
  4.     {
  5.         return;
  6.     }
  7.    
  8.     if(btnStruct->clickedBtn == Btn_0)
  9.     {
  10.     }
  11.     else if(btnStruct->clickedBtn == Btn_1)
  12.     {
  13.     }
  14.     else if(btnStruct->clickedBtn == Btn_2)
  15.     {
  16.     }
  17.     else if(btnStruct->clickedBtn == Btn_3)
  18.     {      
  19.     }
  20.     else if(btnStruct->clickedBtn == Btn_4)
  21.     {
  22.     }
  23.     else if(btnStruct->clickedBtn == Btn_5)
  24.     {
  25.     }
  26.     else if(btnStruct->clickedBtn == Btn_6)
  27.     {
  28.     }
  29.     else if(btnStruct->clickedBtn == Btn_7)
  30.     {
  31.     }
  32.     else if(btnStruct->clickedBtn == Btn_8)
  33.     {  
  34.     }
  35.     else if(btnStruct->clickedBtn == Btn_9)
  36.     {
  37.     }
  38.     else if(btnStruct->clickedBtn == Btn_Star)
  39.     {
  40.     }
  41.     else if(btnStruct->clickedBtn == Btn_Hash)
  42.     {
  43.     }
  44. }