How to extend readind range of frequency and duty on mega?

Based on PWM frequency and duty cycle using input capture on ATmega2560 - #5 by MasterT I wrote the following test. I hope to measure frequency and duty cycle from 1Hz to 1Mhz but the technics used mesure from 1Hz to ~ 86KHz only. Any idea about how to extend the range of frequency and duty from 1Hz to 1MHz ? thanks.

// Frequency & duty cycle measurement (input D48 -ICP5- on mega)
// -------------------------------------------------------------

volatile int32_t period_hi = 0;
volatile int32_t period_lo = 0;
volatile uint8_t tmr_overf = 0;
volatile boolean end_capture = 0;
float duty_cycl;
float freq_cntr;

void setup()

void loop()
  if (end_capture){      
    duty_cycl = float(period_hi) / float(period_hi + period_lo) * 100.0;
    freq_cntr = 16000000.0 / float(period_hi + period_lo);

    Serial.print("Freq: ");
    Serial.print(freq_cntr, 2);      
    Serial.print(" - duty: ");
    Serial.println(duty_cycl, 1);      
    end_capture = false;

void init_T5(void)
  TCCR5A = 0;
  TCCR5B = 0;

  TCCR5B |= (1<< CS50); // set prescaler to 16 MHz

  TCCR5B |= (1<<ICNC5); // input noise canceler on
  TCCR5B |= (1<<ICES5); // input capture edge select (lo-hi) 

  TIMSK5 |= (1<<TOIE5); // Overflow Interrupt Enable 
  TIMSK5 |= (1<<ICIE5); // InCaptureInterrupt Enable   

ISR(TIMER5_OVF_vect) {

  static uint16_t last_v = 0;
  uint16_t curr_v = ICR5;
  uint32_t accuml = 0;

  accuml  = curr_v + tmr_overf *65536UL;  
  accuml -= last_v;    
  last_v  = curr_v;
  tmr_overf = 0;        

  if(TCCR5B &  (1<<ICES5)) {  // lo-hi
    TCCR5B &= ~(1<<ICES5);    // input capture edge select (hi-lo) next 
    period_lo = accuml;
    end_capture = true;       
  else {  // hi-lo
    TCCR5B |= (1<<ICES5);    // input capture edge select (lo-hi) next 
    period_hi = accuml;

According to Nick Gammon's discussion of this measurement technique You are running into limitations due to interrupt latency.
You will need to move to faster processor.

This "interrupt latency" does not make sense for me. In another aplication I am generating an very accurated 4MHz PWM with same hardware. So why would not be possible to read duty cycle from 1MHz square wave ?

Mybe there is a solution if one separated timer would be used dedicated to measure only low level of input wave. But I am not sure it works. Any suggestion will be welcome...

The broad answer is, because there is only one input capture register. So you can only capture one edge transition at a time, that is independent of the service routine. To measure a complete pulse, you have to re-initialize the ICP for the second event. That takes processor time.

What if you wire ICP1 and ICP5 togheter and use Timer1 for frequency and Timer5 for duty ?

Oh, right the Mega has more than one. But no, you would use T1 for rising edge and T5 for falling edge (or the reverse)...

You didn't mention that the frequency was variable before...

I am genetaring succesfully PWM from 1Hz to 1MHz. The chalenge now is reading it with both values: frequency and duty...

The problem with the low frequency measurement using ICP is timer overflow. Otherwise frequency is just a measurement of same edge polarity, duty cycle a measurement of different edge polarity once you know the period from the previous step.

I guess you also realize that CPU based time measurements depend on an accurate CPU clock, which the Mega doesn't really have...

Usually this problem can be passed counting how many overflows happend from one transition to another (n_ovf). Then total count is n_ovf * 65365 + count

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.