Kaynağa Gözat

added sample code

Pat Beirne 1 ay önce
ebeveyn
işleme
2cbcb23165
2 değiştirilmiş dosya ile 271 ekleme ve 1 silme
  1. 1 1
      keypad_to_microcontroller.md
  2. 270 0
      keypad_tutorial.ino

+ 1 - 1
keypad_to_microcontroller.md

@@ -4,7 +4,7 @@ There's a dozen ways to connect a keyboard to a microcontroller, and this paper
 There are similar articles on the internet [[see below](#external-references)], 
 but this paper introduces some new techniques: [row-grounded](#3x4-plus-ground-new-design), 
 [row-grounded-with-diodes](#3x4-plus-ground-and-2-diodes-new-design), 
-[row-grounded-with-4-diodes](#3x4-plus-ground-and-4-diodes-new-design)
+[row-grounded-with-4-diodes](#3x4-plus-ground-and-4-diodes-new-design) and the [sample code](keypad_tutorial.ino)
 
 Some articles focus on a full-function, unambiguous keyboard, 
 where multiple key-presses can be detected. Something

+ 270 - 0
keypad_tutorial.ino

@@ -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