LED/Button matrix issues

I’m using 8-bit decoder 74HC238. Its outputs are connected to 8 ULN2803 inputs. Using 32 LEDs and 48 buttons. LED and button matrix are sharing the same columns. Button inputs are connected to 4021 shift register. I’m trying to light up all the LEDs and read buttons simultaneously but I can’t really get it to work like it should.

void loop() {

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

nextColumn(i);
readButtons(i);


}


void nextColumn(int column) {

        switch (column) {

        case 0:
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, LOW);
        break;

        case 1:
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, HIGH);
        break;

        case 2:
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, LOW);
        break;

        case 3:
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, HIGH);
        break;

        case 4:
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, LOW);
        break;

        case 5:
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, LOW);
        digitalWrite(SHARED_PIN_1, HIGH);
        break;

        case 6:
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, LOW);
        break;

        case 7:
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, HIGH);
        digitalWrite(SHARED_PIN_1, HIGH);
        break;


 }
}


void readButtons(int column) {

    latchPin();

    for (int i=7; i>=0; i--)  {


        int buttonNumber = i*NUMBER_OF_COLUMNS+column;

        digitalWrite(SR_CLOCK_PIN, 0);
        delayMicroseconds(0.2);

        boolean temp = digitalRead(SR_DATA_PIN);

        //button isn't pressed (pull up resistors cause reading to be 1)
        if (temp)  {


                    //send note off for regular press if the note on has been sent
                    if (midiNoteOnSent[buttonNumber] == true) {

                    //generateNote(i, column, MIDI_NOTE_OFF_CHANNEL);
                    #ifdef DEBUG
                    sendMIDImessage(MIDI_NOTE_OFF_CHANNEL, buttonNumber, MIDI_NOTE_VELOCITY);
                    #else
                    buttonDebugInfo(buttonNumber, MIDI_NOTE_OFF_CHANNEL);
                    #endif

                    midiNoteOnSent[buttonNumber] = false;

                    //if the midiLongNoteOn has also been sent send note off for that event as well
                    if (midiLongNoteOnSent[buttonNumber] == true) {

                        //send MIDI note off
                        //generateNote(j, i, MIDI_NOTE_OFF_LONG_PRESS_CHANNEL);

                        //make sure the notes are sent only once while button is released
                        midiLongNoteOnSent[buttonNumber] = false;
                    }

                }
                  debounceTimer[buttonNumber] = millis();
        }



    else {

        //button press detected, check if is it a valid reading
        if ((millis() - debounceTimer[buttonNumber]) > BUTTON_DEBOUNCE_DELAY) {

                //button is really pressed

                //if the note on hasn't been sent already, send it
                if (midiNoteOnSent[buttonNumber] == false) {

                //send MIDI note on
                //generateNote(i, column, MIDI_NOTE_ON_CHANNEL);
                #ifdef DEBUG
                sendMIDImessage(MIDI_NOTE_ON_CHANNEL, buttonNumber, MIDI_NOTE_VELOCITY);
                #else
                buttonDebugInfo(buttonNumber, MIDI_NOTE_ON_CHANNEL);
                #endif

                //make sure the note is sent only once while button is pressed
                midiNoteOnSent[buttonNumber] = true;

                //start measuring time needed to send long-press midi note
                longPressButtonTimer[buttonNumber] = millis();

        }

        //if it's been more than LONG_PRESS_BUTTON_TIMER, send midiLongNoteOn
        else if (((millis() - longPressButtonTimer[buttonNumber]) > LONG_PRESS_BUTTON_TIMER) && midiLongNoteOnSent[buttonNumber] == false)    {

                //send long-press MIDI note on
                //generateNote(j, i, MIDI_NOTE_ON_LONG_PRESS_CHANNEL);
                //make sure the note is sent only once while button is pressed
                midiLongNoteOnSent[buttonNumber] = true;

            }
        }
    }


        digitalWrite(SR_CLOCK_PIN, 1);

  }
 }

void latchPin() {

    //pulse the latch pin; set it to 1 to collect parallel data
    digitalWrite(SR_LATCH_PIN, 1);

    //wait
    delayMicroseconds(20);

    //set it to 0 to transmit data serially
    digitalWrite(SR_LATCH_PIN, 0);
}


void buttonDebugInfo(int buttonNumber, int channel) {

        Serial.print("Button: ");
        Serial.print(buttonNumber);
        Serial.print("\n");

}

So what happens when I press a button is this (using debug mode):

Button: 27
Button: 29
Button: 31
Button: 25
Button: 27
Button: 29
Button: 31
Button: 25
Button: 27
Button: 29
Button: 31
Button: 25
Button: 27
Button: 25
Button: 27
Button: 31
Button: 27
Button: 29

        delayMicroseconds(0.2);

What do you think this is doing? Have you bothered to look at the documentation for the function, to determine what kind of value it takes?

        boolean temp = digitalRead(SR_DATA_PIN);

The digitalRead() function does not return a boolean.

You are doing far more in the readButtons() function than reading the switches. Your function name should reflect that.

PaulS:

        delayMicroseconds(0.2);

What do you think this is doing? Have you bothered to look at the documentation for the function, to determine what kind of value it takes?

To be honest I haven't, I've copied it from ShiftIn tutorial. Fixed that, thanks.

        boolean temp = digitalRead(SR_DATA_PIN);

The digitalRead() function does not return a boolean.

Yes I know that but it works.

What about my third point? I suspect that the reason that LED/Button matrix is slow has to do with what you are doing when reading the switches than from the act of reading the switches/writing to the LEDs.

PaulS:
What about my third point? I suspect that the reason that LED/Button matrix is slow has to do with what you are doing when reading the switches than from the act of reading the switches/writing to the LEDs.

What are you suggesting then?

What are you suggesting then?

I'm suggesting that as a first step, readButtons() should do exactly that and nothing more. Get rid of all the calls to the MIDI functions.

Okay - this is the new readButtons function

readButtons(int column) {


    latchPin();


    //register sends the information about the pins from pin 7 to pin 0, hence the countdown
    for (int i=7; i>=0; i--)  {


        int buttonNumber = i*NUMBER_OF_COLUMNS+column;

        digitalWrite(SR_CLOCK_PIN, 0);
        delayMicroseconds(20);

        boolean temp = digitalRead(SR_DATA_PIN);

        //button isn't pressed (pull up resistors cause reading to be 1)
        if (temp)  {


                    //send note off for regular press if the note on has been sent
                    if (midiNoteOnSent[buttonNumber] == true) {

                    midiNoteOnSent[buttonNumber] = false;

                }

                debounceTimer[buttonNumber] = millis();
        }



    else {

        //button press detected, check if is it a valid reading
        if ((millis() - debounceTimer[buttonNumber]) > BUTTON_DEBOUNCE_DELAY) {


                //button is really pressed

                //if the note on hasn't been sent already, send it
                if (midiNoteOnSent[buttonNumber] == false) {

                buttonDebugInfo(buttonNumber, MIDI_NOTE_ON_CHANNEL);


                //make sure the note is sent only once while button is pressed
                midiNoteOnSent[buttonNumber] = true;

        }
       
        }
    }


        digitalWrite(SR_CLOCK_PIN, 1);

  }

 }

Result is same thing, only much faster due to 0.2 to 20 microseconds fix in delayMicroseconds function:

Button: 26
Button: 28
Button: 30
Button: 24
Button: 30
Button: 24
Button: 26
Button: 28

Result is same thing, only much faster due to 0.2 to 20 microseconds fix in delayMicroseconds function

So, either I'm missing something or the cause of the slowness WAS the MIDI stuff.

Changing the delay from 0 to 20 microseconds rarely results in FASTER code.

PaulS:

Result is same thing, only much faster due to 0.2 to 20 microseconds fix in delayMicroseconds function

So, either I'm missing something or the cause of the slowness WAS the MIDI stuff.

Changing the delay from 0 to 20 microseconds rarely results in FASTER code.

The change from 0.2 to 20 somehow resulted in much-faster print-out in serial monitor (before I even commented out the MIDI part in function). Don't know how. Anyways the issue here is that I'm getting different button numbers while only one is pressed.

Anyways the issue here is that I'm getting different button numbers while only one is pressed.

Sounds like a hardware problem. I'm a software guy. Can't help you with the hardware side, especially not without a schematic.

I really think I’m doing something wrong in the code rather in schematics, but I’ve attached it anyways.

My first guess is that you are blasting through you latchPin() function too many times while pressing a button. I know you are complaining about the code running too slow but it is only too slow when you are concerned about your output. When a human presses a button they will typically hold it down for 10's or 100's of milliseconds. How many times does your loop() function get called per second while someone is pressing a button? Hundreds or maybe thousands of times. You need to track that.

It looks like you are printing information from all 4 (4051) mux chips on every pass through the loop. So you are probably getting information from the same pin on each of the four chips instead of just one like you are expecting.

mstanley:
My first guess is that you are blasting through you latchPin() function too many times while pressing a button. I know you are complaining about the code running too slow but it is only too slow when you are concerned about your output. When a human presses a button they will typically hold it down for 10's or 100's of milliseconds. How many times does your loop() function get called per second while someone is pressing a button? Hundreds or maybe thousands of times. You need to track that.

I can't think of anyhting else at the moment except timer interrupts.

kustom:
I can't think of anyhting else at the moment except timer interrupts.

I was thinking you could use the same technique as

if ((millis() - debounceTimer[buttonNumber]) > BUTTON_DEBOUNCE_DELAY) {

that's already in your code. It allows you to take action after an elapsed time rather than slowing your system down with time wasting delay()'s. It's the method I use in the keypad library.

Using timer interrupts could interfere with the proper operation of the millis() and micros() functions.

I tried something similiar already without success. Argh, this is really frustrating, I thought reading button matrix is a lot simpler.

Once again, issue in short:

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

openDeck.nextColumn**(1);**
openDeck.readButtons**(1);**
}

I press a button, nothing happens (button on column 0 that is, expected bahaviour).

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

openDeck.nextColumn**(0);
openDeck.readButtons
(0)**;
}

I press a button (on column 0) and get a print out on Serial monitor, expected behavior again.

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

openDeck.nextColumn**(i);
openDeck.readButtons
(i)**;
}

This part is driving me mad - i press a button on column 0 and get print out of 4 buttons, each on different column.
This obviously has something to do with deboucing or something simmiliar, I just don’t know what.

Try adding a Serial.print() statement to the body of the for loop, printing the value of i. Perhaps something will reveal itself.

Nothing wrong there.

Nothing wrong there.

But, you’re not going to show us. Well, OK.

kustom:
I tried something similiar already without success. Argh, this is really frustrating, I thought reading button matrix is a lot simpler.

It is simple in principle but there are several issues that need to be dealt with. One of the first things is the speed that I talked about. The chip can scan the keys thousands of times per second so you soon realize that you don’t want your function to return 10,000 times telling you that a key is pressed. So then you need to change your code to only return a value when the key changes from low-to-high or high-to-low. Once you do that then you realize that you not only need to catch a key flying by but you need to make sure your scan code doesn’t block any other code like updating 100 LED’s. The next thing to worry about is what happens if more than one key is pressed at the same time. :wink:

I’m about to leave work so it’ll be a little bit before I can look at it again. Can you post the entire sketch again so I can see where you are with it?

PaulS:

Nothing wrong there.

But, you’re not going to show us. Well, OK.

What’s there to show?

void loop() {

        for (int i=0; i<NUMBER_OF_COLUMNS; i++) {
         
          openDeck.nextColumn(i);
          Serial.print(i); Serial.print("\n"); delay(200);
          openDeck.readButtons(i);
          Serial.print(i); Serial.print("\n"); delay(200);
      
          
        }


}

Output from Serial monitor (only one button is pressed):

3
3
4
4
5
5
6
6
7
7
0
0
1
1
2
2
3
3
4
Button: 32
Column: 4
4
5
5
6
Button: 48
Column: 6
6
7
7
0
Button: 0
Column: 0
0
1
1
2
Button: 16
Column: 2
2
3
3
4
4
5
5
6
6
7
7
0
0
1

etc.