cant read optical wheel encoder with attachInterupt?? (but polling works fine)

hello forum. i want to mesure the rpm, and the degrees of a rotating wheel encoder. the encoder is something like this : http://todbot.com/blog/wp-content/uploads/2008/06/rotary-encoder-closeup-300x221.jpg

and the "sensor" outpouts LOW when there is a hole, and HIGH when the light is blocked... the encoder has 35 holes (10deggres each, and one is missing)

if i use the following code, it works just fine. i can messure both RPM, and the count of holes that the sensor has seen. (although the code breaks appart, in higher than 4000rpm)

int encoder0Pos = 0;
int encoder0PinALast = HIGH;
int n = HIGH;
float starttime=0;
float pulse;
float rpm;

void setup() {
  pinMode (2,INPUT);
  pinMode (encoder0PinB,INPUT);
  Serial.begin (9600);
}


void loop() {
  n = digitalRead(2);
  if ((encoder0PinALast == HIGH) && (n == LOW)) {


    pulse = micros() - starttime ;
    encoder0Pos++;
    rpm=60000000.0/pulse;

    Serial.print (encoder0Pos);
    Serial.print ("    " );
    Serial.println(rpm);
    starttime=micros();
  }

  encoder0PinALast = n;
}

BUT, if i want to do it, using attachInterupt, (etc. have one intterupt happen, whenever a hole, passes infront of the sensor) , it dosent work at all...

even a simple code like that :

int count = 0;


void setup() {
  Serial.begin(9600);
  attachInterrupt(0,counter,FALLING)
}


void counter() {
  count ++ ;
}

void loop() {
  Serial.println(count);
  delay(100);
}

etc, it counts literally random number of holes, for each wheel rotation...

any ideas why would this happen?

    starttime=micros();

Why are you storing the long int returned my micros(0 in a float?

any ideas why would this happen?

Variables used in ISRs and other functions need to be volatile.

PaulS:
Variables used in ISRs and other functions need to be volatile.

Additionally, since the variables shared between the interrupt and main contexts are multibyte values, you should disable interrupts briefly while you copy from the volatile variable to a local temporary variable, to prevent problems where the interrupt updates the volatile variable while the main context code is mid way through reading it.

Why are you storing the long int returned my micros(0 in a float?

no real reason. i normaly use the unsigned long, as supposed when dealing with micros(). i was messing around and forgot to change it before posting (the results are the same anyway )

so, in the case of the second code, i should declare the “count” veriable like that :

volatile int count=0 ;

and also. inside the ISR, should be :

void counter() {
 NoInterupts(); 
 count ++ ;
 Interupts();
}

??
As i understand it, this short of think, can be done with interupts. it’s just that i dont use the correct code??

settra:
inside the ISR, should be :

void counter() {

NoInterupts();
count ++ ;
Interupts();
}



??

No. You should not disable and enable interrupts within an interrupt handler. The disabling and enabling is needed for code running in the main context which needs to access volatile multi-byte variables.

hm? so when do i need to disable?? in my code. if i wanted to do some calculations, using the "counter" variable, then i would disable , just before the calculations. and anable after??

ISR:
void counter() {
 NoInterupts(); 
 count ++ ;
 Interupts();
}

void loop() {
Nointerupts;
Serial.println(count);
Interupts;

}

?

settra: hm? so when do i need to disable??

DO NOT disable or enable interrupts inside an interrupt handler.

If you have a multi-byte variable that is shared between the interrupt context and the main context then it needs to be declared volatile and you need to disable interrupts while you access it in the main context. The preferred way to do that is to disable interrupts, copy the volatile variable into a local temporary variable and then enable interrupts, so you minimise the length of time that interrupts need to be disabled for.

ok , i will try that! but what if the sensor does not produce a clean signal?? if it has noise, and produces many interupts, when only one should had been produced?? woulden't then, disabling interupts, inside the handler, make sense??

ok so this code, will generally work, BUT will some times, produce irrelevant numbers... when rpm is 200, it sometimes (often), results in saying the rpm are 10k + .. mayeb cause it recieves too many pulses for some reason??

volatile unsigned long pulse  ;
volatile unsigned long pulsestart = 0;
volatile unsigned long temp;
volatile unsigned int rpm=0;
// if i declare rpm as just "int" , then rpm are always a negative number for some reason.....

volatile int  count=0 ;


void setup() {
  Serial.begin(9600);
  pinMode(2,INPUT);
  attachInterrupt(0,counter,FALLING);
}


void counter() {
  
  count++ ;
  calcrpm();
  }
  
void calcrpm(){
  
  noInterrupts();
  pulse = micros() -pulsestart ;
  temp = pulse*36;
  pulsestart=micros();
  
  rpm=60000000/temp;
  
  interrupts();
  return ;
}
  
  

void loop() {
  noInterrupts();
  Serial.print("count: ");
  Serial.print(count);
  Serial.print("  pulse:");
  Serial.print(pulse);
  Serial.print("  temp : ");
  Serial.print(temp);
  Serial.print("  RPM : ");
  Serial.println(rpm);
  interrupts();
  delay(100);
  
}

settra:
what if the sensor does not produce a clean signal??
if it has noise, and produces many interrupts, when only one should had been produced?? wouldn’t then, disabling interrupts, inside the handler, make sense??

Interrupts are disabled inside the handler. It could not work otherwise. And they are correspondingly re-enabled as the interrupt handler exits. If you try to muck about with this, you can have interrupts interrupting interrupts and cause even more trouble.

The aspect of noise or “bounce” and superimposed interrupts is precisely the reason you should not use interrupts for mechanical inputs unless really necessary. Mostly if you think you need to use interrupts, it is due to bad code design - such as in this case, using floats where integers are the correct choice (for all counters).

While you are counting, you should generally not be attempting to do other things - such as displaying the count. It doesn’t make sense. In order to count, you have to be rapidly alternating between two tasks - counting the inputs, and keeping track of the time. Interrupts - in most cases - do not simplify the process if only because it takes (significant) time/ clock cycles to service the interrupt itself. If you need to debounce (and you probably should not with an optical encoder but always do with any form of contact except perhaps mercury wetted) then this is particularly the case, executing debounce with interrupts is more difficult than polling.

i told you, the floats was there cause i was messing arround >_> :smiley:

well, in my case, i will ALWAYS have to do things (and many) , while also counting…
i have also tried , time driven polling (every 0.1 ms, use time interrupt and do the counting inside the ISR with polling)

while you are counting, you should generally not be attempting to do other things - such as displaying the count.

what do you mean by that, in code language?? what sort of “protection” should i use, in order to avoid this??

after all, it was nothing from the above... the code that after all worked for me was this :

volatile boolean check=true;

volatile int count;
volatile int rpm;

volatile unsigned long pulse;
volatile unsigned long temppulse;
volatile unsigned long pulsestart=0;

void counter() {
  if (digitalRead(2)==HIGH) {
     check=true;
}
  else{  
       if (check==true) {
               if( (micros()-pulsestart)>=(1.8*pulse) ){
                 count=0;
               }
         
         count++;
         check=false;
         pulse= micros()-pulsestart;
         pulsestart= micros();
         
         temppulse = pulse*36 ;
         rpm = 60000000/temppulse ;
         
         
        
       }
   }
}

!!!

settra: ok , i will try that! but what if the sensor does not produce a clean signal?? if it has noise, and produces many interupts, when only one should had been produced?? woulden't then, disabling interupts, inside the handler, make sense??

No, disabling or enabling interrupts within the handler does not make sense. Put that out of your mind completely. I don't know how many more ways there are to say this.

If your input signal is not clean it may cause missed or spurious interrupts. It is up to you to handle this either in hardware (filtering) or software (debounce/timeout). If this thread is about your attempt to build your own ECU which you mentioned on another thread then you ought to be aware that cars are electrically noisy environments and you will certainly need to deal with noise one way or the other - or both.

so, today i got to test this, on a higher RPM count... even though the code works perfectly, bellow 500rpm, at higher RPM's, it completly fails.. and by "fail" i mean, that, if i rise the RPM, the code outpouts, LESS rpm... as if, it was loosing pulses.... and above 2000rpm, it even stops, updating, the value, like if no new interutps where comming...

any chance, the encoder i used, cant support that high frequency??? it looks like this : http://ecx.images-amazon.com/images/I/41QwOx4vZZL._SL500_AA300_.jpg

but it has two outpouts, one Digital and one Analog (i use the analog, cause i cant seem to figure, what the digital one does)

**POLLING gives the same bad results... **the disk, has 36 holes, per revolution...

Those symptoms do kind of suggest that you might be reaching the minimum response time of the sensor. I can't tell from the picture what part you're using and I recommend that you find the sensor part number and look up the data sheet to find what its maximum frequency is. This will also tell you how to use the digital output. I would expect the digital output to give you a cleaner signal so this would be the better one to use.

Another avenue you could try is to put a scope on the output and see what the signal shape looks like. If it's taking a significant proportion of the pulse length to settle, that would be a problem. This also enables you to confirm that the output levels are right. The scope is also how you would find out what's happening on that digital output if you can't find the data sheet.

Finally, if the sketch you're testing with has any significant complexity then write a test sketch that does nothing but count interrupts so you can make absolutely certain it isn't just a software issue.

well i dont have a scope, appart, anything i can make with the arduino…
the sketch is as simple code as it can get (just calculates the rpm, and serialprintln them). with the arduinoscope, i can see that the pulses, after 500rpm, stop existing, meaning :
either the arduinoscope cant recognize them cause of high frequency.
or the sensor indeed dosen’t produce anymore…

i didint new that the optical sensors had a maximum sampling rate, at all. but if they do, it seems that this must be the case… :confused:
but, just a pair of IR receiver and emitter, should have enough sampling rate… correct?

the speed sensor i use has the markigns “FC-03” on it, but i cant really find anything on the internet, about it, so i guess its not so good (it was the only think i could find topically), so i guess i should try to find a new one and see how it goes…

for instance : https://www.sparkfun.com/datasheets/Components/GP1A57HRJ00F.pdf this pair of IR emmiter and receiver, do not mention anything about maximum sampling, at all :/

@settra, not sure if that's what u ask but:

int ledPin = 13; // LED connected to digital pin 13
int inPin = 7;   // pushbutton connected to digital pin 7

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin 13 as output
  pinMode(inPin, INPUT);      // sets the digital pin 7 as input
}

void loop()
{
  int val = digitalRead(inPin);
  digitalWrite(ledPin, val);    // sets the LED to the button's value
}

the above sketch is simple, you connect the D0 pin of the FC-03 to a pin in your arduino (in this case 7) and you get to light up that LED on pin 13.

I'm working on a way to count distance using this chip at the moment, so i'll update you with the code when i'm done with it if you wish. :)

I was reading you should not do serial.print within an interrupt service routine, it's very slow, especially at 9600 baud.

that may be your problem?

PaulRogers: I was reading you should not do serial.print within an interrupt service routine, [u]it's very slow[/u]

Since serial output requires interrupts, and interrupts are disabled within an interrupt handler, it can cause the Arduino to lock up waiting for an interrupt which can never occur.