Frequency and duty cycle measurement issues

TL;DR
I can't manage to properly measure duty cycle of a PWM signal above several kHz, and I need to get to about 30kHz.

Hello,

I'm interested in using Arduino for frequency and duty cycle measuring of a PWM signal in a project I'm making. I've read up on the matter and tried about four or five different methods so far but none have met my requirements yet. This is what I need:

  • frequency measurement range: 1Hz to at least 30kHz (I need to get above audible frequencies, each additional kilohertz is a bonus)
  • duty cycle measurement range: at least 20% to 80%, ideally 10% to 90%
    I don't think they're unreasonable but I have problems with the second part. I'll describe the issue but first I'll mention that I tried measurements with pulseIn() which was pretty bad and with attachInterrupt which was much better but bad at frequencies above 10kHz and had the duty cycle issue as well. Currently I am using a custom mishmash of two codes Nick Gammon wrote on the matter (pasting the relevant parts of it at the end of this post), which I believe uses Input Capture ability of the internal timers or something like that. It allows me to go up to 60kHz (Nick Gammon says he managed to push it to 200kHz but I couldn't) which is nice, but has the darned duty cycle measurement issue.

So, what's the problem? Basically, the higher the frequency, the narrower the measurable range of the duty cycle is. If I increase the duty cycle too much, I believe the ISR takes too much time and the Arduino doesn't register the RISING edge so very quickly after the FALLING edge. This results in frequency readout showing roughly half of the true frequency and the duty cycle being not 90% or something like that, but usually around 45%. The same situation happens on the opposite end of the duty cycle range: when I go too low (usually when the pulse is shorter than a few micro seconds, i.e. 10kHz and above), the FALLING edge comes too quickly after RISING edge, the shown frequency goes down and duty cycle read spikes from let's say 15% to 60%. This issue starts appearing at 1600 Hz when I push the DC above 98% (no problem in the lower range of DC yet). The lower range measurement starts to get wonky at 15kHz below about 11% DC while at that frequency the upper range issue appears at only 90%. Finally at 35kHz I can't go above 77% DC or below 22% DC without the issue.

Now, I know that there are libraries that allow Arduino to measure extreme frequencies such as FreqCounter, but those can't measure the duty cycle, which is integral to the project I'm making. The code I have so far has been the best but is just not enough (and I think the ISR is as quick as possible as it is). So I don't believe I can fix this problem (though I'd love to do so). What I'd like to do instead is to find out how to recognise when the measurement is inaccurate. I'd like to read the DC on an LCD, and when it starts to be wonky, show fixed "out of range" instead of "83.56%" or something like that. I have absolutely no idea how to program it that way though: in the case of the issue, I just get half the true frequency, which is not something I can easily detect as an error.

So as it is apparent, I'm stumped and I need some advice on how to proceed. Thanks for any help.

volatile boolean first;
volatile boolean sec;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long fallTime;
volatile unsigned long finishTime;

long int refresh_freq=0, refresh_time=0, refreshLCD=0;

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect

ISR (TIMER1_CAPT_vect)
  {
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = ICR1;  // see datasheet, page 117 (accessing 16-bit registers)
  unsigned long overflowCopy = overflowCount;
  
  // if just missed an overflow
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
    overflowCopy++;
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCopy << 16) + timer1CounterValue;
    TIFR1 |= bit (ICF1);     // clear Timer/Counter1, Input Capture Flag
    TCCR1B =  bit (CS10);    // No prescaling, Input Capture Edge Select (falling on D8)
    first = false;
    sec=true;
    return;  
    }
    
  if(sec)
  {
    fallTime = (overflowCopy << 16) + timer1CounterValue;
    TIFR1 |= bit (ICF1);
    TCCR1B =  bit (CS10) | bit (ICES1);    
    sec = false;
    return;  
  }
    
  finishTime = (overflowCopy << 16) + timer1CounterValue;
  triggered = true;
  TIMSK1 = 0;    // no more interrupts for now
  }  // end of TIMER1_CAPT_vect
  
void prepareForInterrupts ()
{
  noInterrupts ();  // protected code
  first = true;
  sec=false;
  triggered = false;  // re-arm for next time
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  
  TIFR1 = bit (ICF1) | bit (TOV1);  // clear flags so we don't get a bogus interrupt
  TCNT1 = 0;          // Counter to zero
  overflowCount = 0;  // Therefore no overflows yet
  
  // Timer 1 - counts clock pulses
  TIMSK1 = bit (TOIE1) | bit (ICIE1);   // interrupt on Timer 1 overflow and input capture
  // start Timer 1, no prescaler
  TCCR1B =  bit (CS10) | bit (ICES1);  // plus Input Capture Edge Select (rising on D8)
  interrupts ();
}  // end of prepareForInterrupts
  

void setup () 
  {
  Serial.begin(115200);
  prepareForInterrupts ();   
  } // end of setup

void loop () 
  {
  if (triggered)
  {
    unsigned long totalTime = finishTime - startTime;
    unsigned long onTime = fallTime - startTime;
    float freq = F_CPU / float (totalTime);
    float duty = (onTime*100.0)/totalTime; 
	
	Serial.print(freq);
	Serial.print("Hz / ");
	Serial.print(duty);
	Serial.print(" %");
	Serial.print("\n");
    prepareForInterrupts ();
  } 
}   // end of loop

Just an idea:

Use multiple cycles to determine the frequency and duty cycle. Once you got the raw frequency, adjust the prescaler so that no or at most one overflow will ever occur during one sample. This should speed up the ISR, and allow for measuring higher frequencies.

Eventually you also can omit clearing TIFR1.ICF1, which is cleared on capture interrupt execution.

Thanks for your help! I deleted the ICF1 parts and it works so thats great.

I however don't fully follow your suggestion. I am quite new to this area and while I do know what prescalers are I don't know how they're supposed to help me push the duty cycle range further or help with the higher frequencies. As far as I know the overflow happens only once every 4ms which means that it shouldn't interfere with measuring periods in the few micro second range, right? Also what do you mean by using multiple cycles to determine the frequency and duty - if the duty is too high or low, the data I'll get will be inaccurate every time. Could you maybe provide a bit more details? Thank you again!

Can you use a Mega? It has multiple input capture registers, so you could use one for the rising edge and one for the falling edge, by tying the input pins together.

When a period becomes longer than 4ms, the prescaler factor can be increased to /1024 so that an overflow occurs only every 4 seconds (approximately). This setting can be used to perform a first measurement of the signal period, with a resolution of 1/16 ms. Then the prescaler is adjusted for best resolution, and 4 times can be taken (2 rising, 2 falling), resulting in e.g. two pulse widths and a gap width. When the duty cycle is near its limits (0 or 100%), e.g. near 0 will result in an inaccurate or missing interrupt for the end of the pulse, in which case the gap time can be used to determine the accurate duty cycle. Similarly the frequency can be determined from either the rising or falling edges' distance, whatever times could be taken more accurately.

aarg:
Can you use a Mega? It has multiple input capture registers, so you could use one for the rising edge and one for the falling edge, by tying the input pins together.

Sadly I don't have one and I don't know whether that would help because the uC can only service one ISR at a time anyway and I suspect that the few microseconds that the ISR takes are the boon of my measurement.

DrDiettrich:
When a period becomes longer than 4ms, the prescaler factor can be increased to /1024 so that an overflow occurs only every 4 seconds (approximately). This setting can be used to perform a first measurement of the signal period, with a resolution of 1/16 ms. Then the prescaler is adjusted for best resolution, and 4 times can be taken (2 rising, 2 falling), resulting in e.g. two pulse widths and a gap width. When the duty cycle is near its limits (0 or 100%), e.g. near 0 will result in an inaccurate or missing interrupt for the end of the pulse, in which case the gap time can be used to determine the accurate duty cycle. Similarly the frequency can be determined from either the rising or falling edges' distance, whatever times could be taken more accurately.

Thanks for the description. Maybe I am just really stupid at thinking about these things but I don't see how that would help as well, because I don't think the overflows are that problematic anyway. At the several kHz frequencies I wanna operate on they don't happen at all because the program resets the counters every time a full measurement is made, and the full measurement is definitely completed before 4ms elapse with no problems whatsoever, and with signals of period around 4ms (i.e. 250Hz or something like that) I don't get the duty cycle measurement issue either because the overflow compensation part of the ISR works perfectly fine.

Today I've been trying to find a way to detect when the Arduino skips an interrupt trigger but to no satisfactory result. PINB readout method turned out to be slow and couldn't react faster than the interrupt trigger itself anyway so it worked only half the time it should.

Reading PINB is delayed when an interrupt occurs, that's why you can't poll for missed interrupts or edges.

Else a tight loop can poll for events faster than an ISR, due to the overhead on entry and exit of the ISR. You may try to catch the edge time in hardware, but handle it inside loop() instead of the ISR. This will block all other code for that time, but that won't harm if there remains nothing else to do.

A counter IC to count pulses that occur could be used for frequency measurement?
A 16 bit counter could count about 60,000 pulses in a second before overflow (about 60KHz).

Use a parallel to serial converter IC to then feed the 16 bit parallel out to serial for the arduino (unless you have 16 pins available).

A second circuit could increment a counter (with say a 1Mhz crystal) while the PWM signal is high.
The arduino can read the count after say 0.01 seconds. As 1Mhz = one count every 1uS...then you can get the length the PWM signal is HIGH and thus know how long out of the sampled hundredth of a second it is LOW.

Duty cycle done.

An Arduino timer can also be used to count pulses :slight_smile:

DrDiettrich:
An Arduino timer can also be used to count pulses :slight_smile:

I thought he was having issues due to the need to measure 60Khz with a decent resolution.

There probably is a way with the MCUs...but I don't know enough to get them to do that ha.

The 8 bit 16MHz AVR architecture allows to measure times at a resolution of 1/16µs, but external signals only up to about 1/8µs due to synchronization. That's far away from the requested frequencies of >=30kHz (1/30ms).

The major problem is the achievable interrupt rate, for capturing edge times, which is almost limited to 100kHz (10µs). More code, like for overflow handling, results in even slower interrupt rates, not only because of the code inside the ISR, but also for register save/restore for the explicit and temporary variables. This means that the minimum pulse width must be above 10µs, or 1% at 10kHz. That's why the longer of pulse and gap width should be measured, to achieve the 1/8µs resolution, or 0.0125% at 10kHz.

In so far it may be a good decision to use a Mega instead of an Uno, with more timers and capture registers. Then it's sufficient to capture 3 times in hardware, for the first rising, falling, and second rising edge, with arbitrary post processing of these values.

Timing issues can be minimized (or eliminated) by taking 2 extended measurements:

First measure the interval from the first rising edge to the 5th rising edge.

Then measure the interval from the first rising edge to the 5th falling edge.

From the 2 measurements you can get pulse period, width, duty and frequency.
Duty cycle can be anything from 1% to 99% with PWM frequency up to 100kHz or so.

Also, the time taken to enter or leave an interrupt should not create an issue.
Any small timing difference can be corrected for.

What you'll need to avoid is doing this:

Nice idea, but how are you going to count the pulses between measurements?

I decided to give it a try...

Notes:

timer2 creates the pwm signal (62.5kHz, 8-bit).
timer1 does the time measurement with 62.5ns resolution.
Working range for duty is 4-254 (1.6% - 99.6%).
Measures and calculates pwm frequency, width, duty, period.
Add jumper from pin 11 (pwm output) to pin 2 (pwm input).

// Test PWM period and width measurement (connect pin 11 to pin 2)
// Measurement timing diagram: http://i.imgur.com/CQMngnZ.png

const byte pwmInputPin = 2;
volatile byte pwmCount = 0, testState = 0;
volatile word measurePeriod, measureWidth;
float pwmPeriod, pwmWidth, pwmDuty, pwmFrequency;


void setup()
{
  pwmBegin(128);  // range for duty is 4-254
  Serial.begin(115200);
  pinMode(pwmInputPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pwmInputPin), measurePWM, RISING);
  TCCR1A = 0;    // normal operation mode
  TCCR1B = 0;    // stop timer clock (no clock source)
  TCNT1  = 0;    // clear counter
  TIMSK0 = 0;    // for testing with timer0 disabled
}

void loop()
{
  if (testState == 3) {            // testing completed, results are ready
    EIMSK &= ~bit(INT0);           // disable INT0 interrupt
    EICRA |= bit(ISC00);           // change INT0 interrupt mode back to rising
    TCCR1B = 0;                    // stop timer clock (no clock source)
    TCNT1 = 0;                     // clear clock counter
    pwmCount = 0;                  // clear pwm counter

    // calculate and print
    pwmPeriod = (measurePeriod + 0.1875) * 0.0625; // 3 clock cycle correction
    pwmWidth = ((measureWidth - (2 * measurePeriod)) * 0.0625) - 0.1875; // 3 clock cycle correction
    pwmDuty = (pwmWidth / pwmPeriod) * 100;
    pwmFrequency = 1000 / pwmPeriod;
    Serial.print("pwmPeriod     ");
    Serial.print(pwmPeriod, 3);
    Serial.println(" microseconds");
    Serial.print("pwmWidth      ");
    Serial.print(pwmWidth, 3);
    Serial.println(" microseconds");
    Serial.print("pwmDuty       ");
    Serial.print(pwmDuty, 3);
    Serial.println(" %");
    Serial.print("pwmFrequency  ");
    Serial.print(pwmFrequency, 3);
    Serial.println(" kHz");
    Serial.println();

    testState = 0;                 // clear testState
    EIFR |= bit(INTF0);            // clear INT0 interrupt flag
    EIMSK |= bit(INT0);            // enable INT0 interrupt
  }
}

void measurePWM()
{
  switch (testState) {

    case 0:
      TCCR1B |= bit(CS10);        // start timer clock with no prescaler
      pwmCount++;
      testState = 1;
      break;

    case 1:
      if (pwmCount == 1) {
        measurePeriod = TCNT1;    // take pwm period reading
        EICRA &= ~bit(ISC00);     // change INT0 interrupt mode to falling
        testState = 2;
      }
      pwmCount++;
      break;

    case 2:
      if (pwmCount == 3) {
        measureWidth = TCNT1;     // take pwm width reading
        testState = 3;            // all tests done
      }
      pwmCount++;
      break;
  }
}

void pwmBegin(unsigned int duty) {
  TCCR2A = 0;                               // TC2 Control Register A
  TCCR2B = 0;                               // TC2 Control Register B
  TIMSK2 = 0;                               // TC2 Interrupt Mask Register
  TIFR2 = 0;                                // TC2 Interrupt Flag Register
  TCCR2B |= (1 << CS20);                    // no clock prescaler for maximum PWM frequency
  TCCR2A |= (1 << WGM21) | (1 << WGM20);    // Waveform Generation Mode 3
  TCCR2A |= (1 << COM2A1);                  // fast PWM on Pin 11 (OC2A)
  OCR2A = duty;                             // range is 0-255
  pinMode(11, OUTPUT);
}

Better performance using input capture:

timer2 creates the pwm signal (62.5kHz, 8-bit).
timer1 does the time measurement with 62.5ns resolution.
Working range for duty is 1-254 (0.4% - 99.6%).
Measures and calculates pwm frequency, width, duty, period.
Add jumper from pin 11 (pwm output) to pin 8 (pwm input).

// Test PWM period and width measurement (connect pin 8 to pin 2)
// Measurement timing diagram: http://i.imgur.com/CQMngnZ.png

const byte pwmInputPin = 2;
volatile byte testState = 0;
volatile word timerValue[3];
float pwmPeriod, pwmWidth, pwmDuty, pwmFrequency;

void setup()
{
  pwmBegin(1);                     // range for duty is 1-254
  pwmMeasureBegin();
  Serial.begin(115200);
}

void loop()
{
  if (testState == 4) {            // tests completed

    word periodValue = timerValue[1] - timerValue[0];
    word widthValue = timerValue[2] - timerValue[0];
    pwmPeriod = periodValue * 0.0625;
    pwmWidth =  (widthValue - (2 * periodValue)) * 0.0625;
    if (pwmWidth > pwmPeriod)
      pwmWidth -= pwmPeriod;
    pwmDuty = (pwmWidth / pwmPeriod) * 100;
    pwmFrequency = 1000 / pwmPeriod;

    Serial.print("pwmPeriod     ");
    Serial.print(pwmPeriod, 3);
    Serial.println(" microseconds");

    Serial.print("pwmWidth      ");
    Serial.print(pwmWidth, 3);
    Serial.println(" microseconds");

    Serial.print("pwmDuty       ");
    Serial.print(pwmDuty, 3);
    Serial.println(" %");

    Serial.print("pwmFrequency  ");
    Serial.print(pwmFrequency, 3);
    Serial.println(" kHz");
    Serial.println();

    pwmMeasureBegin ();
  }
}

ISR (TIMER1_CAPT_vect)
{
  switch (testState) {

    case 0:                            // first rising edge
      timerValue[0] = ICR1;
      testState = 1;
      break;

    case 1:                            // second rising edge
      timerValue[1] = ICR1;
      TCCR1B &=  ~bit (ICES1);         // detect falling edge on pin D8
      TIFR1 = bit (ICF1) | bit (TOV1); // clear flags
      testState = 2;
      break;

    case 2:                            // first falling edge
      testState = 3;
      break;

    case 3:                            // second falling edge
      timerValue[2] = ICR1;
      testState = 4;                   // all tests done
      break;
  }
}

void pwmBegin(unsigned int duty) {
  TCCR2A = 0;                               // TC2 Control Register A
  TCCR2B = 0;                               // TC2 Control Register B
  TIMSK2 = 0;                               // TC2 Interrupt Mask Register
  TIFR2 = 0;                                // TC2 Interrupt Flag Register
  TCCR2B |= (1 << CS20);                    // no clock prescaler for maximum PWM frequency
  TCCR2A |= (1 << WGM21) | (1 << WGM20);    // Waveform Generation Mode 3
  TCCR2A |= (1 << COM2A1);                  // fast PWM on Pin 11 (OC2A)
  OCR2A = duty;                             // range is 0-255
  pinMode(11, OUTPUT);
}

void pwmMeasureBegin ()
{
  TCCR1A = 0;                               // normal operation mode
  TCCR1B = 0;                               // stop timer clock (no clock source)
  TCNT1  = 0;                               // clear counter
  TIFR1 = bit (ICF1) | bit (TOV1);          // clear flags
  testState = 0;                            // clear testState
  TIMSK1 = bit (ICIE1);                     // interrupt on input capture
  TCCR1B =  bit (CS10) | bit (ICES1);       // start clock with no prescaler, rising edge on pin D8
}

Results with pwm duty set to 1:

Yeah, I think this is probably the best you are going to get before having external hardware.

Johnny010:
Yeah, I think this is probably the best you are going to get before having external hardware.

Made some changes pushing to find upper frequency limit ... reached 250kHz.

timer2 creates the PWM signal (250kHz, 6-bit).
OCR2A is the main clock divider and sets the PWM output frequency.
timer1 does the time measurement with 62.5ns resolution.
Working range for duty is 1-63.
Measures and calculates PWM frequency, width, duty, period.
Add jumper from pin 3 (PWM output) to pin 8 (pwm input).

// Test PWM period and width measurement (connect pin 3 to pin 8)
// Measurement timing diagram: http://i.imgur.com/NqV4nXe.png

const byte pwmInputPin = 2;
volatile byte testState = 0;
volatile word timerValue[4];
float pwmPeriod, pwmWidth, pwmDuty, pwmFrequency;

void setup()
{
  pwmBegin(1);                     // 250kHz PWM, range for duty is 1-63
  TIMSK0 = 0;                      // for testing with timer0 disabled
  pwmMeasureBegin();
  Serial.begin(115200);
}

void loop()
{
  if (testState == 5) {            // tests completed
    noInterrupts();
    word periodValue = timerValue[3] - timerValue[2];
    word widthValue = timerValue[2] - timerValue[1];
    word diffValue = widthValue - periodValue;
    pwmPeriod = periodValue * 0.0625;
    pwmWidth =  (widthValue - (2 * periodValue)) * 0.0625;
    if (pwmWidth > pwmPeriod)
      pwmWidth -= pwmPeriod;
    pwmDuty = (pwmWidth / pwmPeriod) * 100;
    pwmFrequency = 1000 / pwmPeriod;

    Serial.print("pwmPeriod     ");
    Serial.print(pwmPeriod, 3);
    Serial.println(" us");

    Serial.print("pwmWidth      ");
    Serial.print(pwmWidth, 3);
    Serial.println(" us");

    Serial.print("pwmDuty       ");
    Serial.print(pwmDuty, 3);
    Serial.println(" %");

    Serial.print("pwmFrequency  ");
    Serial.print(pwmFrequency, 3);
    Serial.println(" kHz");
    Serial.println();

    interrupts();
    pwmMeasureBegin ();
  }
}

ISR (TIMER1_CAPT_vect)
{
  switch (testState) {

    case 0:                            // first rising edge
      timerValue[0] = ICR1;
      testState = 1;
      break;

    case 1:                            // second rising edge
      timerValue[1] = ICR1;
      TCCR1B &=  ~bit (ICES1);         // capture on falling edge (pin D8)
      testState = 2;
      break;

    case 2:                            // first falling edge
      testState = 3;
      break;

    case 3:                            // second falling edge
      timerValue[2] = ICR1;
      testState = 4;
      break;

    case 4:                            // third falling edge
      timerValue[3] = ICR1;
      testState = 5;                   // all tests done
      break;
  }
}

void pwmBegin(unsigned int duty) {
  TCCR2A = 0;                               // TC2 Control Register A
  TCCR2B = 0;                               // TC2 Control Register B
  TIMSK2 = 0;                               // TC2 Interrupt Mask Register
  TIFR2 = 0;                                // TC2 Interrupt Flag Register
  TCCR2A |= (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);  // OC2B cleared/set on match when up/down counting, fast PWM
  TCCR2B |= (1 << WGM22) | (1 << CS20);     // no clock prescaler for maximum PWM frequency
  OCR2A = 63;                               // TOP overflow value is 63 producing 250 kHz PWM
  OCR2B = duty;
  pinMode(3, OUTPUT);
}

void pwmMeasureBegin ()
{
  TCCR1A = 0;                               // normal operation mode
  TCCR1B = 0;                               // stop timer clock (no clock source)
  TCNT1  = 0;                               // clear counter
  TIFR1 = bit (ICF1) | bit (TOV1);          // clear flags
  testState = 0;                            // clear testState
  TIMSK1 = bit (ICIE1);                     // interrupt on input capture
  TCCR1B =  bit (CS10) | bit (ICES1);       // start clock with no prescaler, rising edge on pin D8
}

Ah sorry! Thought you were using an ATMEL chip with only T0 and T1. Good work.

dlloyd:
Made some changes pushing to find upper frequency limit ... reached 250kHz.

timer2 creates the PWM signal (250kHz, 6-bit).
OCR2A is the main clock divider and sets the PWM output frequency.
timer1 does the time measurement with 62.5ns resolution.
Working range for duty is 1-63.
Measures and calculates PWM frequency, width, duty, period.
Add jumper from pin 3 (PWM output) to pin 8 (pwm input).

// Test PWM period and width measurement (connect pin 3 to pin 8)

// Measurement timing diagram: Imgur: The magic of the Internet

const byte pwmInputPin = 2;
volatile byte testState = 0;
volatile word timerValue[4];
float pwmPeriod, pwmWidth, pwmDuty, pwmFrequency;

void setup()
{
  pwmBegin(1);                    // 250kHz PWM, range for duty is 1-63
  TIMSK0 = 0;                      // for testing with timer0 disabled
  pwmMeasureBegin();
  Serial.begin(115200);
}

void loop()
{
  if (testState == 5) {            // tests completed
    noInterrupts();
    word periodValue = timerValue[3] - timerValue[2];
    word widthValue = timerValue[2] - timerValue[1];
    word diffValue = widthValue - periodValue;
    pwmPeriod = periodValue * 0.0625;
    pwmWidth =  (widthValue - (2 * periodValue)) * 0.0625;
    if (pwmWidth > pwmPeriod)
      pwmWidth -= pwmPeriod;
    pwmDuty = (pwmWidth / pwmPeriod) * 100;
    pwmFrequency = 1000 / pwmPeriod;

Serial.print("pwmPeriod    ");
    Serial.print(pwmPeriod, 3);
    Serial.println(" us");

Serial.print("pwmWidth      ");
    Serial.print(pwmWidth, 3);
    Serial.println(" us");

Serial.print("pwmDuty      ");
    Serial.print(pwmDuty, 3);
    Serial.println(" %");

Serial.print("pwmFrequency  ");
    Serial.print(pwmFrequency, 3);
    Serial.println(" kHz");
    Serial.println();

interrupts();
    pwmMeasureBegin ();
  }
}

ISR (TIMER1_CAPT_vect)
{
  switch (testState) {

case 0:                            // first rising edge
      timerValue[0] = ICR1;
      testState = 1;
      break;

case 1:                            // second rising edge
      timerValue[1] = ICR1;
      TCCR1B &=  ~bit (ICES1);        // capture on falling edge (pin D8)
      testState = 2;
      break;

case 2:                            // first falling edge
      testState = 3;
      break;

case 3:                            // second falling edge
      timerValue[2] = ICR1;
      testState = 4;
      break;

case 4:                            // third falling edge
      timerValue[3] = ICR1;
      testState = 5;                  // all tests done
      break;
  }
}

void pwmBegin(unsigned int duty) {
  TCCR2A = 0;                              // TC2 Control Register A
  TCCR2B = 0;                              // TC2 Control Register B
  TIMSK2 = 0;                              // TC2 Interrupt Mask Register
  TIFR2 = 0;                                // TC2 Interrupt Flag Register
  TCCR2A |= (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);  // OC2B cleared/set on match when up/down counting, fast PWM
  TCCR2B |= (1 << WGM22) | (1 << CS20);    // no clock prescaler for maximum PWM frequency
  OCR2A = 63;                              // TOP overflow value is 63 producing 250 kHz PWM
  OCR2B = duty;
  pinMode(3, OUTPUT);
}

void pwmMeasureBegin ()
{
  TCCR1A = 0;                              // normal operation mode
  TCCR1B = 0;                              // stop timer clock (no clock source)
  TCNT1  = 0;                              // clear counter
  TIFR1 = bit (ICF1) | bit (TOV1);          // clear flags
  testState = 0;                            // clear testState
  TIMSK1 = bit (ICIE1);                    // interrupt on input capture
  TCCR1B =  bit (CS10) | bit (ICES1);      // start clock with no prescaler, rising edge on pin D8
}



![|344x500](http://i.imgur.com/eCbbdex.png)

Hi, I need this code to measure duty cycle of a 2.4kHz signal on pin 8 but I do not need the part in this code that generates the PWM signal.
I am a total novie in direct register programming, so I need help to modify this code.

Thanks for any assistance!!