1

I have an encoder that fires ~54000 interruptions per second. This is way too fast for my arduino uno. But I'm trying to see what I can get from it anyway. For science.

unsigned long tickACount = 0;
unsigned long tickBCount = 0;

void myInterrupt(){
  tickACount++;
  tickBCount++;
}

Then in my main loop, every second I print tickACount and tickBCount. I can see my arduino is able to handle only about 2050 interrupts per second. No surprise here.

What surprises me, is that tickACount and tickBCount have different values! tickBCount is consistently higher that tickACount.

Ok now I switch them to volatile. And according to the documentation, volatile only makes sense for 8bit values. The counts will eventually overflow but it doesn't really matter. I just want to see if those two variables are still different.

volatile uint8 tickACount = 0;
volatile uint8 tickBCount = 0;

void myInterrupt(){
  tickACount++;
  tickBCount++;
}

The result is that the two values are still different. Also, wrapping them into some ATOMIC macro does not help.

From what I read, interrupts cannot be interrupted. And beside, if my interrupt was aborted midway, then tickACount would be higher than tickBCount, not the other way around.

Therefore, what can be the explanation for those two variables to be different?

Thank you for any idea!

  • 2
    They are the same. But when you print one and then print the other one, there has been interrupts between that. Make a full sketch that shows the problem. With a lot optimizations, the uno board might do more than 54kHz interrupts, perhaps 100kHz. Interrupt tutorial: gammon.com.au/interrupts – Jot Aug 19 '18 at 18:21
  • 2
    You can save quite a few CPU cycles by creating your own ISR instead of relying on the one from the Arduino core to call your interrupt handler. See this answer, section Write your own ISR. – Edgar Bonet Aug 19 '18 at 18:59
  • 1
    If you want simple counter, why don't you use Timer1 or Timer2 with external clock source? – KIIV Aug 20 '18 at 5:23
  • Would you consider using extra hardware to reduce microcontroller load? How about using shift registers to count pulses. – MichaelT Aug 20 '18 at 10:56
  • I was convinced that 54kHz is way to fast for Arduino, which runs at 16MHz. That's only ~300 cycles per interruption + it has to handle the normal loop. I replaced my interrupts by the Encoder library github.com/PaulStoffregen/Encoder, and it now seem to work fine. I digged a bit into that library, it seems to do about the same thing as I did, but in assembly with probably some big optimization done. Well done, about the tickCountB > tickCountA. The possibility of tickCountB being incremented during the 2 prints hasn't come to my mind. I guess that's experience! – OoDeLally Aug 22 '18 at 16:22
4

54000 interruptions per second [...] is way too fast for my arduino uno

Your test is probably flawed. My Uno does handle that interrupt rate with no issues. See my test code below.

my arduino is able to handle only about 2050 interrupts per second

You are probably doing something wrong, like blocking interrupts for too long, or using a library that does so.

What surprises me, is that tickACount and tickBCount have different values!

As previously stated in comments, this is not the case. Instead, the counters have been incremented while you were printing them.

And according to the documentation, volatile only makes sense for 8bit values.

Either the documentation is grossly wrong, or (most likely) you have misread it.

From what I read, interrupts cannot be interrupted.

This is indeed the default behavior. You could change it, by issuing interrupts() within the interrupt handler, or by declaring an ISR as

ISR(XXX_vect, ISR_NOBLOCK)
{
    ...
}

but that is seldom done.

Below is my test code. I used Timer 2 to generate a square wave on pin 3, which is both a PWM pin (OC2B) and an interrupt pin (INT1). The PWM frequency is about 54.054 kHz.

const uint32_t PRINT_PERIOD = 1e6;  // print counts every second

struct TickCounts {
    uint32_t tickA;
    uint32_t tickB;
};

volatile TickCounts rawCounts;

void myInterrupt() {
    rawCounts.tickA++;
    rawCounts.tickB++;
}

// Atomically read both counts.
static TickCounts getCounts() {
    TickCounts counts;
    noInterrupts();
    counts.tickA = rawCounts.tickA;
    counts.tickB = rawCounts.tickB;
    interrupts();
    return counts;
}

void setup() {
    // PWM at 54 kHz on OC2B = PD3 = INT1
    DDRD  |= _BV(PD3);    // pin PD3 as output
    OCR2A  = 37 - 1;      // period = 37 * 0.5 us = 18.5 us
    OCR2B  = 37/2 - 1;    // duty cycle ~ 50%
    TCCR2A = _BV(COM2B1)  // non-inverting PWM on OC2B
           | _BV(WGM20)   // mode 7: fast PWM, TOP = OCR2A
           | _BV(WGM21);  // ditto
    TCCR2B = _BV(WGM22)   // ditto
           | _BV(CS21);   // clock at F_CPU/8 = 2 MHz

    Serial.begin(9600);
    attachInterrupt(1, myInterrupt, RISING);
}

void loop() {
    static uint32_t last_time_printed;
    if (micros() - last_time_printed >= PRINT_PERIOD) {
        last_time_printed += PRINT_PERIOD;
        TickCounts counts = getCounts();
        Serial.print(counts.tickA);
        Serial.print(" ");
        Serial.println(counts.tickB);
    }
}

And here is the output:

54053 54053
108107 108107
162161 162161
216215 216215
270269 270269
324324 324324
378377 378377
432431 432431
486485 486485
540539 540539
...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.