USB Midi and interrupts

batmundo

Member
Hi!

I have a lighting rig that I had built using Arduino Megas and APA102Cs, and just swapped them out for Teensy 3.5s instead - so far everything seems to be working ok, except some MIDI functionality I'm having trouble with. The gist of the setup is that I'm sending MIDI notes to the hardware to trigger various FastLED functions to animate the LED strips - on the Arduinos I had to use interrupts to ensure incoming MIDI notes wouldn't get dropped. In porting the code to Teensy, I'm finding that I can't use the same interrupt code (because of different hardware I assume?), and when I disable the interrupt portion of the code, all the notes still make in, but LEDs don't display simultaneously, and show up staggered instead. I'm guessing this is because the interrupt isn't working here and the LEDs are displaying after each MIDI note sequentially, rather than handling with each MIDI note as they're received in real time as they come in?

Do I need to be using interrupts here again? Unfortunately, I'm still relatively new to interrupts - I found the code I'm using and incorporated it into my setup without too much difficulty, but I still have very little understanding of what it's actually doing.

In my search, I did find this:
https://forum.pjrc.com/threads/42961-USB-COM-based-interrupt

...but my problem doesn't seem buffer related. Also, they mention avoiding interrupts if possible - is there another way to deal with this then?


Here's a simplified version of the code I've been using for testing:


Code:
void loop() {

  usbMIDI.read();

  for (unsigned char i = 0; i < 128; i++) {
      if (notes[i].state == true) {
        runAnimation (notes[i].note, notes[i].velocity);
      }
  }

  EVERY_N_MILLISECONDS( 20 ) { hue++; }

  FastLED.show();

  EVERY_N_MILLISECONDS(50) {
    fadeToBlackBy(leds_S1, NUM_LEDS_S, globalFadeVal);
    fadeToBlackBy(leds_S2, NUM_LEDS_S, globalFadeVal);
    fadeToBlackBy(leds_S3, NUM_LEDS_S, globalFadeVal);
    fadeToBlackBy(leds_Q1, NUM_LEDS_Q, globalFadeVal);
    fadeToBlackBy(leds_Q2, NUM_LEDS_Q, globalFadeVal);
  }
}


void runAnimation (byte n, byte v) {
    switch (n) {
      case 0: fill_rainbow( leds_S1, NUM_LEDS_S, hue, 7); break;
      case 1: fill_rainbow( leds_S2, NUM_LEDS_S, hue, 7); break;
      case 2: fill_rainbow( leds_S3, NUM_LEDS_S, hue, 7); break;
      case 3: fill_rainbow( leds_Q1, NUM_LEDS_Q, hue, 7); break;
      case 4: fill_rainbow( leds_Q2, NUM_LEDS_Q, hue, 7); break;
  }
}



FWIW, here's the interrupt code I had going on the Mega:

Code:
  cli();// stop interrupts

  // set timer2 interrupt every 128us
  TCCR2A = 0; // set entire TCCR2A register to 0
  TCCR2B = 0; // same for TCCR2B
  TCNT2  = 0; // initialize counter value to 0
  // set compare match register for 7.8khz increments
  OCR2A = 255;  // = (16*10^6) / (7812.5*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS11 bit for 8 prescaler
  TCCR2B |= (1 << CS11);   
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  
  sei();  // allow interrupts
 
Just a blind guess, since I can't see the rest of your program, but perhaps you're using FastLED with the default WS2812 driver which blocks interrupts? It's known to block incoming serial and interfere with other libraries which use interrupts.

If so, you should use WS2812Serial or OctoWS2811. FastLED has drivers that can make use of those libraries so you get all the nice FastLED features without the blocking I/O. Each of those libraries has an example showing how to use it from FastLED.

Generally, using interrupts can give great performance, but the cost is difficult programming to properly share data. If you really want to go that route, use IntervalTimer to get a timer interrupt without needing to mess with hardware registers.

https://www.pjrc.com/teensy/td_timing_IntervalTimer.html

Avoiding direct hardware registers will keep open an easy path to migrate up to Teensy 4.1 if you later need more performance.
 
I'm using APA102 LED strips, so I'm unable to use WS2812Serial or OctoWS2811 without a whole bunch of re-wiring. I've been trying to read through all sorts of resources on interrupts and how they work, but am having some difficulty understanding some of the code involved. The IntervalTimer code seems much easier to comprehend. What's the difference between using this versus the examples here? https://www.pjrc.com/teensy/interrupts.html
 
Back
Top