Input capture behavior

I’ve put together this input capture sketch.

// input capture from Arduino.cc http://forum.arduino.cc/index.php?topic=40748.msg297609#msg297609

// one-shot code https://wp.josh.com/2015/03/05/the-perfect-pulse-some-tricks-for-generating-precise-one-shots-on-avr8/

/******* Sketch to test Input Capture interrupt handling *********************************
  functionality: measure length of pulses on the ICP pin

  Pulses provided by the one-shot code from Josh

  Version of 3/25/19 appears to work and resolved pulse lengths as short as 41 clock cycles - woot!

**************************************************************************************************************/
#define icpPin 8        // ICP input pin on arduino

// some variables to help see that something is happening in the interrupt handlers
volatile unsigned int Value;     // this stores the current ICR1 value (legacy)
volatile unsigned int eVal;     // negative transition ICR1 value
volatile unsigned int sVal;     // positive transition ICR1 value
unsigned int index;

uint8_t Overflows;

/* Overflow interrupt vector */
ISR(TIMER1_OVF_vect) {                // here if no input pulse detected
  Overflows++;                       // increment overflow count
}

//-------------------------------------------------------------------
// Interrupt Capture vector

ISR(TIMER1_CAPT_vect) {

  if ( bit_is_set(TCCR1B , ICES1)) {    // was rising edge detected ?
    sVal = ICR1;                        // save the start time
  }
  else {                                // falling edge was detected
    eVal = ICR1;                        // save the end time
  }
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}
//--------------------------------------------------------------------
// One shot generator vars / setup

#define OSP_SET_WIDTH(cycles) (OCR2B = 0xff-(cycles-1))

unsigned char o = 42; // generate a pulse of 'o' clock cycles
// 42 seems to be the absolute minimum resolution

unsigned int t; // measured pulse time.

// Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

void osp_setup(uint8_t cycles) {

  TCCR2B =  0;      // Halt counter by setting clock select bits to 0 (No clock source).
  // This keeps anyhting from happening while we get set up

  TCNT2 = 0x00;     // Start counting at bottom.
  OCR2A = 0;      // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
  // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to
  // MAX and then overflow back to 0 and get locked up again.
  OSP_SET_WIDTH(cycles);    // This also makes new OCR values get loaded from the buffer on every clock cycle.

  TCCR2A = _BV(COM2B0) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // OC2B=Set on Match, clear on BOTTOM. Mode 7 Fast PWM.
  TCCR2B = _BV(WGM22) | _BV(CS20);        // Start counting now. WGM22=1 to select Fast PWM mode 7

  DDRD |= _BV(3);     // Set pin to output (Note that OC2B = GPIO port PD3 = Arduino Digital Pin 3)
}

// Setup the one-shot pulse generator

void osp_setup() {
  osp_setup(1);
}

// Fire a one-shot pulse. Use the most recently set width.

#define OSP_FIRE() (TCNT2 = OCR2B - 1)

// Test there is currently a pulse still in progress

#define OSP_INPROGRESS() (TCNT2>0)

// Fire a one-shot pulse with the specified width.
// Order of operations in calculating m must avoid overflow of the uint8_t.
// TCNT2 starts one count lower than the match value becuase the chip will block any compare on the cycle after setting a TCNT.

#define OSP_SET_AND_FIRE(cycles) {uint8_t m=0xff-(cycles-1); OCR2B=m;TCNT2 =m-1;}

//---------------------------------------------------------------
void setup() {
  Serial.begin (115200);
  pinMode(icpPin, INPUT);         // ICP pin (digital pin 8 on arduino) as input
  pinMode(7, OUTPUT);             // telltele LED

  TCCR1A = 0 ;                    // this register set to 0!
  TCCR1B = _BV(CS10);             // NORMAL MODE!!, prescaler 1, rising edge ICP1 - this works (prescaler was 2)
  TCCR1B |= _BV(ICES1);           // enable input capture
  TIMSK1 =  _BV(ICIE1);           // enable input capture interrupt for timer 1
  TIMSK1 |= _BV(TOIE1);           // enable overflow interrupt to detect missing input pulses

  // one-shot generator
  osp_setup(); // Initialize one-shot code
  // setup TC1 for input capture
  TCCR1A = 0;
  TIFR1 = (1 << ICF1); /* clear input capture flag */
  TCCR1B = 0x41;      /* capture on rising edge, enable clock with no prescaling */

  Serial.print("Finished setup\r\n");
}

void loop() {

  OSP_SET_AND_FIRE(o); // Generate a one-shot with the width, in cycles, specified by o.
  //  Serial.print("\t");
  //  Serial.print("index ");
  //  Serial.print(index);
  index += 1;  // commenting/uncommenting print statements messes with the reported value
  //  Serial.print("\t\t");
  //    Serial.print(eVal);
  //  Serial.print("\t");
  Serial.println(eVal - sVal);
  //  }
  //  Serial.println(" clocks registered");

  delay(1000);             // wait 1 second for next update
}

The pulses for measurement are generated by TCC2 using this code and have a claimed resolution down to one clock cycle width. Though I can’t verify width I set up a hardware S-R latch to verify pulses are produced. It’s working after a fashion because the code shown reports pulse widths equal to the value set, at least down to 41 clocks.

However, setting the pulse width to less than ~42 clocks breaks the thing, the reported pulse width goes wildly large and random. I’m guessing this is an execution time issue? Commenting/uncommenting various of the print statements preceding [color=blue]Serial.println(eVal - sVal);[/color] also results in random values reported.

Keeping in mind my interpretation may be faulty, according to an assembly dump it should take about 50 and 45 clocks respectively for rising edge/falling edge interrupt processing to complete (not an in depth examination). I don’t know if it’s significant but, this caught my attention. Based on the instruction cycle times, it’s 21 clocks into the capture ISR before the capture register is read for either rising or falling. Is this where the magical 42 (21+21) clock boundary is set?

Am I missing something about now interrupt processing relates to the main line code?

What might be the minimum resolvable pulse width?

The capture code, just in case:

// Interrupt Capture vector

ISR(TIMER1_CAPT_vect) {
 4d0:	1f 92       	push	r1
 4d2:	0f 92       	push	r0
 4d4:	0f b6       	in	r0, 0x3f	; 63
 4d6:	0f 92       	push	r0
 4d8:	11 24       	eor	r1, r1
 4da:	8f 93       	push	r24
 4dc:	9f 93       	push	r25

  if ( bit_is_set(TCCR1B , ICES1)) {    // was rising edge detected ?
 4de:	80 91 81 00 	lds	r24, 0x0081	; 0x800081 <__TEXT_REGION_LENGTH__+0x7e0081>
 4e2:	86 ff       	sbrs	r24, 6
 4e4:	09 c0       	rjmp	.+18     	; 0x4f8 <__vector_10+0x28>
    sVal = ICR1;                        // save the start time
 4e6:	80 91 86 00 	lds	r24, 0x0086	; 0x800086 <__TEXT_REGION_LENGTH__+0x7e0086>
 4ea:	90 91 87 00 	lds	r25, 0x0087	; 0x800087 <__TEXT_REGION_LENGTH__+0x7e0087>
 4ee:	90 93 31 01 	sts	0x0131, r25	; 0x800131 <sVal+0x1>
 4f2:	80 93 30 01 	sts	0x0130, r24	; 0x800130 <sVal>
 4f6:	08 c0       	rjmp	.+16     	; 0x508 <__vector_10+0x38>
  }
  else {                                // falling edge was detected
    eVal = ICR1;                        // save the end time
 4f8:	80 91 86 00 	lds	r24, 0x0086	; 0x800086 <__TEXT_REGION_LENGTH__+0x7e0086>
 4fc:	90 91 87 00 	lds	r25, 0x0087	; 0x800087 <__TEXT_REGION_LENGTH__+0x7e0087>
 500:	90 93 33 01 	sts	0x0133, r25	; 0x800133 <eVal+0x1>
 504:	80 93 32 01 	sts	0x0132, r24	; 0x800132 <eVal>
  }
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
 508:	90 91 81 00 	lds	r25, 0x0081	; 0x800081 <__TEXT_REGION_LENGTH__+0x7e0081>
 50c:	80 e4       	ldi	r24, 0x40	; 64
 50e:	89 27       	eor	r24, r25
 510:	80 93 81 00 	sts	0x0081, r24	; 0x800081 <__TEXT_REGION_LENGTH__+0x7e0081>
}
 514:	9f 91       	pop	r25
 516:	8f 91       	pop	r24
 518:	0f 90       	pop	r0
 51a:	0f be       	out	0x3f, r0	; 63
 51c:	0f 90       	pop	r0
 51e:	1f 90       	pop	r1
 520:	18 95       	reti

Update: It’s not as random as I thought. It prints a pair of counts the same then the next pair are different from the previous, but the same as each other. ??

Capture print.PNG