Problem with Pulse Measurement

I`m having problems with pulse measurement on the Arduino using the Timer Capture Interrupt. I have the following sketch:

volatile uint16_t pulse_width;

ISR(TIMER1_CAPT_vect){
  pulse_width = ICR1;
  
  int x = digitalRead(8);

  if(x == 1){
    //set to trigger on falling edge
    TCCR1B &= B10111111;
  } else {
    //rising edge triggers next
    TCCR1B |= B01000000;    
  }

  Serial.println(x);
}


void setup() {
  Serial.begin(19200);

  //enable overflow and input capture interrupts
  TIMSK1 = B00100000;
  /*
  0
  0
  ICIE1 - 1 - Input Capture Interrupt Enable
  0
  0
  0
  OCIE1B: - 1 - Timer/Counter1, Output Compare B Match Interrupt Enable
  OCIE1A: - 0 - Timer/Counter1, Output Compare A Match Interrupt Enable
  TOIE1: - 0 - Timer/Counter1, Overflow Interrupt Enable
  */
  
  TCCR1B = B01000010;
  /* 
  ICNC1: 1 - Turn on Noise Canceller (4 System Clock Pulses)
  ICES1: 1 - Input Capture Edge Select (Rising to Falling)
  0
  0
  0
  CS12 - 0 - Prescaler at 8
  CS11 - 1 - Prescaler
  CS10 - 0 - Prescaler
  */
  
  DDRB = 0;
  // Set all Port B as Input
  
  sei();
  // Enable Global Interrupt
}

void loop() {
  
}

The source signal is on Digital Pin 8 (Port B, 0)

Ive gone right back to basics, as I realised that pulse widths weren't getting measure correctly, so ive stripped the sketch right down to the basics, but its still not working.

At the moment, i`ve changed the source signal from the pulses I want to measure, to the clock from the external chip, as I know what the results should be and it enables me to see if it's working properly.

As it's measuring the clock, I would expect to see printed

1 0 1 0 1 0 etc

However, what i`m actually getting is:

1 0 0 1 0 1 1 0 1 1 1 0 0

which seems essentially random.

What I don't understand is why its triggering multiple times against a particular edge, rather than triggering on the rising edge, then falling edge, then rising edge etc

The clock pulses are 4uS long, so shouldn't be too fast for the arduino to handle. I have the clock pulses shown on an oscilloscope and they are as expected.

I`ve tried the noise canceller both on and off, and it doesn't make a difference to the result.

If anyone can shed some light on this it would be much appreciated! :)

Serial.println(x);

Don't do this in an interrupt. Your interrupt probably takes more than 1 period to execute and you're getting over-run with interrupts. 4 us is a very short period compare to the length of time .println takes.

In fact, you're going to have trouble keeping up regardless. 4 us is only 64 system clock ticks. Getting into your interrupt routine might be taking about 100 system clock ticks, plus execution, plus you want to do this twice in 4 us (rising and falling).

As Mitch says, you don't want that serial print statement in the interrupt, you also don't need the digitalRead, you can tell which edge it triggered on by checking the ICES1 bit of the TCCRB register.

But why not use pulseIn to measure the pulse, it's a lot easier to use than input capture

Thanks for the replies.

I did wonder if the Serial.print was causing it - I put it in there for debugging. If I remove it, I then have the problem that I can't see the data lol I`ll give it a try though and try and store a load of data and then display it after a certain amount of time.

Each rising or falling pulse is 4us, the combined rising and falling is 8us, so hopefully it won't be too much.

I did try using PulseIn() but had the problem with switching from a rising to falling edge and vice versa. If pulseIn() is set to look for a rising edge, and then you switch it to falling, it will wait until the edge rises and then falls before measuring. As a result it was missing edges (apparently it was changed to work this way at some point as it had problems with reading a falling straight after a rising etc.

I`ll give it a try again with out the Serial, just wish there was an easy way to debug! Thanks again.

The trick is to calculate something in the interrupt routine, store it in a variable, and then in the main loop print out the info over serial port. You'll miss some events, but you won't hold up the interrupt from executing quickly.

Anyway, at 8us I still think it's tight. You'll know it's tight if the main loop seems unresponsive (because close to 100% of your execution time will be spent in the interrupt routine).

Actually, the trick is to use the timer to do the measurement for you in hardware. But Input capture is not easy to explain and you may need to read the data sheet a few times to figure it out. But the principle is to reset TCNT on the edge that starts your pulse save the value of TCNT in the interrupt handler on the other edge. You probably will need a circular buffer to hold the data.

There was an example in the library posted in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1228137503

here is another example from a library that measures the pulse width of radio time signals

/* ICR interrupt vector */
ISR(TIMER1_CAPT_vect){
  TCNT1 = 0;                            // reset the counter
  if( bit_is_set(TCCR1B ,ICES1)){       // was rising edge detected ?   
    icrPulseCount = ICR1/256;  
  }
  else {                                // falling edge was detected    
    icrGapCount = ICR1/256;        
      if(icrGapCount > DCF_GAP_MIN_COUNT) 
         flags.endOfMinute = true; 
  }       
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

If you are not familiar with these techniques you may want to start with an easier project.