Rotary Encoder and Interrupts

Status
Not open for further replies.

Dinera

Member
Hi,

I got a rotary encoder working, but only by polling (like the example in the tutorial).
Can I use the encoder lib with interrupts? Or is there a data lost of the first bit of grey code?

Thanks for help!
 
Hi GremlinRrangler,

Yesterday I was tired and I forgot the code samples :rolleyes:

that's the exact tutorial which I followed.

Code:
/* USB MIDI AnalogControlChange Example

   You must select MIDI from the "Tools > USB Type" menu
   http://www.pjrc.com/teensy/td_midi.html

   This example code is in the public domain.
*/
#include <Encoder.h>
#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI continuous controller for each analog input
Encoder myEnc(0, 1);

const int controller0 = 21; // 10 = pan position
float multi = 4; // 0.5 = 16 steps; 1 = 32 steps; 2 = 64 steps; 4 = 127 steps; 8 = 256 steps (2 steps for one increase)

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
}

// store previously sent values, to detect change
long previous0 = -1;
long sent0 = -1;

elapsedMillis msec = 0;

void loop() {
  long raw = myEnc.read();
  if (raw % 4 == 0) {
    long n0 = raw/multi;
    if (n0 > 127) {
      myEnc.write(127*multi);
      n0 = 127;
    }
    if (n0 < 0) {
      myEnc.write(0);
      n0 = 0;
    }
    if (n0 != previous0) {
      previous0 = n0;
    }
    // only check the analog inputs 50 times per second,
    // to prevent a flood of MIDI messages
    if (sent0 != previous0) {
      if (msec >= 20) {
        msec = 0;    
        // only transmit MIDI messages if analog input changed
        usbMIDI.sendControlChange(controller0, n0, channel);
        sent0 = previous0;
      }
    }
    // MIDI Controllers should discard incoming MIDI messages.
    // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
    while (usbMIDI.read()) {
      // ignore incoming messages
    }
  }
}

This is the code I wrote. It works fine but If I want to use some more rotary encoders I need interrupts. I was trying it with this code:

Code:
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;
const int controller0 = 21; // 10 = pan position

// Encoder
Encoder myEnc(0, 1);

// store current, previously read values and previously sent values, to detect change
long n0 = 0;
long previous0 = -1;
long sent0 = -1;

// MIDI overflow variable
elapsedMillis msec = 0;

// steps to MIDI value 127
float multi = 4; // 0.5 = 16 steps; 1 = 32 steps; 2 = 64 steps; 4 = 127 steps; 8 = 256 steps (2 steps for one increase)

void interrupt_rutine() {
  long raw = myEnc.read();
  if (raw % 4 == 0) {
    n0 = raw/multi;
    if (n0 > 127) {
      myEnc.write(127*multi);
      n0 = 127;
    }
    if (n0 < 0) {
      myEnc.write(0);
      n0 = 0;
    }
    if (n0 != previous0) {
      previous0 = n0;
    }
  }
}

void setup() {
  // to check teensy has power
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  // interrupts
  attachInterrupt(digitalPinToInterrupt(0), interrupt_rutine, CHANGE);
  attachInterrupt(digitalPinToInterrupt(1), interrupt_rutine, CHANGE);  
}

void loop() {
    // only transmit MIDI messages if rotary encoder input changed
    if (sent0 != previous0) {
      // only check the analog inputs 50 times per second,
      // to prevent a flood of MIDI messages
      if (msec >= 20) {
        msec = 0;    
        usbMIDI.sendControlChange(controller0, n0, channel);
        sent0 = previous0;
      }
    }
  
    // MIDI Controllers should discard incoming MIDI messages.
    // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
    while (usbMIDI.read()) {
      // ignore incoming messages
    }

}

I got only one event in KMidimon (Midi monitor in Linux). So I guess the encoder lib and interrupt lib need a special configuration.
 
Confirm this is a Teensy LC or 3.2? Below assumes the newer flavours, if you are using a T2 that will change things.

For midi control type inputs you should be running out of pins before you run out of interupts/performance capacity (since you are only turning them slowly and at most two at a time) So the expected design is you just define inputs and encoders until you are done. You should not need to use encoder_optimize_interrupts unless you are working with a T2, and you should not need to manually attach interupts or write interupt handlers either unless you are planing to fully strip out the encoder library and handle the IO yourself. Doing both on a T3 or LC is going to get interesting results.

Would suggest reverting to the USB_midi example, and just adding extra encoders there.

A suggestion as you add encoders would be to add Serial.begin() to your code and each second print the current Raw encoder values and adjusted midi values to the serial port to make it clearer what is going on. Blinking the LED can also be useful as you get more complex wiring.
 
For a T3.2 it should be using interrupts by default without any action from you, as I understand it the code you had was intended for T1/T2 8 bit Teensyies. If you were using the encoders on say a CNC machine you might need to play with the priority level of those interrupts but you do not appear to be turning them at khz pulse rates so just using them as per the example should just work.

So most likely problem here is something else in the setup/code, especially if Serial is also not firing. Suggest trying the two knobs examples from the encoder page, which will confirm this is actually about the encoders and not the USB midi end of things.
https://www.pjrc.com/teensy/td_libs_Encoder.html.
With testing serial did you code to update on the state change or every second? If you had every second and it stops something more catastrophic is going on that hangs the Teensy. To get every second response you could use
https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

and define
elapsedMillis oneSecondStatusUpdateTimer = 0;
then put
Code:
if (oneSecondStatusUpdateTimer>1000){
   oneSecondStatusUpdateTimer=0;// reset the timer, otherwise will only fire once  
   Serial.println("encoder values are");
   Serial.print(previous0);
   Serial.print(" "); //add a space between numbers
   Serial.print(previous1);
   Serial.println(); //put a line feed at the end of encoder values
}
 
I've tried some more simple code on the T3.2, T3.5 and T3.6.
Code:
if (oneSecondStatusUpdateTimer>1000){
   oneSecondStatusUpdateTimer=0;// reset the timer, otherwise will only fire once  
   Serial.println("Hello ");
   Serial.print("World!");
   Serial.print(" "); //add a space between numbers
   Serial.print("more Text");
   Serial.println(); //put a line feed at the end of encoder values
   if (led == 1) {
      digitalWrite(13, HIGH);
      led = 0;
   }
   else {
      digitalWrite(13, LOW);
      led = 1;
   }
}

The output only prints "Hello" and nothing else. But the LED is blinking every second. I guess this serial problem is a Linux problem.

After studying the code I undersand what you mean with "For a T3.2 it should be using interrupts by default without any action from you". The myEnc.read() take the value which was written by the interrupt.

Now everything is clear. Thank you for your help!
 
The output only prints "Hello" and nothing else. But the LED is blinking every second. I guess this serial problem is a Linux problem.
(...)
Now everything is clear. Thank you for your help!

By surfing around in these forums, you might discover that there are in fact issues with some Linux distributions and serial communication. It's not the Teensy or your code to blame.
 
By surfing around in these forums, you might discover that there are in fact issues with some Linux distributions and serial communication. It's not the Teensy or your code to blame.

I've tried to fix it but after many hours I've accepted that it is not working. In a quiet hour I will take care of the issue ;)
 
Status
Not open for further replies.
Back
Top