Pages: [1]   Go Down
Author Topic: is this approach to MIDI keyboard feasible?  (Read 812 times)
0 Members and 1 Guest are viewing this topic.
New Brunswick, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

so I know there's probably tons of threads on MIDI, but I'd like someone else's opinion on my project so far. My goal is to have the arduino process the keypresses and output them as MIDI. So far I just have the 48 keypresses down, here's the code:

Code:
const int dataPin[] = {8,9,10,11,12,13};

void setup(){
  Serial.begin(9600);
  pinMode(5, OUTPUT);  pinMode(6, OUTPUT);  pinMode(7, OUTPUT);
  pinMode(dataPin[0], INPUT);  pinMode(dataPin[1], INPUT);  pinMode(dataPin[2], INPUT);
  pinMode(dataPin[3], INPUT);  pinMode(dataPin[4], INPUT);  pinMode(dataPin[5], INPUT);
}
 
void loop(){
  int currentNote;
  for(int select = 0; select < 8; select++){
    //pins 5,6,7 determine mux channel
    PORTD = PORTD & B00011111;
    PORTD = PORTD | byte(select) << 5;
    delayMicroseconds(50);
   
    //scan 6 keys in group
    for(int i = 0; i <= 5; i++){
      if (digitalRead(dataPin[i]) == HIGH){
        currentNote = i + select * 6;
        Serial.print("key ");Serial.print(currentNote);Serial.println(" is pressed");         
      }
    }
  }
}

and here's the schematic (I got tired of placing buttons 1 at a time in kicad, there are really 8 groups of 6 keys)



I don't know anything about the nuts and bolts of MIDI, but I figure I'll at least need a way to keep track of each key's state throughout the main loop. I'm thinking an array of 48 booleans, is there a more efficient way?

I guess my main question is, will there be enough "room" for the arduino to do both keypresses and MIDI output?
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm going to say yes, it's doable. As with all MIDI keyboards, the tradeoff is between latency and polyphony. If someone presses all 48 keys at once (polyphony), how long before the last note is sent out (latency). With MIDI being 31250 bps you can't do much about this, but it gives you a sense for how long you have to do the processing. For a MIDI note event with 3 serial characters at 31250 bps that's 96 microseconds between note events. That's a "reasonable" amount of time for the Arduino to do things like manage note states and so on.

However, you're going to want to optimize your key reading code to remove the 50 microsecond delay and use PIND access directly instead of digitalRead(), since it is much faster.

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino
Logged

New Brunswick, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

oh wow, PIND would replace that entire 2nd loop... thanks for the suggestion. I had the delay because I was getting weird behaviour without it— keys that are connected to the first data line (i.e. 0, 6, 12, 18) would read high even when a different group of keys was selected by the mux. I was thinking it might be the mux switching over, but that's supposed to take less than 275ns, so it must be something else. anyway, I'll play around with it, thanks for the feedback!
Logged

New Brunswick, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, I'm pretty frustrated right now... I rewrote it using PINB instead of digitalRead and it works, but only if I have a delay after setting PORTD. Something, somewhere, just isn't responding fast enough to the output stages. For debugging purposes I have this:

Code:
  for(int select = 0; select < 8; select++){
    //pins 5,6,7 determine mux channel
    PORTD = PORTD & B00011111;
    PORTD = PORTD | byte(select) << 5;
    //delayMicroseconds(20);
    in = PINB;         
    out = PORTD;
    Serial.print("in: ");Serial.println(in,BIN);
    Serial.print("out:");Serial.println(out,BIN);
    delay(500);   
  }
without the delay, and keeping the first key on the keyboard pressed, I get this:
Code:
in: 1
out:1
in: 1
out:100001
in: 1
out:1000001
in: 1
out:1100001
in: 1
out:10000001
in: 1
out:10100001
in: 1
out:11000001
in: 1
out:11100001
with that delay, it works as intended:
Code:
in: 1
out:1
in: 0
out:100001
in: 0
out:1000001
in: 0
out:1100001
in: 0
out:10000001
in: 0
out:10100001
in: 0
out:11000001
in: 0
out:11100001

The only thing I can think of is taking out the mux and rewiring the circuit to use 1 pin for each select line on the keyboard. If that doesn't solve it, I don't know where the latency is. And without the mux, I'm limited to 36 keys, taking up all the digital pins.
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Heh...you've been bitten by the "two-cycle synchronizer" delay smiley  See Figure 13-2 in Section 13.2 of the latest ATmega328P datasheet.

You actually need a 2-cycle delay between when an input physically changes at the pin and when you see it in software. So try adding some nop's instead of a delay():

Code:
  PORTD = PORTD | byte(select) << 5;
  asm volatile ("nop");
  asm volatile ("nop");
  in = PINB;

--
The Gadget Shield: accelerometer, RGB LED, IR transmit/receive, speaker, microphone, light sensor, potentiometer, pushbuttons
Logged

New Brunswick, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Haha oh dear, you're gonna laugh at me for this, but it turns out it was the pulldown resistor on that pin. I tore the whole thing apart and rebuilt it, then started getting the problem on a completely different key! Since I only have 2 of each resistor I just grabbed a bunch of 1k multiplier ones, but somehow a 160k slipped in... that'll teach me

thanks for your help, I'll keep that nop tip in mind!
Logged

Wellington, New Zealand
Offline Offline
Sr. Member
****
Karma: 1
Posts: 404
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you're using an UNO or a Mega2560 then you might want to go with the MIDI USB device approach rather than serial.  Basically you reprogram the UNO's atmega8u2 with MIDI USB driver firmware so the UNO appears to be a USB MIDI device to the host, and it converts serial data at 115200 baud (or potentially faster) into MIDI USB messages sent to the host PC.

You can get the MIDI USB driver here: http://hunt.net.nz/users/darran/blog/, along with details on how to use DFU mode to flash the driver to the atmega8u2's.

If you're using windows you'll need flip to program the atmega8u2 (here: http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3886).

Cheers,
Darran.
Logged


New Brunswick, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ah, I'm using the diecimila... I plan on hooking it up to my gameboy in the end, so I'll be using serial. Since I don't have an EPROM yet to do that I will be testing it with my pc.
Logged

Pages: [1]   Go Up
Jump to: