key matrix 56 keys, no diodes on teensy 4.0

Status
Not open for further replies.

lokki

Well-known member
hi there i have a key matrix setup for a midi foot controller i am working on. it consists of 56 buttons (keys) arranged in a 8x7 matrix.

this is my test code:

Code:
/* @file EventSerialKeypad.pde
 || @version 1.0
 || @author Alexander Brevig
 || @contact alexanderbrevig@gmail.com
 ||
 || @description
 || | Demonstrates using the KeypadEvent.
 || #
 */
#include <Keypad.h>

const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
char keys[ROWS][COLS] = {
{'1','2','3','4','5','6','7'},
{'8','9','A','B','C','D','E'},
{'F','G','H','I','J','K','L'},
{'M','N','O','P','Q','R','S'},
{'T','U','V','W','X','Y','Z'},
{'a','b','c','d','e','f','g'},
{'h','i','j','k','l','m','n'},
{'o','p','q','r','s','t','u'},
};
byte rowPins[ROWS] = {2, 3, 4, 5, 6, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
byte ledPin = 13; 

boolean blink = false;
boolean ledPin_state;

void setup(){
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT);              // Sets the digital pin as output.
    digitalWrite(ledPin, HIGH);           // Turn the LED on.
    ledPin_state = digitalRead(ledPin);   // Store initial LED state. HIGH when LED is on.
    keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
}

void loop(){
    char key = keypad.getKey();

    if (key) {
        Serial.println(key);
    }
   /* if (blink){
        digitalWrite(ledPin,!digitalRead(ledPin));    // Change the ledPin from Hi2Lo or Lo2Hi.
        delay(100);
    } */
}

// Taking care of some special events.
void keypadEvent(KeypadEvent key){
    switch (keypad.getState()){
    case PRESSED:
        if (key == 't') {
            digitalWrite(ledPin,!digitalRead(ledPin));
            ledPin_state = digitalRead(ledPin);        // Remember LED state, lit or unlit.
        }
        break;

    case RELEASED:
        if (key == 't') {
            digitalWrite(ledPin,ledPin_state);    // Restore LED state from before it started blinking.
            blink = false;
        }
        break;

    case HOLD:
        if (key == 's') {
            blink = true;    // Blink the LED when holding the * key.
        }
        break;
    }
}

it mostly works. some keys behave strangely though. they report as different keys. first i thought it was a wiring problem, but resoldering did not help. also i realised that since i get a key reported (the false one) the wires have to be connected somehow "right" since with only one or no wire attached there was of course no reading for this switch. i am pretty confident the code is alright, since it works for almost all switches.

things i noticed:

when i put my finger across the switch contacts and "short" them with my finger i sometimes get the correct readings on the non working switches. so i thought, aha bad soldering, but no, that is not it.

so, might it be that my cables are too long for the teensy digital pins? since this is a large foot controller they span for about 1,5m to 2m for some connections. the thing with the finger led me to believe it could be something like that, just gut feeling.

also noteworthy: there are quite some keys that start to report false keys when i put a finger across the switch terminals, they all work correctly when they are not touched though.

or could a bouncy switch be the cause? the faulty switches report either as switch from the same position a row above or as the switch before in that column, so i.e. the key that should read "E" outputs "D".

can there be done something about the cable length thing? external pull up resistors? or is the teensy 4.0 to fast for the key matrix to work correctly, do i need to slow the reading part down?

thanks for any tips!
 
You can debug the hardware by running a sketch that sets one column, and then checks each row input, waiting for a connection, and prints what it sees.
Advance to the next column, check each row input.

Something like this (may not compile, as I just typed it up)

Code:
const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
byte rowPins[ROWS] = {2, 3, 4, 5, 6, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

// set everything up to be limp and inactive
void setup() {
  for (int i = 0; i != ROWS; ++i) {
    pinMode(rowPins[i], INPUT_PULLUP);
  }
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], INPUT_PULLUP);
  }
  Serial.begin();
  startRowCol();
}

int row = 0;
int col = 0;

// look for connections to the current row/column, 
// advancing the state machine to look for the next
// when we do
void loop() {
  if (gotRowCol()) {
    Serial.println("got it");
    col += 1;
    if (col == COLS) {
      row += 1;
      col = 0;
      if (row == ROWS) {
        row = 0;
      }
    }
    startRowCol();
  }
}

// Strobe the appropriate column for what we're looking for
void startRowCol() {
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], (i == col) ? OUTPUT : INPUT_PULLUP);
    if (i == col) {
      digitalWrite(colPins[i], LOW);
    }
  }
  Serial.print("press row ");
  Serial.print(row);
  Serial.print(" col ");
  Serial.println(col);
}

// see whether we got a connection to the row we're looking for
bool gotRowCol() {
  return digitalRead(rowPins[row]) == LOW;
}

If this works fine, then your wiring is fine, and the bug is somewhere in the Keypad library, or your use of it.

Also, the comments of "four rows" and "three columns" are hilarious! This is why you shouldn't comment "what" the code is doing, but you should comment "WHY" it's doing it.
 
You can debug the hardware by running a sketch that sets one column, and then checks each row input, waiting for a connection, and prints what it sees.
Advance to the next column, check each row input.

Something like this (may not compile, as I just typed it up)

Code:
const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
byte rowPins[ROWS] = {2, 3, 4, 5, 6, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

// set everything up to be limp and inactive
void setup() {
  for (int i = 0; i != ROWS; ++i) {
    pinMode(rowPins[i], INPUT_PULLUP);
  }
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], INPUT_PULLUP);
  }
  Serial.begin();
  startRowCol();
}

int row = 0;
int col = 0;

// look for connections to the current row/column, 
// advancing the state machine to look for the next
// when we do
void loop() {
  if (gotRowCol()) {
    Serial.println("got it");
    col += 1;
    if (col == COLS) {
      row += 1;
      col = 0;
      if (row == ROWS) {
        row = 0;
      }
    }
    startRowCol();
  }
}

// Strobe the appropriate column for what we're looking for
void startRowCol() {
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], (i == col) ? OUTPUT : INPUT_PULLUP);
    if (i == col) {
      digitalWrite(colPins[i], LOW);
    }
  }
  Serial.print("press row ");
  Serial.print(row);
  Serial.print(" col ");
  Serial.println(col);
}

// see whether we got a connection to the row we're looking for
bool gotRowCol() {
  return digitalRead(rowPins[row]) == LOW;
}

If this works fine, then your wiring is fine, and the bug is somewhere in the Keypad library, or your use of it.

Also, the comments of "four rows" and "three columns" are hilarious! This is why you shouldn't comment "what" the code is doing, but you should comment "WHY" it's doing it.

thanks for your answer!

the code is actually from the examples library, i just adjusted the necessary stuff hence still 4 and 3 in there. but yeah pretty useless/obvious as is :)

i shortened some of the long wires (as much as possible) and the behaviour is much better! it seems to work reliably now. so my guess with the wires being too long is not that far fetched after all. any way to improve this (other then shortening wires?) external components? buffers on the input side?
 
Long wires matter when you communicate at tens or hundreds of megahertz.
Long wires also matter if they are very long, and VERY thin (like, thinner than 36 gauge) because they may have some resistance that may maybe matter. Maybe. Unlikely.
Long wires may also matter if you have significant capacitance or inductance involved, or if you are picking up significantly strong EMI from some nearby equipment.
None of these seem to apply in your case. 5 feet / 1.5 meters just isn't that much. Unless there are low-ESR high-capacitance capacitors tied to the wires, in which case the wire inductance plus the capacitance makes for a nice resonant circuit, which may cause voltage swings much larger than the main VCC. If this is the case, some 20-300 Ohm resistors in line with the wires will make for nice dampeners.

To make a long story short: I think you're mis-diagnosing what's going wrong here.
Perhaps the keypad itself is problematic?
Perhaps the I/O pins on your teensy are damaged?
Or the maybe the code inside the Keypad library doesn't have sufficient delay between setting the pins on, and actually reading the data. It may be possible that the time to turn around those pin configurations in the CPU, is "similar" to the time it takes to strobe the columns. You could test this by adding a delayMicroseconds(100) in an overridden version of Keypad.

Code:
class MyKeypad : public Keypad {
public:
  MyKeypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) : Keypad(userKeymap, row, col, numRows, numCols) {}
  void pin_write(byte pinNum, boolean level) {
    digitalWrite(pinNum, level);
    delayMicroseconds(50);
  }
};
// now use MyKeypad instead of KeyPad

Also: Did you try the sketch I suggested? Reading each key separately, controlling the matrix directly, will let you diagnose if the problem is with the wiring, or with the chip-or-library. If the sketch I suggest works fine, the problem is with the chip-or-library. If the sketch I suggest doesn't read the keys when you press them, the problem is with wiring-or-keypad.

Also: If you don't have an oscilloscope or a logic analyzer, you're largely debugging blind. I recommend Saleae logic analyzers, they're great and come at a good price. The super-cheap Chinese "pocket oscilloscopes" or "bus pirate" type logic analyzers, may be sufficient for your needs in a pinch, but are not good value for money in my experience. Better buy a low-end Rigol, if you want a scope you can trust to keep using for a while.
 
You can debug the hardware by running a sketch that sets one column, and then checks each row input, waiting for a connection, and prints what it sees.
Advance to the next column, check each row input.

Something like this (may not compile, as I just typed it up)

Code:
const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
byte rowPins[ROWS] = {2, 3, 4, 5, 6, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

// set everything up to be limp and inactive
void setup() {
  for (int i = 0; i != ROWS; ++i) {
    pinMode(rowPins[i], INPUT_PULLUP);
  }
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], INPUT_PULLUP);
  }
  Serial.begin();
  startRowCol();
}

int row = 0;
int col = 0;

// look for connections to the current row/column, 
// advancing the state machine to look for the next
// when we do
void loop() {
  if (gotRowCol()) {
    Serial.println("got it");
    col += 1;
    if (col == COLS) {
      row += 1;
      col = 0;
      if (row == ROWS) {
        row = 0;
      }
    }
    startRowCol();
  }
}

// Strobe the appropriate column for what we're looking for
void startRowCol() {
  for (int i = 0; i != COLS; ++i) {
    pinMode(colPins[i], (i == col) ? OUTPUT : INPUT_PULLUP);
    if (i == col) {
      digitalWrite(colPins[i], LOW);
    }
  }
  Serial.print("press row ");
  Serial.print(row);
  Serial.print(" col ");
  Serial.println(col);
}

// see whether we got a connection to the row we're looking for
bool gotRowCol() {
  return digitalRead(rowPins[row]) == LOW;
}

If this works fine, then your wiring is fine, and the bug is somewhere in the Keypad library, or your use of it.

Also, the comments of "four rows" and "three columns" are hilarious! This is why you shouldn't comment "what" the code is doing, but you should comment "WHY" it's doing it.

coming back to this! thanks for the code. it only needed very little adjustment to make it work (setting a baud rate vor Serial.begin();) just in case you want to change your post to reflect that.

all buttons work fine with your sketch! thanks a lot for that. so it seems to be a problem with the library or my use of it.

what i am aiming for is a midi foot controller that sends note/on and off events (among other things) so i need to know when a button is pressed and when it is released. this is why i used the EventKeypad example as a starting point. since i just adapted the example i suspect the error is in the library (a timing issue as you wrote maybe).
i have an oscilloscope so can test further, but as your code runs fine the wiring seems to be ok. can the chip still be defective if your code runs fine? will try your MyKeypad advice next...

thanks!!
 
ok, the MyKeypad trick did not help, still the same issue. i used it like this, correct?

Code:
/* @file EventSerialKeypad.pde
 || @version 1.0
 || @author Alexander Brevig
 || @contact alexanderbrevig@gmail.com
 ||
 || @description
 || | Demonstrates using the KeypadEvent.
 || #
 */
#include <Keypad.h>

class MyKeypad : public Keypad {
public:
  MyKeypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) : Keypad(userKeymap, row, col, numRows, numCols) {}
  void pin_write(byte pinNum, boolean level) {
    digitalWrite(pinNum, level);
    delayMicroseconds(200);
  }
};
// now use MyKeypad instead of KeyPad

const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
char keys[ROWS][COLS] = {
{'1','2','3','4','5','6','7'},
{'8','9','A','B','C','D','E'},
{'F','G','H','I','J','K','L'},
{'M','N','O','P','Q','R','S'},
{'T','U','V','W','X','Y','Z'},
{'a','b','c','d','e','f','g'},
{'h','i','j','k','l','m','n'},
{'o','p','q','r','s','t','u'},
};
byte rowPins[ROWS] = {2, 3, 4, 5, 6, 9, 10, 11}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

Keypad keypad = MyKeypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
byte ledPin = 13; 

boolean blink = false;
boolean ledPin_state;



void setup(){
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT);              // Sets the digital pin as output.
    digitalWrite(ledPin, HIGH);           // Turn the LED on.
    ledPin_state = digitalRead(ledPin);   // Store initial LED state. HIGH when LED is on.
    keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
}

void loop(){
    char key = keypad.getKey();

    if (key) {
        Serial.println(key);
    }
   /* if (blink){
        digitalWrite(ledPin,!digitalRead(ledPin));    // Change the ledPin from Hi2Lo or Lo2Hi.
        delay(100);
    } */
}

// Taking care of some special events.
void keypadEvent(KeypadEvent key){
    switch (keypad.getState()){
    case PRESSED:
        if (key == 't') {
            digitalWrite(ledPin,!digitalRead(ledPin));
            ledPin_state = digitalRead(ledPin);        // Remember LED state, lit or unlit.
        }
        break;

    case RELEASED:
        if (key == 't') {
            digitalWrite(ledPin,ledPin_state);    // Restore LED state from before it started blinking.
            blink = false;
        }
        break;

    case HOLD:
        if (key == 's') {
            blink = true;    // Blink the LED when holding the * key.
        }
        break;
    }
}
 
hi there, further testing. and it gets more fishy...

if i run this code:

Code:
/* @file MultiKey.ino
|| @version 1.0
|| @author Mark Stanley
|| @contact mstanley@technologist.com
||
|| @description
|| | The latest version, 3.0, of the keypad library supports up to 10
|| | active keys all being pressed at the same time. This sketch is an
|| | example of how you can get multiple key presses from a keypad or
|| | keyboard.
|| #
*/

#include <Keypad.h>

const byte ROWS = 8; //four rows
const byte COLS = 7; //three columns
char keys[ROWS][COLS] = {
{'1','2','3','4','5','6','7'},
{'8','9','A','B','C','D','E'},
{'F','G','H','I','J','K','L'},
{'M','N','O','P','Q','R','S'},
{'T','U','V','W','X','Y','Z'},
{'a','b','c','d','e','f','g'},
{'h','i','j','k','l','m','n'},
{'o','p','q','r','s','t','u'},

};
byte rowPins[ROWS] = {11, 10, 9, 6, 5, 4, 3, 2}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {27, 29, 31, 33, 28, 30, 12 }; //connect to the column pinouts of the kpd

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

unsigned long loopCount;
unsigned long startTime;
String msg;


void setup() {
    Serial.begin(9600);
    loopCount = 0;
    startTime = millis();
    msg = "";
}


void loop() {
    loopCount++;
    if ( (millis()-startTime)>5000 ) {
        Serial.print("Average loops per second = ");
        Serial.println(loopCount/5);
        startTime = millis();
        loopCount = 0;
    }

    // Fills kpd.key[ ] array with up-to 10 active keys.
    // Returns true if there are ANY active keys.
    if (kpd.getKeys())
    {
        for (int i=0; i<LIST_MAX; i++)   // Scan the whole key list.
        {
            if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
            {
                switch (kpd.key[i].kstate) {  // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
                    case PRESSED:
                    msg = " PRESSED.";
                break;
                    case HOLD:
                    msg = " HOLD.";
                break;
                    case RELEASED:
                    msg = " RELEASED.";
                break;
                    case IDLE:
                    msg = " IDLE.";
                }
                Serial.print("Key ");
                Serial.print(kpd.key[i].kchar);
                Serial.println(msg);
            }
        }
    }
    
}  // End loop

which is the multikey example adapted to my wiring situation, some switches once pressed report 3 switches being pressed held and released e.g.
Code:
Key 6 PRESSED.
Key 7 PRESSED.
Key E PRESSED.
Key 6 HOLD.
Key 7 HOLD.
Key E HOLD.
Key 6 RELEASED.
Key 7 RELEASED.
Key E RELEASED.
Key 6 IDLE.
Key 7 IDLE.
Key E IDLE.

if i change the pin order in the row array, different keys will act funky, so the order the teensy reads or writes the pins seems to change the problem
 
ok, now we are getting somewhere :)

i am at my bench, and the oscilloscope is connected to the keypad row 1 (which has some problematic keys). the crocodile clamp goes to ground. whenever the oscilloscope is attached to row 1 everything works! as soon as i remove the clamp (but still have ground connected to the oscilloscope) the keys go flaky/wonky again. if i also remove the ground connection, the situation worsens even further. (more keys go wonky) this made me realise a few things:

-apparently it is not a good idea to program a keypad on a macbook laptop with a magnetic power adapter (since the ground connection is not really good)
-it is also not a good idea to program on a laptop running on battery...
-somehow the probe on row 1 fixes all my problems...

so, how do i emulate a probe being connected to row 1?

thanks for any insights and for the tips so far.
 
@lokki, have you tried slowing your loop down?

I use keypad on a mac with a mag cable and on battery with no issues
 
yes i put a delayMicroseconds(200) at the end of the loop, it made no difference. still false button triggering...
 
hmm, no that does not help too much. i put up to a delayMicroseconds(8000); in there and it still triggers wrongly on some keys. there is a slight improvement though (some keys that were firing 3 different key events only fire 2) i am still puzzled why it works with an oscilloscope attached to row1... maybe i try putting some small capacitor from row1 to ground? EDIT: yep a 22pf from Row1 to Ground absolutely cured all problems... still puzzled.

EDIT2: pressing two keys simultaneously still gives some false triggers while it worked perfectly with the oscilloscope. so i am going to try different capacitor values and maybe some large resistor as well? (i.e. 10mohm)
 
Hi lokki
Good you got a fix. I'm no expert but looks like there is an influence from the oscilloscope impedance and your long cables as you've found with the cap but I can't say that I understand that.

I had a quick look at the keypad library - it has a default debounce time of 10 millis. It looks like it just returns without doing anything if you query it any quicker than the debounce time. Don't know how sensitive you are t0 timing but you can set a different time with setDebounceTime(x), 1 milli is as low as you can go.
 
thanks for the library check, could have done that myself actually. from looking at the library the debounce is really the most simple approach, just only scan every x milliseconds. this is of course far from ideal for time critical input (tapping a tempo for example). i have a debounce implementation for single buttons that has no onset latency but i am not sure it is worth the trouble to implement it for the keypad. (also not sure about the best approach) so far the keypad works with a setDebounceTime(1); without any double triggering. i added a 1mohm resistor in parallel with the 22pf and now two simultaneously pressed buttons also work. (i got those values by looking at the spec of my oscilloscope). thanks for your help @houtson and @jwatte
 
If this is a time-critical event, then your friend is going to be assembly code.

However, I would be hesitant to rely on any library routine to do the debounce without careful scrutinization of the code and knowing how often it is called.

Typically, what I do is scan each row into a structure or an array. I also keep one historical copy of that same array (the previous scan). If something changes I tase that switch and look for two consecutive instances of that switch that are the same state before accepting that change as either a valid keypress or valid release of the key.

The logic is pretty simple. You just need to find the fastest way to implement it and I would not blindly accept that a library routine is your best resource.

You might find this link useful. https://www.allaboutcircuits.com/technical-articles/switch-bounce-how-to-deal-with-it/

Notice the oscilloscope trace in the link. The settling time for the switch is going to be the lower limit for your debounce time.

As an aside, steering diodes are useful when you need to have multiple keys pressed at the same time to prevent ghost keys from appearing. Probably not an issue with something like a foot pedal, but you understand your application better than I.
 
thanks @Loren42 for your answer, and you are right maybe it is better to write my own code for the scanning and have full control over it.

my debounce implementation for any "musical/midi" switches has always been to take the first LOW read as the button press (assuming pull-up input and switch connection to ground) and then to set a debounce time in which any digital HIGH is disregarded. only after the debounce time a digital HIGH is read as a button release. this approach has zero latency on onset and therefore feels very fast. button release is not so time critical in most musical scenarios.

i will read up on matrix button reading implementation now :)
 
-apparently it is not a good idea to program a keypad on a macbook laptop with a magnetic power adapter (since the ground connection is not really good)
-it is also not a good idea to program on a laptop running on battery...

These don't affect what the Teensy sees at all. Well, theoretically, if your keyboard relied on the MacBook ground, and the Teensy relied on the USB connector to see MacBook ground, perhaps it would but ... that would be terrible design, and in that case, would immediately stop working when you unplug the MacBook.
I doubt that has much to contribute to the actual problem. It may have something to contribute to "provoking" the problem, though -- if the problem is something like a too-weak pull-up/down, or a really marginal ground connection, or something like that.

But if the "raw bit banging" sketch works reliably, then the problem really is with the software.
 
These don't affect what the Teensy sees at all. Well, theoretically, if your keyboard relied on the MacBook ground, and the Teensy relied on the USB connector to see MacBook ground, perhaps it would but ... that would be terrible design, and in that case, would immediately stop working when you unplug the MacBook.
I doubt that has much to contribute to the actual problem. It may have something to contribute to "provoking" the problem, though -- if the problem is something like a too-weak pull-up/down, or a really marginal ground connection, or something like that.

But if the "raw bit banging" sketch works reliably, then the problem really is with the software.

yeah, your sketch works perfectly. i am scratching my head though. since i program the teensy from my macbook, i have no external power connected to it, so my macbook serves as the only ground connection and it has to go thru usb to the macbook at the moment so i am not completely sure what you mean with your statement. (this will change when i actually use the foot pedal, since the pedal will then be powered by a wall-wart or by another usb device (with host port).

however both the power connector and the ground of the oscilloscope as well as the probe make a huge difference in the way the sketch behaves. simulating the probe with an 1mohm resistor and a 22pf in parallel also "fixes" the problems with he keypad library. but i will look into programming my own matrix-scanning now, and see if it behaves any better...
 
ok. i rerun your testcode again with the probe ground connected to teensy ground and it does not quite work. whenever i disconnect/reconnect the oscilloscope ground it sees a couple of keys low and reports them as done.

i measured my wiring with a multimeter and i think it is alright. what would be your suggestion to connect the oscilloscope to for further tests?

i might also try to connect the key-matrix to an arduino i have and see if it works there. i remember reading that the internal pull ups of teensy 4 are not really useable in some situations. thanks for all your time and help so far
 
my macbook serves as the only ground connection

I think you mis-understand what "ground" means.
Ground, in small-signal electronics, is simply the signal designated as a potential of 0 Volts compared to the other parts of the circuit.

"Protective Earth," which is something you will found in grounded electrical outlets, and industrial installations, is something totally different. The point there is that there's a separate return path for current, should something go wrong in wiring and components, and ideally that return path is low impedance so enough high current is drawn so the circuit breaker is tripped, OR the ground fault is detected by a ground fault circuit interrupter circuit.

"Ground" is the "GND" point on the Teensy board. You should always hook your oscilloscope clip to that pin, or to something that's well connected to that pin.

Assuming all the connections are tight, and well made, with no corrosion or frayed wiring, then there may be a problem with the Teensy itself. Maybe it's accidentally seen some over-voltage on some pins at some point? Because of the way that the protective diodes on the pins are wired up, over-volting one pin may spread to many other pins, too, or even the main controller, if you're un-lucky.

Other than that, a circuit diagram and photograph would be the next step in trying to figure out what might or might not work.
 
ok, some soldering time later. and i am out of ideas.

first of all let me "apologise" @jwatte, of course i used the term ground wrongly. this comes in part from my guitar and bass fx soldering career (and active musician career as well) where a problem with ground usually means that not all stomp boxes have proper ground connection as in connection to earth and therefore hum or other noise is introduced into the audio path. that is also where i discovered the MacBook is not well "grounded" since audio devices that were powered with the MacBook usb, always had these noise issues.

so here is what i did: i rechecked the wiring with a multimeter -> all good.

i disconnected all wires from the teensy and attached the whole key matrix to an arduino uno and ran this sketch there:

Code:
// Keyboard Matrix Tutorial Example
// baldengineer.com
// CC BY-SA 4.0
 
// JP1 is an input
byte rows[] = {2,3,4, 5, 6, 9, 10, 11};
const int rowCount = 8;

// JP2 and JP3 are outputs
byte cols[] = {27, 29, 31, 33, 28, 30, 32};
const int colCount = 7;

byte keys[colCount][rowCount];

void setup() {
  Serial.begin(9600);

  for(int x=0; x<rowCount; x++) {
    Serial.print(rows[x]); Serial.println(" as input");
    pinMode(rows[x], INPUT);
  }

  for (int x=0; x<colCount; x++) {
    Serial.print(cols[x]); Serial.println(" as input-pullup");
    pinMode(cols[x], INPUT_PULLUP);
  }
    
}

void readMatrix() {
  // iterate the columns
  for (int colIndex=0; colIndex < colCount; colIndex++) {
    // col: set to output to low
    byte curCol = cols[colIndex];
    pinMode(curCol, OUTPUT);
    digitalWrite(curCol, LOW);

    // row: interate through the rows
    for (int rowIndex=0; rowIndex < rowCount; rowIndex++) {
      byte rowCol = rows[rowIndex];
      pinMode(rowCol, INPUT_PULLUP);
      keys[colIndex][rowIndex] = digitalRead(rowCol);
      pinMode(rowCol, INPUT);
      delayMicroseconds(200);
    }
    // disable the column
  //  digitalWrite(curCol, HIGH);
    pinMode(curCol, INPUT_PULLUP);
   // delayMicroseconds(200);
  }
}

void printMatrix() {
  for (int rowIndex=0; rowIndex < rowCount; rowIndex++) {
    if (rowIndex < 10)
      Serial.print(F("0"));
    Serial.print(rowIndex); Serial.print(F(": "));

    for (int colIndex=0; colIndex < colCount; colIndex++) { 
      Serial.print(keys[colIndex][rowIndex]);
      if (colIndex < colCount)
        Serial.print(F(", "));
    } 
    Serial.println("");
  }
  Serial.println("");
}

void loop() {
  readMatrix();
  if (Serial.read()=='!')
    printMatrix();
}

(note that in the example above i already changed the pin-numbering back to teensy 4.0 pins)

this sketch worked without any issues on the uno, any one or two keys i pressed where successfully detected.

so, the teensy must be broken i thought.

took a new teensy 4.0 out of the bag, soldered the 15 wires to it again run the above sketch and bam, it does not work!
i see two keys reported when i only press down one, sometimes i see 6 or 7 keys reported when i press one.

so i switched out the teensy, tried with an arduino uno and that worked, took a fresh teensy and it does not work again!
my wiring must be good, otherwise it would not work with the arduino. so there must be something wrong with the code
(that does not apply on arduino uno) or teensy firmware pin handling (initialization) or the pull ups are indeed too weak to make such a "large" key matrix work.
i think i will have to switch to another micro-controller in that case, do the "older " teensies have stronger pull-ups? external ones can't be used, since i have to switch the pinMode in the code.

EDIT: so with some tweaking of the delayMicroseconds(); value and removing the
Code:
 pinMode(rowCol, INPUT);
i got it working for now. let's see if it stays working :)
 
Last edited:
Status
Not open for further replies.
Back
Top