├── Keyboard_Matrix1.png ├── examples ├── Keypad_Matrix_Test │ └── Keypad_Matrix_Test.ino └── Message_Generator │ └── Message_Generator.ino ├── README.md ├── Keypad_Matrix.h └── Keypad_Matrix.cpp /Keyboard_Matrix1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickgammon/Keypad_Matrix/HEAD/Keyboard_Matrix1.png -------------------------------------------------------------------------------- /examples/Keypad_Matrix_Test/Keypad_Matrix_Test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Example of using Keypad_Matrix with a 4x4 keypad matrix. 4 | * 5 | */ 6 | 7 | #include 8 | 9 | const byte ROWS = 4; 10 | const byte COLS = 4; 11 | 12 | // how the keypad has its keys laid out 13 | const char keys[ROWS][COLS] = { 14 | {'1', '2', '3', 'A'}, 15 | {'4', '5', '6', 'B'}, 16 | {'7', '8', '9', 'C'}, 17 | {'*', '0', '#', 'D'}, 18 | }; 19 | 20 | const byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad 21 | const byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad 22 | 23 | // Create the Keypad 24 | Keypad_Matrix kpd = Keypad_Matrix( makeKeymap (keys), rowPins, colPins, ROWS, COLS ); 25 | 26 | void keyDown (const char which) 27 | { 28 | Serial.print (F("Key down: ")); 29 | Serial.println (which); 30 | if (which >= '0' && which <= '9') 31 | { 32 | if (kpd.isKeyDown ('A')) 33 | Serial.println ("A is down as well."); 34 | if (kpd.isKeyDown ('B')) 35 | Serial.println ("B is down as well."); 36 | if (kpd.isKeyDown ('C')) 37 | Serial.println ("C is down as well."); 38 | if (kpd.isKeyDown ('D')) 39 | Serial.println ("D is down as well."); 40 | } 41 | } 42 | 43 | void keyUp (const char which) 44 | { 45 | Serial.print (F("Key up: ")); 46 | Serial.println (which); 47 | } 48 | 49 | 50 | void setup() 51 | { 52 | Serial.begin (115200); 53 | Serial.println ("Starting."); 54 | kpd.begin (); 55 | kpd.setKeyDownHandler (keyDown); 56 | kpd.setKeyUpHandler (keyUp); 57 | } 58 | 59 | void loop() 60 | { 61 | kpd.scan (); 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Keypad Matrix library 2 | 3 | This supports a keypad matrix as described [in this post](http://www.gammon.com.au/forum/?id=14175). 4 | 5 | Example wiring for 4x4 matrix: 6 | 7 | ![Keyboard matrix](Keyboard_Matrix1.png) 8 | 9 | It supports n-key rollover (multiple keys pressed at once). To handle more than two keys pressed at once diodes (eg. 1N4148) are strongly recommend as shown in the above schematic. Without them you will get "ghost" key presses under certain circumstances (with 3 or more keys pressed at once). 10 | 11 | 12 | ### Example of use 13 | 14 | ```c++ 15 | const byte ROWS = 4; 16 | const byte COLS = 4; 17 | 18 | // how the keypad has its keys laid out 19 | const char keys[ROWS][COLS] = { 20 | {'1', '2', '3', 'A'}, 21 | {'4', '5', '6', 'B'}, 22 | {'7', '8', '9', 'C'}, 23 | {'*', '0', '#', 'D'}, 24 | }; 25 | 26 | const byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad 27 | const byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad 28 | 29 | // Create the Keypad (makeKeymap casts the keys array to const char *) 30 | Keypad_Matrix kpd = Keypad_Matrix (makeKeymap (keys), rowPins, colPins, ROWS, COLS ); 31 | ``` 32 | 33 | In setup, call "begin" to allocate memory, and set a keydown and/or a keyup handler: 34 | 35 | ```c++ 36 | void setup () 37 | { 38 | kpd.begin (); 39 | kpd.setKeyDownHandler (keyDown); 40 | } 41 | ``` 42 | 43 | In loop, call "scan" to check the current key status: 44 | 45 | ```c++ 46 | void loop() 47 | { 48 | kpd.scan (); 49 | } 50 | ``` 51 | 52 | 53 | The handlers return the keyvalue (from the "keys" array) corresponding to which key just went down or up. The handlers are 54 | called automatically for the appropriate keys during the call to "scan" above. 55 | 56 | ```c++ 57 | void keyDown (const char which) 58 | { 59 | Serial.print (F("Key down: ")); 60 | Serial.println (which); 61 | } // end of keyDown 62 | ``` 63 | 64 | You can also query if other keys are currently down (eg. for doing something like Ctrl+C) 65 | 66 | ```c++ 67 | if (isKeyDown ('*')) 68 | { 69 | // do something 70 | } 71 | ``` 72 | 73 | ### Custom handlers 74 | 75 | The row handlers and read handler are intended to allow you to do your own reading of the columns. For example, 76 | you might use a 74HC165 to shift in 8 bits using SPI. You can omit those for conventional wiring to digital 77 | pins as default handlers are supplied. 78 | 79 | The read handler is called once for each column (passed as an argument the column pin from the colPins array). The default 80 | behaviour is to do a digital read of that column. The read handler should return LOW (pressed) or HIGH (not pressed). 81 | 82 | The start row handler is intended to prepare for reading the columns (for example, it might read the bits for each column in one operation). The default behaviour is to set that row to OUTPUT and LOW. 83 | 84 | The end row handlers is intended to wrap up after a row. The default behaviour is to set that row back to INPUT mode. 85 | 86 | For external hardware (like shift registers) you may want to pass "false" to enablePullups as that won't be relevant there. 87 | 88 | 89 | ### How to install 90 | 91 | Just use the "clone or download" button to get a .zip file with all of the library files in it, and unzip that into your "libraries" folder inside your Arduino sketchbook folder. 92 | -------------------------------------------------------------------------------- /Keypad_Matrix.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Keypad_Matrix.h 4 | 5 | Author: Nick Gammon 6 | Date: 20 February 2018 7 | 8 | 9 | PERMISSION TO DISTRIBUTE 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 12 | and associated documentation files (the "Software"), to deal in the Software without restriction, 13 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 15 | subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | 21 | LIMITATION OF LIABILITY 22 | 23 | The software is provided "as is", without warranty of any kind, express or implied, 24 | including but not limited to the warranties of merchantability, fitness for a particular 25 | purpose and noninfringement. In no event shall the authors or copyright holders be liable 26 | for any claim, damages or other liability, whether in an action of contract, 27 | tort or otherwise, arising from, out of or in connection with the software 28 | or the use or other dealings in the software. 29 | 30 | */ 31 | 32 | typedef void (*keyHandler) (const char which); 33 | typedef int (*colHandler) (uint8_t); 34 | typedef void (*rowHandler) (uint8_t); 35 | 36 | #define makeKeymap(x) ((const char*)x) // for passing a multi-dimensional array to the constructor 37 | 38 | class Keypad_Matrix { 39 | const char * keyMap_; // array mapping key numbers to a character (eg. 0,0 is 'A') 40 | const uint8_t * rowPins_; // array of row pin numbers 41 | const uint8_t * colPins_; // array of column pin numbers 42 | uint8_t numRows_; 43 | uint8_t numCols_; 44 | bool enablePullups_; 45 | uint8_t totalKeys_; // numRows_ * numCols_ 46 | char * lastKeySetting_; // bitmap of last setting of each key 47 | unsigned long * lastKeyTime_; // when each key was last changed 48 | unsigned long debounce_Time_; // how long to debounce for 49 | // event handlers (callback functions) 50 | keyHandler keyDownHandler_; // handler for keydown event 51 | keyHandler keyUpHandler_; // handler for keyup event 52 | rowHandler startRowHandler_; // handler called at the start of each row 53 | rowHandler endRowHandler_; // handler called at the end of each row 54 | colHandler readHandler_; // handler for reading a column 55 | 56 | // default handlers for starting and ending a row 57 | static void startRow (uint8_t rowPin); 58 | static void endRow (uint8_t rowPin); 59 | 60 | public: 61 | 62 | // constructor 63 | Keypad_Matrix (const char * keyMap, const uint8_t *rowPins, const uint8_t *colPins, 64 | const uint8_t numRows, const uint8_t numCols, const bool enablePullups = true); 65 | // destructor 66 | ~Keypad_Matrix (); 67 | 68 | // public functions 69 | void begin (); // call to initialize (set pullups, allocate memory) 70 | void scan (); // call periodically to scan the keys 71 | bool isKeyDown (const char which); // see if a certain key is down 72 | void setKeyDownHandler (keyHandler handler) { keyDownHandler_ = handler; } // handle key down 73 | void setKeyUpHandler (keyHandler handler) { keyUpHandler_ = handler; } // handle key up 74 | void setRowHandlers (rowHandler start, rowHandler end) 75 | { 76 | startRowHandler_ = start; 77 | endRowHandler_ = end; 78 | } // row handlers 79 | void setColumnReadHandler (colHandler handler) { readHandler_ = handler; } // read a column 80 | void setDebounceTime (const unsigned long debounce_Time) { debounce_Time_ = debounce_Time; } 81 | }; // end of Keypad_Matrix class 82 | 83 | 84 | // See: http://c-faq.com/misc/bitsets.html 85 | 86 | #define BITMASK(b) (1 << ((b) % CHAR_BIT)) 87 | #define BITSLOT(b) ((b) / CHAR_BIT) 88 | #define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b)) 89 | #define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b)) 90 | #define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b)) 91 | -------------------------------------------------------------------------------- /examples/Message_Generator/Message_Generator.ino: -------------------------------------------------------------------------------- 1 | // Author: Nick Gammon 2 | // Date: 16 March 2018 3 | 4 | #include 5 | #include 6 | 7 | const byte ROWS = 4; 8 | const byte COLS = 4; 9 | 10 | // what is on the keypad keys 11 | char keys[ROWS][COLS] = { 12 | { '1', '2', '3', 'A' }, 13 | { '4', '5', '6', 'B' }, 14 | { '7', '8', '9', 'C' }, 15 | { '*', '0', '#', 'D' }, 16 | }; 17 | 18 | // how much to slow down keyboard output (so keys aren't missed) 19 | const unsigned long DELAY_BETWEEN_KEYS = 10; 20 | 21 | // keypad matrix wiring 22 | byte rowPins[ROWS] = { 2, 3, 4, 5 }; // connect to the row pinouts of the keypad 23 | byte colPins[COLS] = { 6, 7, 8, 9 }; // connect to the column pinouts of the keypad 24 | 25 | // light the LED on key-down 26 | const byte ledPin = 13; 27 | // when we last lit the LED 28 | unsigned long ledLit; 29 | 30 | // helper macros to output strings with "F" macro around them 31 | #define MSG(arg) \ 32 | do \ 33 | myKeyboard.print (F (arg)); \ 34 | while (false) 35 | 36 | #define MSGLN(arg) \ 37 | do \ 38 | myKeyboard.println (F (arg)); \ 39 | while (false) 40 | 41 | // do a newline easily 42 | #define NEWLINE() \ 43 | do \ 44 | myKeyboard.println (); \ 45 | while (false) 46 | 47 | // to avoid compiler warnings about unused arguments 48 | #define UNUSED(expr) (void)(expr) 49 | 50 | // Create the Keypad 51 | Keypad_Matrix kpd = Keypad_Matrix (makeKeymap (keys), rowPins, colPins, ROWS, COLS); 52 | 53 | // class for printing to our keyboard with a delay between each key 54 | class my_Keyboard_class : public Print 55 | { 56 | public: 57 | virtual size_t write (uint8_t c); 58 | }; // end of class my_Keyboard_class 59 | 60 | size_t my_Keyboard_class::write (uint8_t character) 61 | { 62 | Keyboard.write (character); 63 | delay (DELAY_BETWEEN_KEYS); 64 | return 1; 65 | } // end of my_Keyboard_class::write 66 | 67 | my_Keyboard_class myKeyboard; 68 | 69 | void setup () 70 | { 71 | // initialize control over the keyboard: 72 | Keyboard.begin (); 73 | 74 | // set up various pins 75 | pinMode (ledPin, OUTPUT); 76 | 77 | // initialize the keypad handler 78 | kpd.begin (); 79 | kpd.setKeyDownHandler (keyDown); 80 | kpd.setKeyUpHandler (keyUp); 81 | 82 | } // end of setup 83 | 84 | // for things like multiple arrows, eg: specialKey (KEY_LEFT_ARROW, 2); 85 | void specialKey (const uint8_t whichKey, const int n) 86 | { 87 | for (int i = 0; i < n; i++) 88 | { 89 | Keyboard.press (whichKey); 90 | delay (5); 91 | Keyboard.releaseAll (); 92 | delay (5); 93 | } // end of for 94 | } // end of specialKey 95 | 96 | // messages for holding down A and then pressing a number, * or # 97 | void do_A_messages (const char which) 98 | { 99 | switch (which) 100 | { 101 | case '1': MSG ("Sending message for A+1"); break; 102 | case '2': MSG ("Sending message for A+2"); break; 103 | case '3': MSG ("Sending message for A+3"); break; 104 | case '4': MSG ("Sending message for A+4"); break; 105 | case '5': MSG ("Sending message for A+5"); break; 106 | case '6': MSG ("Sending message for A+6"); break; 107 | case '7': MSG ("Sending message for A+7"); break; 108 | case '8': MSG ("Sending message for A+8"); break; 109 | case '9': MSG ("Sending message for A+9"); break; 110 | case '0': MSG ("Sending message for A+0"); break; 111 | case '*': MSG ("Sending message for A+*"); break; 112 | case '#': MSG ("Sending message for A+#"); break; 113 | } // end of switching on which key 114 | } // end of do_A_messages 115 | 116 | // messages for holding down B and then pressing a number, * or # 117 | // Note: Using MSGLN means each one will end with a newline 118 | void do_B_messages (const char which) 119 | { 120 | switch (which) 121 | { 122 | case '1': MSGLN ("Sending message for B+1"); break; 123 | case '2': MSGLN ("Sending message for B+2"); break; 124 | case '3': MSGLN ("Sending message for B+3"); break; 125 | case '4': MSGLN ("Sending message for B+4"); break; 126 | case '5': MSGLN ("Sending message for B+5"); break; 127 | case '6': MSGLN ("Sending message for B+6"); break; 128 | case '7': MSGLN ("Sending message for B+7"); break; 129 | case '8': MSGLN ("Sending message for B+8"); break; 130 | case '9': MSGLN ("Sending message for B+9"); break; 131 | case '0': MSGLN ("Sending message for B+0"); break; 132 | case '*': MSGLN ("Sending message for B+*"); break; 133 | case '#': MSGLN ("Sending message for B+#"); break; 134 | } // end of switching on which key 135 | } // end of do_B_messages 136 | 137 | // messages for holding down C and then pressing a number, * or # 138 | void do_C_messages (const char which) 139 | { 140 | switch (which) 141 | { 142 | case '1': MSG ("Sending message for C+1"); break; 143 | case '2': MSG ("Sending message for C+2"); break; 144 | case '3': MSG ("Sending message for C+3"); break; 145 | case '4': MSG ("Sending message for C+4"); break; 146 | case '5': MSG ("Sending message for C+5"); break; 147 | case '6': MSG ("Sending message for C+6"); break; 148 | case '7': MSG ("Sending message for C+7"); break; 149 | case '8': MSG ("Sending message for C+8"); break; 150 | case '9': MSG ("Sending message for C+9"); break; 151 | case '0': MSG ("Sending message for C+0"); break; 152 | case '*': MSG ("Sending message for C+*"); break; 153 | case '#': MSG ("Sending message for C+#"); break; 154 | } // end of switching on which key 155 | } // end of do_C_messages 156 | 157 | // messages for holding down D and then pressing a number, * or # 158 | void do_D_messages (const char which) 159 | { 160 | switch (which) 161 | { 162 | case '1': MSG ("Sending message for D+1"); break; 163 | case '2': MSG ("Sending message for D+2"); break; 164 | case '3': MSG ("Sending message for D+3"); break; 165 | case '4': MSG ("Sending message for D+4"); break; 166 | case '5': MSG ("Sending message for D+5"); break; 167 | case '6': MSG ("Sending message for D+6"); break; 168 | case '7': MSG ("Sending message for D+7"); break; 169 | case '8': MSG ("Sending message for D+8"); break; 170 | case '9': MSG ("Sending message for D+9"); break; 171 | case '0': MSG ("Sending message for D+0"); break; 172 | case '*': MSG ("Sending message for D+*"); break; 173 | case '#': MSG ("Sending message for D+#"); break; 174 | } // end of switching on which key 175 | 176 | } // end of do_D_messages 177 | 178 | // message for pressing a key with no modifier key 179 | void do_unshifted_messages (const char which) 180 | { 181 | switch (which) 182 | { 183 | case '1': MSG ("Sending message for 1"); break; 184 | case '2': MSG ("Sending message for 2"); break; 185 | case '3': MSG ("Sending message for 3"); break; 186 | case '4': MSG ("Sending message for 4"); break; 187 | case '5': MSG ("Sending message for 5"); break; 188 | case '6': MSG ("Sending message for 6"); break; 189 | case '7': MSG ("Sending message for 7"); break; 190 | case '8': MSG ("Sending message for 8"); break; 191 | case '9': MSG ("Sending message for 9"); break; 192 | case '0': MSG ("Sending message for 0"); break; 193 | case '*': MSG ("Sending message for *"); break; 194 | case '#': MSG ("Sending message for #"); break; 195 | } // end of switching on which key 196 | } // end of do_unshifted_messages 197 | 198 | // do action on key-up 199 | void keyUp (const char which) 200 | { 201 | if (kpd.isKeyDown ('A')) 202 | do_A_messages (which); 203 | else if (kpd.isKeyDown ('B')) 204 | do_B_messages (which); 205 | else if (kpd.isKeyDown ('C')) 206 | do_C_messages (which); 207 | else if (kpd.isKeyDown ('D')) 208 | do_D_messages (which); 209 | else 210 | do_unshifted_messages (which); 211 | } // end of keyUp 212 | 213 | // on key-down turn on the LED 214 | void keyDown (const char which) 215 | { 216 | UNUSED (which); 217 | digitalWrite (ledPin, HIGH); 218 | ledLit = millis (); 219 | } // end of keyDown 220 | 221 | void loop () 222 | { 223 | kpd.scan (); 224 | // turn LED off after 200 ms 225 | if (millis () - ledLit >= 200) 226 | digitalWrite (ledPin, LOW); 227 | } // end of loop 228 | -------------------------------------------------------------------------------- /Keypad_Matrix.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Keypad_Matrix.h 4 | 5 | Author: Nick Gammon 6 | Date: 20 February 2018 7 | 8 | 9 | PERMISSION TO DISTRIBUTE 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 12 | and associated documentation files (the "Software"), to deal in the Software without restriction, 13 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 15 | subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | 21 | LIMITATION OF LIABILITY 22 | 23 | The software is provided "as is", without warranty of any kind, express or implied, 24 | including but not limited to the warranties of merchantability, fitness for a particular 25 | purpose and noninfringement. In no event shall the authors or copyright holders be liable 26 | for any claim, damages or other liability, whether in an action of contract, 27 | tort or otherwise, arising from, out of or in connection with the software 28 | or the use or other dealings in the software. 29 | 30 | -------------------------------- 31 | 32 | Example of use: 33 | 34 | const byte ROWS = 4; 35 | const byte COLS = 4; 36 | 37 | // how the keypad has its keys laid out 38 | const char keys[ROWS][COLS] = { 39 | {'1', '2', '3', 'A'}, 40 | {'4', '5', '6', 'B'}, 41 | {'7', '8', '9', 'C'}, 42 | {'*', '0', '#', 'D'}, 43 | }; 44 | 45 | const byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad 46 | const byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad 47 | 48 | // Create the Keypad (makeKeymap casts the keys array to const char *) 49 | Keypad_Matrix kpd = Keypad_Matrix (makeKeymap (keys), rowPins, colPins, ROWS, COLS ); 50 | 51 | ... 52 | 53 | In setup, call "begin" to allocate memory, and set a keydown and/or a keyup handler: 54 | 55 | kpd.begin (); 56 | kpd.setKeyDownHandler (keyDown); 57 | 58 | ... 59 | 60 | In loop, call "scan" to check the current key status: 61 | 62 | void loop() 63 | { 64 | kpd.scan (); 65 | } 66 | 67 | ... 68 | 69 | The handlers return the keyvalue (from the "keys" array) corresponding to which key just went down or up. The handlers are 70 | called automatically for the appropriate keys during the call to "scan" above. 71 | 72 | 73 | void keyDown (const char which) 74 | { 75 | Serial.print (F("Key down: ")); 76 | Serial.println (which); 77 | } // end of keyDown 78 | 79 | ... 80 | 81 | You can also query if other keys are currently down (eg. for doing something like Ctrl+C) 82 | 83 | if (isKeyDown ('*')) 84 | // do something 85 | 86 | ------ 87 | 88 | The row handlers and read handler are intended to allow you to do your own reading of the columns. For example, 89 | you might use a 74HC165 to shift in 8 bits using SPI. You can omit those for conventional wiring to digital 90 | pins as default handlers are supplied. 91 | 92 | The read handler is called once for each column (passed as an argument the column pin from the colPins array). The default 93 | behaviour is to do a digital read of that column. The read handler should return LOW (pressed) or HIGH (not pressed). 94 | The start row handler is intended to prepare for reading the columns (for example, it might read the bits for each column in one operation). The default behaviour is to set that row to OUTPUT and LOW. 95 | The end row handlers is intended to wrap up after a row. The default behaviour is to set that row back to INPUT mode. 96 | 97 | For external hardware (like shift registers) you may want to pass "false" to enablePullups as that won't be relevant there. 98 | 99 | */ 100 | 101 | #include // for Arduino stuff like data types, pinMode, etc. 102 | #include // for CHAR_BIT 103 | #include // for class definition, etc. 104 | 105 | // constructor 106 | Keypad_Matrix::Keypad_Matrix (const char *keyMap, const uint8_t *rowPins, const uint8_t *colPins, 107 | const uint8_t numRows, const uint8_t numCols, const bool enablePullups) 108 | : keyMap_ (keyMap), rowPins_ (rowPins), colPins_ (colPins), numRows_ (numRows), numCols_ (numCols), enablePullups_ (enablePullups) 109 | 110 | { 111 | totalKeys_ = numRows * numCols; 112 | lastKeySetting_ = NULL; 113 | lastKeyTime_ = NULL; 114 | debounce_Time_ = 10; // milliseconds 115 | // no handlers yet 116 | keyDownHandler_ = NULL; 117 | keyUpHandler_ = NULL; 118 | startRowHandler_ = startRow; 119 | endRowHandler_ = endRow; 120 | // default to doing a digitalRead 121 | readHandler_ = digitalRead; 122 | } // end of Keypad_Matrix::Keypad_Matrix 123 | 124 | Keypad_Matrix::~Keypad_Matrix () 125 | { 126 | if (lastKeySetting_) 127 | free (lastKeySetting_); 128 | if (lastKeyTime_) 129 | free (lastKeyTime_); 130 | 131 | // set each column to back to input 132 | if (enablePullups_) 133 | for (uint8_t i = 0; i < numCols_; i++) 134 | pinMode (colPins_ [i], INPUT); 135 | 136 | } // end Keypad_Matrix::~Keypad_Matrix 137 | 138 | void Keypad_Matrix::begin () 139 | { 140 | // if begin() already called, don't allocate memory again 141 | if (lastKeySetting_ != NULL) 142 | return; 143 | 144 | // allocate one bit per key, rounded up to next byte 145 | lastKeySetting_ = (char *) calloc ((totalKeys_ + CHAR_BIT - 1) / CHAR_BIT, 1); 146 | // allocate an unsigned long for the time the key last changed, for each key 147 | if (lastKeySetting_ != NULL) 148 | lastKeyTime_ = (unsigned long *) calloc (totalKeys_, sizeof (unsigned long)); 149 | 150 | // give up if we couldn't allocate memory for both arrays 151 | if (lastKeyTime_ == NULL) 152 | { 153 | if (lastKeySetting_) 154 | free (lastKeySetting_); 155 | return; 156 | } 157 | 158 | // set each column to input-pullup (optional) 159 | if (enablePullups_) 160 | for (uint8_t i = 0; i < numCols_; i++) 161 | pinMode (colPins_ [i], INPUT_PULLUP); 162 | 163 | } // end of Keypad_Matrix::begin 164 | 165 | void Keypad_Matrix::scan () 166 | { 167 | // if Keypad_Matrix::begin has not been called then memory hasn't been allocated for the arrays 168 | if (lastKeySetting_ == NULL || 169 | startRowHandler_ == NULL || // we need these handlers 170 | endRowHandler_ == NULL || 171 | readHandler_ == NULL) 172 | return; 173 | 174 | uint8_t keyNumber = 0; // current key number 175 | unsigned long now = millis (); // for debouncing 176 | char keyChanged [(totalKeys_ + CHAR_BIT - 1) / CHAR_BIT]; // remember which ones changed 177 | memset (keyChanged, 0, sizeof (keyChanged)); // nothing yet 178 | bool changed = false; // did anything change? Not yet. 179 | 180 | // check each row 181 | for (uint8_t row = 0; row < numRows_; row++) 182 | { 183 | // handle start of a row 184 | // default: set that row to OUTPUT and LOW 185 | startRowHandler_ (rowPins_ [row]); 186 | 187 | // check each column to see if the switch has driven that column LOW 188 | for (uint8_t col = 0; col < numCols_; col++) 189 | { 190 | // debounce - ignore if not enough time has elapsed since last change 191 | if (now - lastKeyTime_ [keyNumber] >= debounce_Time_) 192 | { 193 | bool keyState = readHandler_ (colPins_ [col]) == LOW; // true means pressed 194 | if (keyState != (BITTEST (lastKeySetting_, keyNumber) != 0)) // changed? 195 | { 196 | lastKeyTime_ [keyNumber] = now; // remember time it changed 197 | changed = true; // remember something changed 198 | BITSET (keyChanged, keyNumber); // remember this key changed 199 | 200 | // remember new state 201 | if (keyState) 202 | BITSET (lastKeySetting_, keyNumber); 203 | else 204 | BITCLEAR (lastKeySetting_, keyNumber); 205 | } // if key state has changed 206 | } // debounce time up 207 | keyNumber++; 208 | } // end of for each column 209 | 210 | // handle end of a row 211 | // default: put row back to high-impedance (input) 212 | endRowHandler_ (rowPins_ [row]); 213 | } // end of for each row 214 | 215 | // If anything changed call the handlers. 216 | // We do this now in case the keys aren't polled very frequently. We have now 217 | // detected all the changes (first) before calling any handlers, in case the 218 | // handler wants to know of combinations like Ctrl+Z. 219 | if (changed) 220 | { 221 | // do key-ups first to make sure that combinations handled by external code are correct 222 | for (uint8_t i = 0; i < totalKeys_; i++) 223 | { 224 | if (BITTEST (keyChanged, i) != 0) // did this one change? 225 | { 226 | if (BITTEST (lastKeySetting_, i) == 0) // is now up 227 | if (keyUpHandler_) 228 | keyUpHandler_ (keyMap_ [i]); 229 | } // end of this key changed 230 | } // end of for each key 231 | 232 | // now do key-downs 233 | for (uint8_t i = 0; i < totalKeys_; i++) 234 | { 235 | if (BITTEST (keyChanged, i) != 0) // did this one change? 236 | { 237 | if (BITTEST (lastKeySetting_, i) != 0) // is now down 238 | if (keyDownHandler_) 239 | keyDownHandler_ (keyMap_ [i]); 240 | } // end of this key changed 241 | } // end of for each key 242 | } // end of something changed 243 | 244 | } // end of Keypad_Matrix::scan 245 | 246 | bool Keypad_Matrix::isKeyDown (const char which) 247 | { 248 | // if Keypad_Matrix::begin has not been called then memory hasn't been allocated for the arrays 249 | if (lastKeySetting_ == NULL) 250 | return false; 251 | 252 | // locate the desired key by a linear scan 253 | // - this is a bit inefficient, but for a 16-key keypad it will be pretty fast 254 | for (uint8_t i = 0; i < totalKeys_; i++) 255 | { 256 | if (keyMap_ [i] == which) 257 | return BITTEST (lastKeySetting_, i) != 0; // true if down 258 | } 259 | return false; // that key isn't known 260 | } // end of Keypad_Matrix::isKeyDown 261 | 262 | // default handler for starting a row 263 | void Keypad_Matrix::startRow (uint8_t rowPin) 264 | { 265 | // set that row to OUTPUT and LOW 266 | pinMode (rowPin, OUTPUT); 267 | digitalWrite (rowPin, LOW); 268 | } // end of Keypad_Matrix::startRow 269 | 270 | // default handler for ending a row 271 | void Keypad_Matrix::endRow (uint8_t rowPin) 272 | { 273 | // put row back to high-impedance (input) 274 | pinMode (rowPin, INPUT); 275 | } // end of Keypad_Matrix::startRow 276 | --------------------------------------------------------------------------------