Faster frequency measurement

I'm using this library with a Pro Mini: FreqCount Library, for Measuring Frequencies in the 1 kHz to 5 MHz Range. Using this library, I can only reliably count up to about 7.7 MHz. I need to count up to at least 12.8 MHz. I thought about using a D flip-flop to divide by two, but somehow that seems like a kludge. I'd like to do it all in software.

From Googling, it seems that there are ways to do this faster, using C or assembler with a straight AVR chip. However, I don't know how to incorporate such routines into an Arduino program.

Alternately, is there a way to have the Arduino do the divide by two by using a spare digital input for the 12.8 MHz and outputting 6.4 MHz on another pin? The 6.4 MHz is slow enough that it could be fed to Pin 5, using the above library.

Can anyone help me out with either a faster frequency counter library, or a way to do the divide by two?

16 MHz is 1 clock cycle, 8 MHz is 2 clock cycles, so you can't really measure 12.8MHz directly as that is 1.5 clock cycles.
You'd have to count # of interrupts over some longer time span and then do the math.
Since it takes ~4uS to service an interrupt, that is not going to give good results either with edges being missed.
You really need to use the hardware counters, let them run for some known time period and do the math from there.
See section 16 of the datasheet.

While I don't fully understand the low level stuff, the library I am using is counting input transistions. The first line on that page says "FreqCount measures the frequency of a signal by counting the number of pulses during a fixed time." This sounds like what you are telling me to do. The problem is, it isn't doing it fast enough for my needs.

Well, open the library & see how it's doing it.
Many libraries don't take full advantage of the '328P hardware, like not using SPI and bit banging the pins instead.

I did look at the library, hence my statement "I don't fully understand the low level stuff". If you can decipher what that library is doing and figure out how to make it count higher frequencies, please do.

For now, I did use a flip-flop as a divider and it is working fine. If possible, I would like to eliminate the extra hardware.

Paul writes some tight software - I don't know enough about the low level stuff to able to improve it.

After further research, it seems that the AVR chips can't measure a frequency higher than 1/2 of the uC clock speed (I'm getting 7.7 MHz, which is almost half). So, I guess I'll have to stick with using a flip-flop as a divider.

The SN74LV8154 is a very handy dual 16bit counter chip that can be read
reliably as its changing (and daisy-chained to a 32 bit counter). Its available
in DIP and SMT, but has a byte-wide parallel output bus.

I've used it before in a frequency counter circuit, doing parallel->serial
conversion with a shift register to save Arduino pin count.

The SN74LV8154 is good to 40MHz.

I can provide a schematic using it if interested....

you might want to check - http://interface.khm.de/index.php/lab/experiments/arduino-frequency-counter-library/ - too

..cpld based.. :slight_smile:
http://forum.arduino.cc/index.php?topic=120220.msg904618#msg904618

If you must do this, I would recommend using an external chip, either the one recommended above by MarkT or find a Frequnecy/Voltage converter IC (Or Voltage/Frequency, they often work in reverse, but double check before buying it), or some other external device to measure your frequency. As you noticed, the arduino cannot handle that itself very well. You could try to do something by intentionally using aliasing/decimation, but the math involved with that can be fairly complicated. That also involves using DSP practices to just find the frequency. I also imagine that will take up a vast majority of your processing time as well.

MarkT:
The SN74LV8154 is a very handy dual 16bit counter chip that can be read
reliably as its changing (and daisy-chained to a 32 bit counter). Its available
in DIP and SMT, but has a byte-wide parallel output bus.

I've used it before in a frequency counter circuit, doing parallel->serial
conversion with a shift register to save Arduino pin count.

The SN74LV8154 is good to 40MHz.

I can provide a schematic using it if interested....

Hi Mark,

I'm trying to use SN74LV8154 to count two sequences of pulses. It seems you are the only one using this chip in the forum. Do you still have the schematic? I don't know how I should connect the RCLK pin.

BTW, in another post, you mentioned "What you don't do is reset the counters - that will lose counts, you take successive readings from each counter and subtract the previous reading to get the new count." In that case what if the counter reaches its limit? Will it roll back to 0 automatically?

Thanks

you have a resolution of something like 3.89 mV for dV/dt using a mega328 as on an uno board, regardless of the clock speed So if dV / dt is say periodically oscillating with a smaller amplitude it will not be able to measure the change.

And as someone mentioned it takes 4 microseconds to service an interrupt, so having any attempt to measure a period between pulses less than that, or increasing the clock speed, would not achieve anything due to this being a limitation of the way the MCU operates, ie the way it initializes each loop of the sketch it;s programmed with, and how it keeps count of its position relative to the beginning of the current loop of the sketch main it is executing.

I have been curious about the same thing and like you wanted to see what i could get software wise in terms of accuracy/frequency range but its like someone initially told me you WILL need a nice oscilloscope. so hence the return to uni lol

From Googling, it seems that there are ways to do this faster, using C or assembler with a straight AVR chip. However, I don't know how to incorporate such routines into an Arduino program.

get an ICSP programmer like USBasp.

download something like AVRdudess 2.1

put an appropriate MCU in an appropriate arduino board

go to preferences in the arduino IDE and make sure that the display window is checked to showing everything when the sketch is compiled, so that you can copy the destination on your hard drive it spits out a hex file.

copy this hex file's target into the "flash" window of the AVR software you downloaded.

connect the ICSP programmer to the USB of the PC, and its ICSP cable to the arduino board housing your target chip. there should be an option on the interface for the AVR software to detect the chip's memory etc.

hit program.

take the MCU chip off the board and into the circuit you wanted it for.

but keep in mind your "stand alone" AVR chip is only going to out perform what it does on the arduino board if the circuit you are putting it into allows it to, im not going to say impossible, but hmm im skeptical about it being better i mean im pretty sure if can be done it would have been done by the original designers of the arduino boards, there is huge range of them now so.. alot of prototyping has been done already...

Basic rule is you need to sample at speed at least double what you want to read. So for 12.8MHz, you need to sample at 25.6MHz minimum. You also have to look at all the other timing issues. An external chip is probably best for this.

Nyquist

Where I work, we use a factor of 5:1 for sampling.

xhr0428:
Hi Mark,

I'm trying to use SN74LV8154 to count two sequences of pulses. It seems you are the only one using this chip in the forum. Do you still have the schematic? I don't know how I should connect the RCLK pin.

BTW, in another post, you mentioned "What you don't do is reset the counters - that will lose counts, you take successive readings from each counter and subtract the previous reading to get the new count." In that case what if the counter reaches its limit? Will it roll back to 0 automatically?

Thanks

Hello,

I doubt this is still an issue for you, given the age of this thread. But i also had some trouble reading the SN74LV8154 counter.

Got it to work eventualy and here is the code, hope it saves someone some time if they stuble upon this post.

#include "SPI.h";

bool done = false;

// Counter pins (SN74LV8154)
const int cclr = 8;
const int clka = 9;
const int gal = 7;
const int gau = 6;
const int gbl = 5;
const int gbu = 4;
const int rclk = 3;
// all other non-Y pins pulled to GND

// Shift register pins (SN74HC165)
const int shLd = 2;
const int clk = 13;  // not used in code, pre-defined SPI pin in arduino UNO/Nano for clock signal
const int qh = 12;   // not used in code, pre-defined SPI pin in arduino UNO/Nano for data
// all other A-H pins to output of counter Y0 -> A, Y1 -> B, Y2 -> C etc...

void setup() 
{
  // Counter pins
  pinMode(cclr, OUTPUT);
  pinMode(clka, OUTPUT);
  pinMode(gal, OUTPUT);
  pinMode(gau, OUTPUT);
  pinMode(gbl, OUTPUT);
  pinMode(gbu, OUTPUT);
  pinMode(rclk, OUTPUT);
  digitalWrite(gal, HIGH);
  digitalWrite(gau, HIGH);
  digitalWrite(gbl, HIGH);
  digitalWrite(gbu, HIGH);
  digitalWrite(cclr, LOW);
  digitalWrite(clka, LOW);
  digitalWrite(rclk, LOW);
  
  // Shift register pins (others are handled by SPI lib)
  pinMode(shLd, OUTPUT);
  
  Serial.begin(115200);
  SPI.begin();
}

void pulse(int pin, int value)
{
  digitalWrite(pin, value);
  digitalWrite(pin, !value);
}

void loop() 
{
  if(!done)
  {
    Serial.println("start");

    // Clear counter
    pulse(cclr, LOW);

    // Some test clock cycles, 513 = 10 00000001
    for(int i = 0; i < 513; i++)
    {
      pulse(clka, HIGH);
    }
    
    // Transfer data from the counter to the register
    pulse(rclk, HIGH);

    // NOTE: counter can continue counting now, it will not effect the value in the register

    // Read low byte of register A
    digitalWrite(gal, LOW);    // Set low byte of register A to outputs
    pulse(shLd, LOW);          // Sample outputs data with shift register
    byte bl = SPI.transfer(0); // Read data from shift register
    
    // Read upper byte of register A
    digitalWrite(gal, HIGH);   // Prevent invalid state, only one ga_ may be LOW at any given time
    digitalWrite(gau, LOW);    // Set upper byte of register A to outputs
    pulse(shLd, LOW);          // Sample outputs data with shift register
    byte bu = SPI.transfer(0); // Read data from shift register
    
    Serial.println(bl);  // should be 1
    Serial.println(bu);  // should be 2
    int val = (bu << 8) + bl;
    Serial.println(val);  // should be 513
    Serial.println("end");
    done = true;
  }
}

A couple things from my past.
One is to have a frequency reference of say 10MHz that you
feed into a balanced mixer with your unknown.
You use a low pass filter to pick out the lower side band and
measure that.
If you had a digitally tuned reference, you could cover bigger spans.

Another way we used to measure short time spans was to used a
constant current source to charge a capacitor. We could digitally
allow charging and not charging with a shunt load and diode
blocking. If the diode was reverse voltage to the capacitor, no
charging. if forward, it would charge.
We used this to measure time spans of single pulses down into the
sub ns range, once calibrated but only to about +- 2 digits.
Just some thoughts.
Dwight