Unstable signals with many key presses on a midi keyboard matrix

Status
Not open for further replies.

floretan

Member
I am building a new version of my teensy-based synth (here's v1: https://www.youtube.com/watch?v=MxDHKpysxYs), and I'm having some issues with keypresses when four or more keys are pressed at the same time.

This time I'm using a teensy 3.6 and the keybed from a Korg Microkey, which includes two 74HC138 multiplexers and a chip select signal to switch between the two. The PCB of the keyboard includes diodes, so ghost notes are not an issue. The 37 keys each have two switches, and I also got velocity-sensitivity working. Everything works fine when I press up to three keys, but when a fourth key is pressed, I get random readings from all pressed keys, which triggers a lot of note on and note off events.

Things I tried:
- Checked that all the used pins are not in use by the audio library (I'm using pins 0-4 as outputs for the chip selector and the three multiplexer inputs, and pins 24-31 as INPUT_PULLUP to read the switches in a matrix row).
- Commenting out the audio library from my project. I thought that could affect timing somehow, but that wasn't the issue.
- Increased the delay that addresses propagation in the multiplexer, that also didn't help.
- Power the keybed from the Vin pin of the teensy instead of using the 3.3V pin. The 74HC138 supports up to 6V, but somehow with that setup I don't get any keypresses detected.

After a good amount of debugging, I checked the voltage of the VIN of the keybed PCB (which is connected to the Teensy 3.3V pin), and I got a reading of 2.6V. Each time a key is pressed, that voltage drops a bit, and crosses 2.5V when four keys are pressed simultaneously.

What I'm now wondering is why the voltage is only 2.6V in the default state (the cables are only around 15cm long) and why the voltage drops with each key pressed? I'm less experienced on the electronics/hardware side, and I'm aware that this is a bit of a complicated setup, any ideas what could be going on or what to try next?


Here is my code as well:

Code:
// Possible key states
typedef enum {
  KEY_IS_UP,
  KEY_IS_DOWN,
  KEY_IS_GOING_UP,    // We increment the timer in this state
  KEY_IS_GOING_DOWN,  // We increment the timer in this state
} state_t;

typedef struct {
  byte midi_note;
  state_t state; // Bit fields
  long t; // Microsecond timestamp
} Key;

const int multiplexAPin = 1;
const int multiplexBPin = 2;
const int multiplexCPin = 3;
const int chipSelect = 0;

int propdelay = 50;

const int inputs[] = {24, 25, 26, 27, 28, 29, 30, 31};

// The interval between the first and second switch while hitting keys with the
// highest velocity.
const float shortestInterval = 2000.0;

// Every time the interval between the first and second switch doubles, lower
// the midi velocity by this much:
const int velocityAttenuation = 20;

// Keep track of state.
Key keys[88];

bool isKeyBlack[] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0};

void setup() {
  // put your setup code here, to run once:
  pinMode(multiplexAPin, OUTPUT);
  pinMode(multiplexBPin, OUTPUT);
  pinMode(multiplexCPin, OUTPUT);
  pinMode(chipSelect, OUTPUT);

  for (int i = 0; i < 8; i++) {
    pinMode(inputs[i], INPUT_PULLUP);
  }

  Serial.begin(9600);

  delay(500);

  // Init keys state
  for ( int key = 0; key < 88; key++) {
    keys[key].midi_note = 21 + key;
    keys[key].t = 0;
    keys[key].state = KEY_IS_UP;
  }

  digitalWrite(multiplexAPin, LOW);
  digitalWrite(multiplexBPin, LOW);
  digitalWrite(multiplexCPin, LOW);
}

void loop() {
  for (int chip = 0; chip <= 1; chip++) {
    digitalWrite(chipSelect, chip);
    // 0
    digitalWrite(multiplexCPin, LOW);
    delayMicroseconds(propdelay);
    readKeys(0 + chip * 8);

    // 1
    digitalWrite(multiplexAPin, HIGH);
    delayMicroseconds(propdelay);
    readKeys(1 + chip * 8);

    //  3
    digitalWrite(multiplexBPin, HIGH);
    delayMicroseconds(propdelay);
    readKeys(3 + chip * 8);

    // 2
    digitalWrite(multiplexAPin, LOW);
    delayMicroseconds(propdelay);
    readKeys(2 + chip * 8);

    // 6
    digitalWrite(multiplexCPin, HIGH);
    delayMicroseconds(propdelay);
    readKeys(6 + chip * 8);

    // 7
    digitalWrite(multiplexAPin, HIGH);
    delayMicroseconds(propdelay);
    readKeys(7 + chip * 8);

    // 5
    digitalWrite(multiplexBPin, LOW);
    delayMicroseconds(propdelay);
    readKeys(5 + chip * 8);

    // 4
    digitalWrite(multiplexAPin, LOW);
    delayMicroseconds(propdelay);
    readKeys(4 + chip * 8);
  }

}

void readKeys(int note_offset) {

  for (int i = 0; i < 4; i++) {

    int key_index = 3 + note_offset * 4 + i;
    bool switchA = !digitalRead(inputs[i * 2]);
    bool switchB = !digitalRead(inputs[i * 2 + 1]);

    if (switchA) {

      if (switchB && keys[key_index].state == KEY_IS_GOING_DOWN) {
        keys[key_index].state = KEY_IS_DOWN;

        unsigned long t = micros() - keys[key_index].t;
        if (t < shortestInterval) {
          t = shortestInterval;
        }

        if (isBlackKey(keys[key_index].midi_note)) {
          // Black keys are shorter, we need to compensate to get an even velocity.
          t = t*2;
        }

        int velocity = 127 - log(t / shortestInterval) / log(2) * velocityAttenuation;

        if (velocity < 0) {
          velocity = 0;
        }

        usbMIDI.sendNoteOn(keys[key_index].midi_note, velocity, 0);
      }

      if (!switchB) {
        if (keys[key_index].state == KEY_IS_UP) {
          keys[key_index].state = KEY_IS_GOING_DOWN;
          keys[key_index].t = micros();
        }
        else if (keys[key_index].state == KEY_IS_DOWN) {
          keys[key_index].state = KEY_IS_GOING_UP;
        }
      }
    }
    else if (keys[key_index].state != KEY_IS_UP) {
      keys[key_index].state = KEY_IS_UP;
      usbMIDI.sendNoteOff(keys[key_index].midi_note, 127, 0);
    }
  }
}

bool isBlackKey(byte midi_note) {
  return isKeyBlack[midi_note % 12];
}
 
Short version:
I was able to fix the problem by pulling one of the keybed connections low by attaching it to an output pin on the teensy rather than ground.

Slighty longer version:
I couldn't find a diagram online. Besides the 74HC138 and the diodes for the matrix, there are also three resistors and a capacitor, all three connecting ground and Vin directly on different parts of the keybed PCB. I'm not sure what their exact purpose is, and one of my next options was to remove them.

I also did some more debugging, the issue seems to be that the ground input of the keybed and the ground of the Teensy had a difference of around 0.7V. The 3.3V signal was actually correct, it was the ground signal that went up when more keys were pressed.

What pointed me in the right direction was that the keybed had two separate connections: the ground itself, and another input which also needs to be pulled down for things to work, but which also connects to the ground plane on the PCB. I'm still not sure of how it works exactly, but it was that part which was affecting the ground signal.

Thank you Paul for the quick response. I was able to fix this myself, but it's amazing to have such great support around the teensy platform.
 
Status
Not open for further replies.
Back
Top