Pages: [1]   Go Down
Author Topic: Frequency Counter with DutyCycle Measurement  (Read 4619 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

Worked this weekend on a frequency counter that also reports the duty cycle. Several variations passed and the current proto is working quite well. 
The sketch measures the frequency from 5Hz to 30KHz (~1%) and the duty cycle quite well from 5Hz to 1000Hz.
Above 1000 Hz effects come in that reduces the working range of the duty cycle measurement. I do not understand the cause here yet.

Todo:
- compare the sketch with a logic analyser.
- investigate the cause of the drop in working range at higher frequencies.
- wrap it in a box smiley-wink

As frequency generator I used an Arduino with HW timer for medium frequencies (500 - 30KHz) and an Arduino using (micro)delays for low frequencies (5 - 2000Hz)

As always remarks are welcome

Code:
//
//    FILE: freqCounter06.ino
//  AUTHOR: Rob Tillaart
//    DATE: 2013-04-13
//
// PURPOSE: freq counter
//
// tested working range
//
// FREQ   MIN DC   MAX DC
// -----------------------
// 5       1%      98%
// 10      1%      98%
// 50      1%      98%
// 100     1%      98%
// 250     1%      98%
// 500     1%      98%   
// 1000    1%      98%
// 2500    3%      96%
//  5K     6%      94%
// 10K    12%      88%
// 15K    17%      82%
// 20K    23%      77%
// 25K    29%      70%
// 30K    35%      65%
//

#define DISPLAY_INTERVAL   1000000L
#define IRQPIN             2

volatile unsigned int count = 0;
volatile unsigned int ovf = 0;

unsigned long frequency = 0;
unsigned long highCount = 0;
unsigned long totalCount = 0;
float dutyCycle = 0;
unsigned long lastDisplay = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("* Frequency Counter 0.6 *");
  Serial.println(" ");
  Serial.println("Connect pulse TTL to Arduino pin 2");
  Serial.println("Connect pulse GND to Arduino GND");

  pinMode(IRQPIN, INPUT);
  attachInterrupt(0, pulse, CHANGE);

  TIMSK1 = (1 << TOIE1); // timer overflow interrupt enabled
  TCCR1A = 0x00; //
  TCCR1B = 1; // prescaler == 1 => timer runs at clock speed: 16 MHz
}

void loop()
{
  uint32_t now = micros();
  if (now - lastDisplay > DISPLAY_INTERVAL)
  {
    lastDisplay = now;

    cli();
    {
      frequency = count;
      count = 0;
      totalCount = highCount;
      highCount = 0;
    }
    sei();

    frequency /= 2;
    dutyCycle = totalCount/160000.0;

    Serial.print(frequency);
    Serial.print(" Hz. - ");
    Serial.print(dutyCycle, 1);
    Serial.println(" %");
  }
}


ISR(TIMER1_OVF_vect)
{
  ovf++;
}

void pulse()
{
  if (PIND & 0x04)
  {
    TCNT1 = 0x0000;
    ovf = 0;
  }
  else
  {
    highCount += TCNT1;
    highCount += ovf*65536L;
  }
  count++;
}
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 3
Posts: 39
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
>'- investigate the cause of the drop in working range at higher frequencies.'
some ideas to investigate
- serial can have latencies and buffer overruns plus it can 'conflict' with the ISR (--> missed signal flanks). That´s why people try to store data on SD for some interval, then stop measurement and transmit the collected data via serial (see Storage forum)
- signal quality: check with an oscilloscope, if the signal flanks are clear or somehow floating
- cross check, if the ISR is really triggered on each change; you may get doubled or missing ISRs  (e.g. I some time crosschecked this by inverting a digital out pin every time the ISR was triggered and compared this on a second osci channel with the input square signal)
- how long does it take your arduino function generator to switch high<->low ? Usually the flank will also take some ns to µs (there will be no no-time 'jump')

Best, Robert


Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks, that are good points!
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Did some math last night:

If I have a 30KHz signal the interrupt is called 60.000 times per second (rising/falling)
If the dutyCycle is 50% the timing is always 16.6 usec between rise and fall and rise etc
if the dutyCycle is 35% the timing is (approx) 11 usec (rise) and 22 (fall)

if I have a 10KHz signal the interrupt is called 10.000 times per second
if the dutycycle is 10% the timing is (approx) 10 usec (rise) and 90 (fall).

So my conclusion is that the current (ISR) code gets (reentry) problems when the smallest period of the PWM hits the 10-12 usec.

I see a partial solution in removing the overflow*65536L part ==> Time to tinker smiley-wink
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 3
Posts: 39
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Good calculation:-)
You could measure the code with a separate routine, where you run your code wrapped with toggling a digital out pin and see with a scope how long it takes to toggle
Logged


Pages: [1]   Go Up
Jump to: