8x16 button matrix Leo midi controller input expanding help

Hi!

I'm building an 8x16 button matrix MIDI controller with a Leonardo, and was wondering what I should use to expand the amount of inputs. I'll be needing 24 inputs, right? It will be used for playing midi notes, so seems like the buttons should be scanned relatively fast.

I found tttapa's MIDI controller library which seems like the way to go. Not yet sure how to implement the expanders/shift registers (?), but I'll be studying the library and it should be clearer once I get an idea of the hardware needed.

I'm not very experienced with coding yet, so apologies in advance. Any help is appreciated.

emaj:
It will be used for playing midi notes, so seems like the buttons should be scanned relatively fast.

"Relatively fast" in human terms means a resolution of perhaps one twentieth of a second. In that time, the microcontroller has executed some 500,000 instructions.

You can scan your 8 by 16 matrix with a 74HC4017 and read the inputs with a pair of PCF8574s

or a single PCF8575 module.

Actually, I cannot see a cheap module to mount the 74HC4017, so you may be best to use one PCF8574 as the scanner and one PCF8575 as the reader, using the modules, your button matrix is already built - at least the electronics. Note you need to use a series diode on each button.

Paul__B:
...so you may be best to use one PCF8574 as the scanner and one PCF8575 as the reader...

Thanks for the quick reply! I ordered one of each, and now I'll have to figure out how to scan the matrix and somehow get the midi library to understand that stuff. Will be trying to google-fu that, but any quick tips?

If you are using this key matrix to play chords, rather than strictly one note at a time, you will run into the problem of "ghosting". To avoid this, you will need to connect a diode in series with every switch.

You can get 74hc4017 74hc4067 modules. To use them you will need 4 digital outputs. Plus 8 digital inputs to read the matrix, so you should have enough pins on Leonardo for your key matrix.

s-l400 (22).jpg

s-l400 (22).jpg

Well, you use the libraries to manage the chips/ modules, which should allow you to simply write the scan byte to the eight rows and read two bytes for the corresponding column inputs.

Note the nature of these port expanders - when you write a value to the single register, it pulls down pins you have set to 0 but only transiently pulls up the pins you set to 1, otherwise leaving them with weak pull-ups. A read of the register will then read pins set to 0 as 0, and pins set to 1 as 1 unless something - a button - is pulling the pin down.

This means each button must have a diode in series with cathode going to the "scan" module which selectively pulls down rows, and anode toward the "reading" module.

If you were seeking only one button press, you would pull down all rows to start with and read the columns until one was pulled low, at which point you would do a complete scan.

If however you expect multiple buttons to be coincidentally pressed, you will pull down one row at a time and take note of which column or columns are then pulled down for that row.

Also note the need for debouncing.

PaulRB:
You can get 74hc4017 modules.

Oh, you can? I looked on eBay (my usual source) and Adafruit (not really available for me here in Australia) and could not find them.

With the 74hc4017 it is not just "ghosting" but without the diodes the buttons can connect two scan lines together.

PaulRB:
To use them you will need 4 digital outputs. Plus 8 digital inputs to read the matrix, so you should have enough pins on Leonardo for your key matrix.

Umm, 10 scan lines, 8 "reading" lines, 80 matrix points.

I thought the OP said an 8 by 16 matrix ... :roll_eyes:

My bad. I meant to say 74hc4067, not 74hc4017.

74hc4067 is a 16-way multiplexer. Connect it's common pin to ground and it's 4 address inputs to Arduino digital outputs. It's 16 I/o pins connect to the matrix rows. 8 Arduino inputs (set to INPUT_PULLUP) connect to the matrix columns. The sketch scans through the 16 rows in turn and reads the 8 column inputs. If a switch is pressed, one of the Arduino input pins is pulled LOW through the 4067. That should work, do you agree?

Thank you both for good advice. I ordered a 4067, too. I'll prototype with a 4051 until the parts arrive.

PaulRB:
That should work, do you agree?

Can't see a problem.

Don't forget the diodes!

Paul__B:
Don't forget the diodes!

Yup! I've made a few mechanical keyboards so the diodes are routine. Just have to pay attention to polarity if it differs in this application, as soon as I wrap my head around this.

E:

Paul__B:
This means each button must have a diode in series with cathode going to the "scan" module which selectively pulls down rows, and anode toward the "reading" module.

Seems like this is the opposite of the polarity I've done before, have to keep that in mind.
Nevermind, it's the same way I've done it before. :slight_smile:

as soon as I wrap my head around this

It's not too difficult. You can think of the 74hc4067 as being like a 16-position rotary switch. The "com" pin is like the wiper contact, connecting to one of the 16 other contacts, while the other 15 are not connected to anything. The 4 "address" pins a, b, c & d control the position of the rotary switch. They will be made high or low by an Arduino output pin. That gives 2^4 = 16 combinations to control the switch position.

Your sketch will need to scan the matrix one row at a time, by setting the a, b, c & d pins to select the matrix row to be read. Because the com pin (the "wiper") is connected to ground, this will ground one row of the matrix, while all the other rows are "floating", being connected to neither ground or 5V.

Once the row had been selected, the sketch can then read the 8 Arduino inputs. If the corresponding button in the column is not pressed, the input will read high because the Arduino's internal pull-up resistors. But if the button is pressed, the Arduino input will read low because it is connected to ground through the diode, button and the 4067.

PaulRB:
It's not too difficult. You can think of the...

That makes sense to me, thanks!

I made a schematic, not sure if 100% correct. (See attachment)

I also tried to slap together a sketch using the examples provided with the library as reference, not exactly sure if it's in any way correct, though. Seems "too easy". No thought given to debouncing yet. But it compiles which I'm happy about.

//#include <frequencyToNote.h>
#include <MIDIUSB.h>
//#include <pitchToFrequency.h>
//#include <pitchToNote.h>

#include <MIDI_Controller.h>

// Create an instance of 'AnalogMultiplex' with the output pin of the multiplexer connected to
// pin 2 and the address pins connected to pins 3, 4 and 5.
// AnalogMultiplex(pin_t analogPin, { pin_t addressPin1, addressPin2, ... addressPinN } )
AnalogMultiplex multiplexer(2, { 3, 4, 5 } );


const uint8_t velocity = 0b1111111; // Maximum velocity (0b1111111 = 0x7F = 127)
const uint8_t addresses[8][8] = {
  { 1,  2,  3,  4,  5,  6,  7,  8  },
  { 9,  10, 11, 12, 13, 14, 15, 16 },
  { 17, 18, 19, 20, 21, 22, 23, 24 },
  { 25, 26, 27, 28, 29, 30, 31, 32 },
  { 33, 34, 35, 36, 37, 38, 39, 40 },
  { 41, 42, 43, 44, 45, 46, 47, 48 },
  { 49, 50, 51, 52, 53, 54, 55, 56 },
  { 57, 58, 59, 60, 61, 62, 63, 64 }
};

// Create a new instance of the class 'ButtonMatrix', called 'buttonmatrix', with dimensions 8 rows and 8 columns, with the rows connected to pins 6..13
// and the columns connected to multiplexer pins 0..7, that sends MIDI messages with the notes specified in 'addresses' on MIDI channel 1, with velocity 127
ButtonMatrix<8, 8> buttonmatrix({6, 7, 8, 9, 10, 11, 12, 13}, {multiplexer.pin(0), multiplexer.pin(1), multiplexer.pin(2), multiplexer.pin(3), multiplexer.pin(4), multiplexer.pin(5), multiplexer.pin(6), multiplexer.pin(7),}, addresses, 1, velocity);

void setup() {}

void loop() {
  // Refresh the buttons and send changes over MIDI
  MIDI_Controller.refresh();
}

Any corrections and points appreciated.

So we can see it:
64proto.PNG
Nope, diodes wrong way if you want to use the internal pull-ups on the eight Arduino lines though I note you have - for whatever reason - connected the "select" pin on the 4051 so you could set successive row pins to ground and sense buttons one at a time with the multiplexer using the internal pull-up on D2.

Paul__B:
Nope, diodes wrong way if you want to...

Ohh right, I think I finally got it. So, I pull the columns down consecutively, and then check on the arduino side if any of the lines are low, with the internal pull-ups keeping them high until then. Depending on the position of the 4051 I will know which buttons are pressed. Right?

Here's the updated schematic:

Sounds right. :grinning:

And you figured out to post images the best way. :sunglasses:

Paul__B:
Sounds right. :grinning:

And you figured out to post images the best way. :sunglasses:

Nice! Then it's on to prototyping:

Not 8x16 then?

Why is the "A" pin (pin 3) on the '4051 connected to an Arduino pin? Could just be connected to ground.

PaulRB:
Not 8x16 then?

Why is the "A" pin (pin 3) on the '4051 connected to an Arduino pin? Could just be connected to ground.

I'll make the full sized one with linear mx style switches and out of acrylic once the parts start arriving as I only had the 4051 and gateron brown switches laying around. The 'A' pin connection has been corrected, I originally copied the idea from somewhere else before fully understanding it's function.