|
@@ -0,0 +1,270 @@
|
|
|
|
+/* demo several 4x4 and 3x4 keyboard scanners
|
|
|
|
+ *
|
|
|
|
+ * on ESP8266, full 8 pins, using gpios 15,13,14,12 2,0,4,5
|
|
|
|
+ *
|
|
|
|
+ * note: on the Wemos, gpio15 has an external pulldown
|
|
|
|
+ * and gpio16 does not have an internal pullup
|
|
|
|
+ * making both awkward to use
|
|
|
|
+ *
|
|
|
|
+ * using the module that has 16 tact buttons, connector at the left, S1 top left, S16 bottom right
|
|
|
|
+ * the pins are bottom to top : row 4..1, col 1..4
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+int rows[] = {5,4,0,2}; // driven
|
|
|
|
+int cols[] = {14,12,13,15}; // input only
|
|
|
|
+
|
|
|
|
+//#define FIVE_PIN 1
|
|
|
|
+//#define SEVEN_PIN 1
|
|
|
|
+#define FOUR_PIN 1
|
|
|
|
+
|
|
|
|
+void setup() {
|
|
|
|
+ delay(1000);
|
|
|
|
+ Serial.begin(115200);
|
|
|
|
+ // set all rows as 0, ready for interrupt sensing
|
|
|
|
+ rowsSet(0);
|
|
|
|
+ // set up all column pins as inputs with pullups
|
|
|
|
+ pinMode(cols[0],INPUT_PULLUP);
|
|
|
|
+ pinMode(cols[1],INPUT_PULLUP);
|
|
|
|
+ pinMode(cols[2],INPUT_PULLUP);
|
|
|
|
+ attachInterrupt(digitalPinToInterrupt(cols[0]), kbd_irq, FALLING);
|
|
|
|
+ attachInterrupt(digitalPinToInterrupt(cols[1]), kbd_irq, FALLING);
|
|
|
|
+ attachInterrupt(digitalPinToInterrupt(cols[2]), kbd_irq, FALLING);
|
|
|
|
+ Serial.println();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 0, set all rows low, anything else is a bit pattern of hi/lo
|
|
|
|
+// simulate open-drain with pullup
|
|
|
|
+void rowsSet(int rowPattern) {
|
|
|
|
+ for (int r=0; r<4; r++) {
|
|
|
|
+ if (rowPattern & 1)
|
|
|
|
+ pinMode(rows[r],INPUT_PULLUP);
|
|
|
|
+ else {
|
|
|
|
+ pinMode(rows[r],OUTPUT);
|
|
|
|
+ digitalWrite(rows[r],0);
|
|
|
|
+ }
|
|
|
|
+ rowPattern >>= 1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// state machine 0=ready, 1=interrupt detected, 2=interrupt handled waiting for release
|
|
|
|
+const int KEY_NONE = 0;
|
|
|
|
+const int KEY_DETECTED = 1;
|
|
|
|
+const int KEY_PROCESSED = 2;
|
|
|
|
+static int key_state;
|
|
|
|
+
|
|
|
|
+ICACHE_RAM_ATTR void kbd_irq(void) {
|
|
|
|
+ // inform the main code
|
|
|
|
+ if (key_state == KEY_NONE)
|
|
|
|
+ key_state = KEY_DETECTED;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static char lut4[] = "123A456B789C*0#D"; // used in 4x4 keypad
|
|
|
|
+static char lut[] = "123456789*0#"; // used with 3x4 kaypad
|
|
|
|
+int char_count;
|
|
|
|
+
|
|
|
|
+// calculator demo
|
|
|
|
+// key=0, ignore key=ascii '0'...'9' data key='*' addition key='#' show total
|
|
|
|
+void calculator(char key) {
|
|
|
|
+ if (key) {
|
|
|
|
+ static int acc = 0, sum=0;
|
|
|
|
+ if (key>='0' && key<='9') {
|
|
|
|
+ acc = acc*10+(key&0x0f);
|
|
|
|
+ }
|
|
|
|
+ else if (key=='*') { // addition
|
|
|
|
+ sum += acc;
|
|
|
|
+ acc = 0;
|
|
|
|
+ }
|
|
|
|
+ else if (key=='#') { // finish and show total
|
|
|
|
+ sum += acc;
|
|
|
|
+ Serial.println(String("total: ")+String(sum));
|
|
|
|
+ sum = acc = 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef SEVEN_PIN
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// check all the column inputs, return a number, 1/2/3=pressed 0=nokey
|
|
|
|
+int readColumns() {
|
|
|
|
+ // return 10 - digitalRead(cols[0]) - 2*digitalRead(cols[1]) - 3*digitalRead(cols[2]) - 4*digitalRead(cols[3]);
|
|
|
|
+ return 6 - digitalRead(cols[0]) - 2*digitalRead(cols[1]) - 3*digitalRead(cols[2]);
|
|
|
|
+}
|
|
|
|
+const int NO_KEYS = 0;
|
|
|
|
+
|
|
|
|
+int rowPattern [] = {~1, ~2, ~4, ~8};
|
|
|
|
+
|
|
|
|
+void loop() {
|
|
|
|
+ // put your main code here, to run repeatedly:
|
|
|
|
+ int row, col;
|
|
|
|
+ switch(key_state) {
|
|
|
|
+ case KEY_NONE:
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_DETECTED:
|
|
|
|
+ // just got an interrupt, scan the keys
|
|
|
|
+ for (row=0; row<4; row++) {
|
|
|
|
+ rowsSet(rowPattern[row]);
|
|
|
|
+ delayMicroseconds(10);
|
|
|
|
+ col = readColumns();
|
|
|
|
+ if (col)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ rowsSet(0);
|
|
|
|
+ key_state = KEY_PROCESSED;
|
|
|
|
+
|
|
|
|
+ // keyboard scan done, the information is in (row,col)
|
|
|
|
+ if (col) {
|
|
|
|
+ Serial.print(String(lut[3*row+col-1])); // <---the key that was pressed
|
|
|
|
+ if (++char_count >= 16)
|
|
|
|
+ char_count=0, Serial.println();
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_PROCESSED:
|
|
|
|
+ // wait for release
|
|
|
|
+ col = readColumns();
|
|
|
|
+ if (col == NO_KEYS)
|
|
|
|
+ key_state = KEY_NONE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NOTE: even though a 7 pin connection allows 2-key rollover, the code example
|
|
|
|
+// above does NOT handles 2 keys pressed at the same time.
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#ifdef FIVE_PIN
|
|
|
|
+// column decode these are octal values, groups of 3 bits
|
|
|
|
+
|
|
|
|
+// check all the column inputs, return the pattern; no key press returns 0b111
|
|
|
|
+int readColumns() {
|
|
|
|
+ return digitalRead(cols[0]) + 2*digitalRead(cols[1]) + 4*digitalRead(cols[2]);
|
|
|
|
+}
|
|
|
|
+const int NO_KEYS = 07;
|
|
|
|
+
|
|
|
|
+int rowPattern [] = {0b11, 0b10, 0b01, 0b00};
|
|
|
|
+
|
|
|
|
+// column sense patterns, as the rowPattern is cycled; these are octal values, 3 bits at a time
|
|
|
|
+int fiveKeyLUT[] = {06666, 05555, 03333, // 123
|
|
|
|
+ 07676, 07575, 07373, // 456
|
|
|
|
+ 07766, 07755, 07733, // 789
|
|
|
|
+ 07666, 07555, 07333}; // *0#
|
|
|
|
+
|
|
|
|
+void loop() {
|
|
|
|
+ // put your main code here, to run repeatedly:
|
|
|
|
+ int row, col, d;
|
|
|
|
+ char key = 0;
|
|
|
|
+ switch(key_state) {
|
|
|
|
+ case KEY_NONE:
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_DETECTED:
|
|
|
|
+ //Serial.println("got key");
|
|
|
|
+ // just got an interrupt, scan the keys
|
|
|
|
+ d = 0;
|
|
|
|
+ for (row=0; row<4; row++) {
|
|
|
|
+ //Serial.println(String("scan row ")+String(row)+" using pattern 0b"+String(rowPattern[row],BIN));
|
|
|
|
+ rowsSet(rowPattern[row]);
|
|
|
|
+ delayMicroseconds(10);
|
|
|
|
+ d = 8*d + readColumns(); // shift left by 3, and read next 3
|
|
|
|
+ //Serial.println(String("and rows read as: 0")+String(d,OCT));
|
|
|
|
+ }
|
|
|
|
+ rowsSet(0);
|
|
|
|
+ key_state = KEY_PROCESSED;
|
|
|
|
+
|
|
|
|
+ // now analyze the column reads
|
|
|
|
+ //Serial.println(String("final d=")+String(d,OCT));
|
|
|
|
+ for (int i=0; i<sizeof(fiveKeyLUT)/sizeof(fiveKeyLUT[0]); i++)
|
|
|
|
+ if (d==fiveKeyLUT[i]) {
|
|
|
|
+ key = lut[i];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (key) {
|
|
|
|
+ Serial.print(key);
|
|
|
|
+ if (++char_count >= 16)
|
|
|
|
+ char_count=0, Serial.println();
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_PROCESSED:
|
|
|
|
+ // wait for release
|
|
|
|
+ col = readColumns();
|
|
|
|
+ if (col==NO_KEYS)
|
|
|
|
+ key_state = KEY_NONE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ calculator(key);
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#ifdef FOUR_PIN
|
|
|
|
+// decode these are hex values
|
|
|
|
+const int fourKeyLUT[] = { 0xaa, 0x55, 0x00, // 123
|
|
|
|
+ 0xee, 0xdd, 0xcc, // 456
|
|
|
|
+ 0xfa, 0xf5, 0xf0, // 789
|
|
|
|
+ 0xea, 0xd5, 0xc0 // *0#
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// check all the column inputs, return a number, 01/10/00=pressed 11=nokey
|
|
|
|
+int readColumns() {
|
|
|
|
+ return digitalRead(cols[0]) + 2*digitalRead(cols[1]);
|
|
|
|
+}
|
|
|
|
+const int NO_KEYS = 0x3;
|
|
|
|
+
|
|
|
|
+int rowPattern [] = {0b11, 0b10, 0b01, 0b00};
|
|
|
|
+
|
|
|
|
+void loop() {
|
|
|
|
+ // put your main code here, to run repeatedly:
|
|
|
|
+ int row, col, d;
|
|
|
|
+ char key=0;
|
|
|
|
+ switch(key_state) {
|
|
|
|
+ case KEY_NONE:
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_DETECTED:
|
|
|
|
+ //Serial.println("got key");
|
|
|
|
+ delay(5); //wait for the bounce to settle
|
|
|
|
+ // just got an interrupt, scan the keys
|
|
|
|
+ d = 0;
|
|
|
|
+ for (row=0; row<4; row++) {
|
|
|
|
+ //Serial.println(String("scan row ")+String(row)+" using pattern 0b"+String(rowPattern[row],BIN));
|
|
|
|
+ rowsSet(rowPattern[row]);
|
|
|
|
+ delayMicroseconds(10);
|
|
|
|
+ d = 4*d + readColumns(); // shift left by 2, and read next 2
|
|
|
|
+ //Serial.println(String("and rows read as: 0x")+String(d,HEX));
|
|
|
|
+ }
|
|
|
|
+ rowsSet(0);
|
|
|
|
+ key_state = KEY_PROCESSED;
|
|
|
|
+
|
|
|
|
+ // now analyze the column reads
|
|
|
|
+ // Serial.println(String("final d=")+String(d,HEX));
|
|
|
|
+ key = 0;
|
|
|
|
+ for (int i=0; i<sizeof(fourKeyLUT)/sizeof(fourKeyLUT[0]); i++)
|
|
|
|
+ if (d==fourKeyLUT[i]) {
|
|
|
|
+ key = lut[i];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (key) {
|
|
|
|
+ Serial.print(key);
|
|
|
|
+ if (++char_count >= 16)
|
|
|
|
+ char_count=0, Serial.println();
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case KEY_PROCESSED:
|
|
|
|
+ // wait for release
|
|
|
|
+ col = readColumns();
|
|
|
|
+ if (col==NO_KEYS)
|
|
|
|
+ key_state = KEY_NONE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ calculator(key);
|
|
|
|
+}
|
|
|
|
+#endif
|