Accurate timing while performing many I/O functions

Hi guys, just digging into Arduino and loving it! :slight_smile:

Perhaps a bit ambitious for one of my first projects (although I have programming experience), but here's the challenge I've run into thus far.

I am using the Arduino Mega2560.

I want my Arduino to do the following:

-Monitor multiple analog inputs (5+)
-Monitor a rotary encoder with push button switch
-Write outputs to digital potentiometers (5+, one for each analog input)
-Have an additional switch that can be used to tap a tempo and have it flash an LED to that tempo
-Fade in and out two additional LEDs with separate timings on each simultaneously

I want all of this to happen at once. My question is, is it possible to having many I/O operations going on while maintaining accurate time based events (fading of LEDs, detecting the tap input, etc.)?

I'm just looking for general advice here. I've already gotten many of these functions working on their own, but not together at once yet.

My question is, is it possible to having many I/O operations going on while maintaining accurate time based events (fading of LEDs, detecting the tap input, etc.)?

Yes.

Look at the blink without delay example already loaded in your arduino IDE.

I'm just looking for general advice here.

Do not use the delay function.

Yes you should be able to do all that, as long as the inputs like the rotary encoder aren’t coming in too fast.

It will need some reasonably well thought out code though.


Rob

Cool, thanks for the encouragement guys. XD

The rotary encoder should be turned at an average rate by a "normal" person... And thanks for the tip on that blink without delay example! I'll definitely look into it.

From my experience, polling a rotary encoder every 1ms gives good results, but if you poll it every 4ms then it doesn't work well when you twist the rotary encoder quickly. You can find the code I use to interface to rotary encoders at the end of Five things I never use in Arduino projects | David Crocker's Solutions blog. I either use the MsTimer2 library or similar to generate a 1ms tick interrupt for this purpose, or if I have a fast main loop that doesn't call any blocking functions (such as serial output) then I poll the encoder in the main loop if/when at least 1ms has elapsed since the last poll. I generally poll the pushbutton at the same time as the encoder, although you can get away with polling it much less frequently.

Bear in mind that if you do use a tick interrupt with an interval as short as 1ms then you need to keep the interrupt code short.

Reading analog inputs takes around 120us each time. If you don't need to read them very frequently, just use analogRead(). If you do need to read them very frequently, it is better to access the ADC directly using a combination of a tick interrupt and a conversion complete interrupt, so that the processor can do other work while the conversion is in progress.

The rotary encoder...

Best use interrupt for that to count the pulses. See - http://www.arduino.cc/en/Reference/AttachInterrupt - fort the basics

robtillaart:

The rotary encoder...

Best use interrupt for that to count the pulses. See - http://www.arduino.cc/en/Reference/AttachInterrupt - fort the basics

That's ok if you only have one encoder and don't need the 2 interrupts for anything else.

That's ok if you only have one encoder and don't need the 2 interrupts for anything else.

Well the mega board has 5 user interrupts. And it's not always necessary to use 2 interrupts for a encoder, as interrupting on just one channel and then reading the value of the other channel will function fine depending on the step rate of the encoder (steps per revolution) and how you are utilizing the interrupt trigger, falling, raising, or change. I can use my magnetic encoder rated at 128 steps/rev and obtain 512, 256, or 128 'steps' per revolution depending on interrupt structure used.

The biggest road bump I have seen with using encoders is that the cheap mechanical encoders have sucky contact bounce and bad mechanical detent to encoder contact phase alignment that can be difficult to deal with. I don't use mechanical encoders anymore sense I found a close out bargain for some oak magnetic encoders for $5 each and bought 5. Hope I don't run out before finding another 'sale'. :wink:

Lefty

retrolefty:
And it's not always necessary to use 2 interrupts for a encoder, as interrupting on just one channel and then reading the value of the other channel will function fine depending on the step rate of the encoder (steps per revolution) and how you are utilizing the interrupt trigger, falling, raising, or change.

If you only monitor a single pin for changes in the state, you won't see all the Gray code transitions, which means that bounce becomes a potential problem. I guess that is OK using your magnetic encoders (which I presume are bounce-free), but you would need to hardware debounce that signal if using a mechanical encoder. Another reason I like to poll them in a timer interrupt is that it make it easy to multiplex them with other pushbuttons or encoders if I am short of input pins.

retrolefty:
The biggest road bump I have seen with using encoders is that the cheap mechanical encoders have sucky contact bounce and bad mechanical detent to encoder contact phase alignment that can be difficult to deal with

Assuming you're only interested in how many detents the encoder has gone through (the usual case for mechanical encoders used as human input devices), bounce isn't a problem if you capture all the Gray code transitions. It would only be a problem if both contacts bounced at once, which is unlikely because the 2 sets of contacts open and close at different times. I have seen the phase problem you mentioned on one brand of encoder I used, and my software works around this by allowing a +/- 1 error in the state at the detent.

retrolefty:
I don't use mechanical encoders anymore sense I found a close out bargain for some oak magnetic encoders for $5 each and bought 5. Hope I don't run out before finding another 'sale'. :wink:

That does sound like a very good price! I hope you'll post a message if you come across another such sale.

Grumpy_Mike:

My question is, is it possible to having many I/O operations going on while maintaining accurate time based events (fading of LEDs, detecting the tap input, etc.)?

Yes.

Look at the blink without delay example already loaded in your arduino IDE.

Is there a way to do the blink without delay while having the HIGH shorter than the LOW? For example, I want an LED on (HIGH) for 100ms and off (LOW) for 900ms. By default the blink without delay splits it evenly with 500ms HIGH and 500ms LOW.

Nevermind! Got it. For those wondering, how to do a blink without delay with different HIGH/LOW timings:

int tempoDelay = 1000;
float tempoHighDelay = 0.05;
float tempoLowDelay = 0.95;

long ledPrev = 0;
long ledCurrent = 0;
int ledValue = LOW;
int LED = 13;

void loop()
{
      ledCurrent = millis();
  
      if(ledValue == LOW && ledCurrent - ledPrev >= (tempoDelay * tempoLowDelay))
      {
        ledPrev = ledCurrent;
        ledValue = HIGH;
        digitalWrite(LED, ledValue);
      }else if(ledValue == HIGH && ledCurrent - ledPrev >= (tempoDelay * tempoHighDelay))
      {
        ledPrev = ledCurrent;
        ledValue = LOW;
        digitalWrite(LED, ledValue);
      }
}

Try something like this, change

    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;

to

    if (ledState == LOW) {
      ledState = HIGH;
      interval = 100;
    } else {
      ledState = LOW;
      interval = 1000;
   }

Rob