0

Can I use two interrupts on to count rpm square signals coming from two hall effect sensor modules (that has built in comparators to generate square signals) ?

What if two signals come together ? Will they conflict with each other ?

Will this work on attiny85 ?

I used to use two interrupt to read encoders where the two signals never conflict with each other

Thank you in advance

  • What maximum frequency do the square waves have? If you really cannot risk to loose any edge of the signals, you can use them as clock input for the 2 timers of the ATTiny – chrisl May 27 at 9:26
  • Thank you for your comment chrisl ...I have 40 Hz as maximum input, but actually I work with arduino programing language so I didn't get the idea of using 2 timers ... – Elias Taalab May 27 at 13:10
  • Oh, during writing an answer, I found, that only 1 of the two timers has an counter input pin, so that will not work on the ATtiny85. I will add that do my anser. – chrisl May 27 at 14:58
  • You can use INT0 and PCINT. The hardware will handle the checking of the signal, and will set a flag. When any of these flags are set the CPU will call the ISR. If both signals happen at the same time, both flags will be set, and the CPU will call their ISRs, one after the other. You won't loose any signals, unless the ISR takes more time, than the time between signals. The only slight problem is that the ISR isn't necessarily called at the exact time of the event. – Gerben May 27 at 15:10
1

There is more than 1 option how this can be done. A frequency of 40Hz is relatively slow, so you are not limited by this.

In my comment I first suggested to use the Timers/Counters with the signal as input, but I found, that this is only possible with one of the two timers of the ATtiny. Despite that, I will add this to the answer for information (and because this can be possible on other boards with more timer input pins).

  • Using the PinChange interrupt and doing counting in software: Since the ATtiny85 has only 1 external interrupt, you have to use the PinChange interrupt for this. If activated, this interrupt will trigger, when ever one of the pins 0 to 5 changes it's value. You can mask away any pin in this range, so that it doesn't count. You would use 2 pins and mask the others away. In the interrupt routine you would check the state of the two pins and increment the respective counter:

    volatile unsigned int counter1=0;
    volatile unsigned int counter2=0;
    
    ISR(PCINT0){
        if(digitalRead(input_pin_1)) counter1++;
        if(digitalRead(input_pin_2)) counter2++;
    }
    

    (Note the volatile declaration of the variables, because they change inside of an ISR). The counterX variables can then read in defined time intervals (for example very second) to calculate the RPM. Of course, executing code in an ISR can mean, that you miss another interrupt. During the short time period between the first interrupt and the completed execution of the ISR no other PinChange interrupt will get registered. If that is a problem is dependend on how often the pulses will be inside this small range. If that only happens very rarely you can ignore that, since it introduces only very small errors. You can even make the ISR faster by not using digitalRead(), which takes a bit to execute, but using direct Prt Manipulations. That makes the error more unlikely.

    The interrupts have to be configured for this, so you need a setup code like this (this will use direct manipulation of the status registers of the ATtiny):

    PCMSK = 0b000110; // Mask all pins for this interrupt,except for PCINT1 and PCINT2
    GIFR &= ~(1 << PCIF); //Clear PinChange interrupt flag
    GIMSK |= (1 << PCIE); //Enable PinChange interrupt
    

    In the loop function you can use the non-blocking code style from the BlinkWithoutDelay example, to regularly calculate the RPM from the counter values;

    void loop(){
        if(millis()-timestamp > INTERVAL){
            GIMSK &= ~(1 << PCIE); //Disable PinChange Interrupt, so that we don't get interrupted by ti
            unsigned int countervalue1 = counter1;
            unsigned int countervalue2 = counter2;
            counter1=0;
            counter2=0;
            IMSK |= (1 << PCIE); //Reenable PinChange interrupt
            // Do the calcuation here with countervalueX and show the result somewhere
        }
    }
    

    It is important to disable the corresponding interrupt, when you are accessing the counterX variables, because you have to prevent them from changing midway during processing. To keep this short, they are just copied to local variables and then released again.


Attention: the following suggestion doesn't work on an ATtiny85, since it only has 1 timer input pin.

  • Using both available Timers/Counters and the signals as clock input: If you really don't want to miss any pulses, you can utilize two Timers/Counters. Timers and Counters are the same. A counter increments a status register on every falling or rising edge (depending on the configuration) of a signal. If this signal is derivated from the chips clock, this is called a Timer. You would use it as a Counter, feeding each square wave to one Counter input pin. To actually get the RPM, you can either check the counters value (TCNT0) after defined time interval, or you can activate the overflow interrupt of both timers and measure the time between the overflows.

    Since here the edge-sensing is done in hardware, no pulse can be missed. On the other side, this way needs more configuring and blocks 2 precious timers.


When working with the status registers in such a direct way, the datasheet of the ATtiny85 is a must read.

  • Ok I think using an Arduino pro mini will be cheap enough if I want to use the second method (the two timers method ) and it will be accurate ... What do you think if I want to add i2c or serial to the code ? – Elias Taalab May 27 at 22:06
  • That shouldn't be a problem with the Pro mini, since it has hardware serial and hardware I2C. The ATtiny not, so the Pro mini really would be the better board. – chrisl May 27 at 22:10
  • If I use two interrupts will this conflict with arduino code (like if I have three loadcells that I need to read or write a value to a servo ) ?? .. my original post was about using an attiny85 (or atmega328 in arduino pro mini ) to send rpm values to a master arduino that will gather all values and make some dicitions .. – Elias Taalab May 27 at 22:21
  • As I remember the servo library uses Timer1 and Timer 0 is normally used for the Arduino timing functions (delay, millis and siblings). To also use that a slave Arduino to read the rpm might be good. Or a bigger Arduino with more timers – chrisl May 28 at 6:59
0

If you can skip using interrupts, your Tiny could still do the job and not miss any pulses, as long as loop() can be executed at >40Hz, and the pulses are not be extremely short "spikes" but have a reasonable duty-cycle (or if short, you can sample faster):

// Pin data
static struct {
   uint8_t pinNum;
   bool wasHi;       // last state
   unsigned count;   // pulse count
} pinsDat[2] = {
   {input_pin_1, false, 0},
   {input_pin_2, false, 0},
};


void setup(){
   ; // your setup here
}


void loop(){
   countPulses();
   // ...
}

void countPulses(){
   for( uint8_t i = 0; i < 2; ++i){
      if( !digitalRead(pinsDat[i].pinNum) )
         isHigh1 = false;       // pin lo, just save its state
      else if( !pinsDat[i].wasHi ){
         ++pinsDat[i].count;    // pin hi; count it
         ++pinsDat[i].wasHi;    // save its state
      }
   }
}

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.