Problem with second shift register 74hc595

I'm making an old keyboard with no board or speaker into a midi interface, but I have encountered a problem with it.

All keys in the button matrix work correctly for the most part except that when a key from column 8 is active column 9 gets disabled , and both columns 10 and 11 work correctly. Column 9 does work when 8 is not active

I'm using an Arduino Pro Micro so that I can use its native HID function.
The only clue i have is that the problem occurs on the last data pin in the first shift register and the first pin on the second.

Code:

#include "MIDIUSB.h"
#define NUM_ROWS 6
#define NUM_COLS 11

// Pin Definitions

// Row input pins
const int row1Pin = 2;
const int row2Pin = 3;
const int row3Pin = 4;
const int row4Pin = 5;
const int row5Pin = 6;
const int row6Pin = 7;

// 74HC595 pins
const int dataPin = 8;
const int latchPin = 9;
const int clockPin = 10;

boolean keyPressed[NUM_ROWS][NUM_COLS];
uint8_t keyToMidiMap[NUM_ROWS][NUM_COLS];

// bitmasks for scanning columns
int bits[] =
{ 
  B00000001,
  B00000010,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B01000000,
  B10000000
};

void setup()
{
  int note = 36; //left most key on keyboard

  for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      keyPressed[rowCtr][colCtr] = false;
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }

  // setup pins output/input mode
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  pinMode(row1Pin, INPUT);
  pinMode(row2Pin, INPUT);
  pinMode(row3Pin, INPUT);
  pinMode(row4Pin, INPUT);
  pinMode(row5Pin, INPUT);
  pinMode(row6Pin, INPUT);

}

void loop()
{
  for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    //scan next column
    scanColumn(colCtr);

    //get row values at this column
    int rowValue[NUM_ROWS];
    rowValue[0] = digitalRead(row1Pin);
    rowValue[1] = digitalRead(row2Pin);
    rowValue[2] = digitalRead(row3Pin);
    rowValue[3] = digitalRead(row4Pin);
    rowValue[4] = digitalRead(row5Pin);
    rowValue[5] = digitalRead(row6Pin);

    // process keys pressed
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = true;
        noteOn(rowCtr,colCtr);
      }
    }

    // process keys released
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = false;
        noteOff(rowCtr,colCtr);
      }
    }
  }
}

void scanColumn(int colNum)
{
  digitalWrite(latchPin, LOW);

  if(0 <= colNum && colNum <= 7)
  {
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
  }
  else
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //left sr
  }
  digitalWrite(latchPin, HIGH);
}

void noteOn(int row, int col)
{
  midiEventPacket_t noteOn = {0x09, 0x90 | 1, keyToMidiMap[row][col], 127};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}
void noteOff(int row, int col)
{
  midiEventPacket_t noteOff = {0x08, 0x80 | 1, keyToMidiMap[row][col], 127};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

Circuit / Example Output:

Welcome to the forum.

Thank you for providing all the information and the pictures and using code-tags around your sketch :smiley:
Are you only testing it in a simulation ? That's okay, Tinkercad is pretty accurate, but it does not simulate the bouncing of the buttons.
How do you run your sketch in Tinkercad without Pro Micro board ?
Do you also have the circuit in real hardware ? May we see a photo of it ?

We like to know exactly which sketch runs on what and what the result is.

Would it be possible to make a schematic ?
Could you at least provide a picture of the full Tinkercad circuit ?

You could put the row pins into a array:

const int rowPins[NUM_ROWS] = { 2, 3, 4, 5, 6, 7 };

Did you know that the 'C' and 'C++' language can define a bit value with 0b00000001 and that Arduino added those B00000001 for no reason ?

You don't need the int bits[] array, you can make that bit pattern in code. But that's no problem, keep the array if you want.

Could you share a link to your Tinkercad project ? A few of us use Tinkercad and might want to test it. There is a option somewhere in Tinkercad to make a public link, but I can never find it.

There is no output to the Serial Monitor.
:point_right: You have to print (many) messages to the Serial Monitor to understand what is going on.

Tinkercad has the option to debug a sketch. It takes some time to understand it, and I don't like it, but you can stop somewhere and see the values of the variables.

Right, thanks for the quick response.
So first, I've never simulated the project I found the original by Evan Kale and only built physical prototypes for it. I got a Tinkercad account so i could make the schematics for the forums.

The original project was made with an UNO and had a MIDI Jack. I thought i could just replace it with the pro micro and use the MIDIUSB library and change the output. The original had something like this as the output code:

void noteOn(int row, int col)
{
  Serial.write(0x90); //0x80 = midi note off code
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(127);  //note velocity so midi serial should read 0x90,(key),127 to send command press key
}

void noteOff(int row, int col)
{
  Serial.write(0x80); //0x80 = midi note off code
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(127);  //Serial should read 0x80,(key),127 to send command release key
}

But I can't get the simulation to work like the physical project.

Here is the link to the TinkerCAD Project:

And a dropbox link where I will also upload pictures and the code of the prototype and the board later today:

Theres also a Proteus schematic in the dropbox

Missing 0.1µF bypass capacitor across VCC and GND of each IC.

1 Like

Can you give links to the original project ?

You have to test the keys first. That means to make a test-sketch that scans the keys and shows them to the serial monitor.

I tried that in Tinkercad. It turned out that the diodes are misaligned. You have to re-attach the diodes (move them away a little and then move them back into place).
The simplified sketch:

#define NUM_ROWS 6
#define NUM_COLS 11

// Row input pins
const int rowPin[NUM_ROWS] = { 2, 3, 4, 5, 6, 7};

// 74HC595 pins
const int dataPin = 8;
const int latchPin = 9;
const int clockPin = 10;

bool key[NUM_ROWS][NUM_COLS];

void setup()
{
  Serial.begin( 9600);
  while( !Serial);     // wait for Serial Monitor to open for Leonardo
  Serial.println( "The sketch has started");
  
  for(int row = 0; row < NUM_ROWS; row++)
  {
    for(int col = 0; col < NUM_COLS; col++)
    {
      key[row][col] = false;
    }
  }

  // setup pins output/input mode
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  for( auto a:rowPin)
    pinMode(a, INPUT);
}

void loop()
{
  for (int col=0; col<NUM_COLS; col++)
  {
    //scan next column
    selectColumn(col);

    // process keys
    for(int row=0; row<NUM_ROWS; row++)
    {
      int rowValue = digitalRead( rowPin[row]);
      
      if(rowValue == HIGH && !key[row][col])
      {
        key[row][col] = true;
        noteOn(row,col);
      }
      else if(rowValue == LOW && key[row][col])
      {
        key[row][col] = false;
        noteOff(row,col);
      }
    }
  }
}

void selectColumn(int column)
{
  digitalWrite(latchPin, LOW);

  uint16_t pattern = 0x01 << column;
  shiftOut(dataPin, clockPin, MSBFIRST, highByte( pattern)); //right sr
  shiftOut(dataPin, clockPin, MSBFIRST, lowByte( pattern)); //left sr

  digitalWrite(latchPin, HIGH);
}

void noteOn(int r, int c)
{
  Serial.print( r);
  Serial.print( ", ");
  Serial.print( c);
  Serial.println( " On");
}

void noteOff(int r, int c)
{
  Serial.print( r);
  Serial.print( ", ");
  Serial.print( c);
  Serial.println( " Off");
}

To check if column 9 is disabled if column 8 is active, can you tell which row is which ? Do you start counting at 0 or 1 ? If you can draw that in the picture, then I can shortcut a button of column 8 in Tinkercad and see if I can push a button of column 9.

In Tinkercad, I tried to add shortcuts to many buttons, and then pressed the other buttons. Everything works, so the scanning with my simplified sketch works.

Can you try my simplified sketch and find the problem in your hardware ?
You can slow down the column selection and test it with a multimeter.

1 Like

Wow I didn’t know it simulates solderless bread board exactly how it is.

I would suggest that your keyboard is faulty or in some way not behaving like you think it should.

Thank you both for the help.
dlloyd was correct the main problem was fixed by soldering in the 2 capacitors.

A second problem cropped up with some buttons in the matrix rapid firing when certain combinations were pressed.
example if button from row 1 column 1 was pressed with the button from row 2 column2 the button from row 1 column 2 would just go nonstop.

I had originally thought it was a resistor or a diode in the button matrix, but thanks to Koepels simplified sketch it was fixed. I did have to swap the first nested for loop and add an array to map the keys to buttons.

The link to the original instructable i followed the video is private for some reason, but the images and are still live:

Again thanks both of you.

Yes a matrix will produce that effect, it is producing the key down notification each time the matrix is scanned. If you stop it by stoping the matrix scan when ever you detect a key held down you can only play one note at a time, so no chords.

The proper fix involves making a list of what keys are being held down and only producing a notification when what keys are held down changes. That way you can hold as many keys down as you have room for in the list. It is not trivial to code but is not too complex either.