Frequency in (count) and frequency out on Nano

Hi there.

I'm new to the Arduino arena, and is looking for a bit of guidance.

My project involves a simple read, compute and output routine.

I would like to read a TTL frequency signal, range 0 to 400Hz, and depending on what the frequency is, I want to output another frequency, apply a scale or output a fixed frequency when input is out of range, also 0 to 400Hz, give or take.

I thought outputting the signal using one of the PWM ports, and modifying the timer would do the trick for the output, but am afraid that timers will be interrupted while doing the frequency input reading.

For the application I need, interrupting the output, or turning off while reading the input is unacceptable. I would like to update the output every 200mS or so, so nothing really fancy. Accuracy to 5Hz or better.

I'm struggling to find the appropriate libraries for use in a Nano.

Would some of you be able to point me in the right direction?

If you search for the nano versions 3.0+ have the same processor as the Uno, AtMega328

The PWMs are a hardware feature, they run in the background. Unless you specifically alter the configuration of that PWM it won't be affected by what the processor is doing.

CommonRodent:
The PWMs are a hardware feature, they run in the background. Unless you specifically alter the configuration of that PWM it won't be affected by what the processor is doing.

http://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM

I was of the understanding that tampering with hardware timers (to modify the PWM base frequency) could affect other functions also using the timers. At the same time, I read that some of the frequency input libraries also modify, or at least utilize some of the timers, and at times disable others.

Maybe I'm wrong.

The http://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM lacks proper formatting, some headings and tables are shown as normal text. making in detail the tables unreadable. Can somebody fix that?

@DKGuffe: Each of the 3 general-purpose timers supports some (2?) PWM outputs. When another library uses an timer for its own purposes, this will affect the PWM outputs bound to that timer. E.g. millis() and other timing functions use timer0, tone() will use the timer related to pins 3 and 11. UART and I2C seem to use additional (independent, specialized) timers for their baudrate and frequencies, but I suspect that the SoftwareSerial and more libraries use one of the general-purpose timers. I'm still trying to find out more about the usage of timers and other shared resources...

if you need more timer-related functionality, you can use e.g. an Arduino Mega, which comes with 6 individual timers and other goodies.

DKGuffe:
I would like to update the output every 200mS or so, so nothing really fancy. Accuracy to 5Hz or better.

If the input frequency is 2 Hz or 4 Hz, how would you expect to distinguish such low frequencies within every 200ms and set the output accordingly?

jurs:
If the input frequency is 2 Hz or 4 Hz, how would you expect to distinguish such low frequencies within every 200ms and set the output accordingly?

I know the limitations at low frequency, it may be difficult to measure, and update, hence I said, 200mS or so.

In real life, I would probably have to put some limits on updating time, to match the actual measured signal.

So if I were in the 0 to 10Hz range, my update rate would have to be significantly slower, for instance.

I have yet to measure and characterize the hardware signal I interface to, so it may turn out to be a non-existing problem. I’m measuring a speed signal, and from the manual, the sensor outputs about 100Hz at 30km/h, assuming rough linearity, the frequency at 5km/h would be 15 Hz or so. Below that could be ignored completely without any consequences for the project success

That sounds like a "speedo corrector". There's lots of examples of that online.

Just use Timer2 (a 16-bit timer) and recognise that by using that timer you don't get to use PWM on some of the pins (I forget exactly which) but the pins that are 'lost' to PWM are going to be your output pins giving you the frequency you want.

DKGuffe:
I'm measuring a speed signal, and from the manual, the sensor outputs about 100Hz at 30km/h, assuming rough linearity, the frequency at 5km/h would be 15 Hz or so. Below that could be ignored completely without any consequences for the project success

OK, so let's stay at a 200ms sample time, so you are able to detect frequencies down to 5 Hz. Below 5 Hz you'd see values jumping between 0 and the actual frequency.

The input frequency could be detected/counted by setting up a hardware interrupt (pin-2 or pin-3 with Atmega328 based boards). The interrupt routine counts up a counter and remembers the micros() timestamp of the last interrupt time, your loop theen creates intervals of 200 milliseconds to evaluate the counter and timestamp. So input frequency range of 5 Hz to 400 Hz would be no problem.

And the output frequency can be generated in the same range.

Pseudo programming logic / part of the program loop:

// this is NOT fully working code
const long sampleInterval=200000L; // 200 ms = 200000 µs sample time
unsigned long lastSampleTime;
long halfOutputInterval=0;
unsigned long lastOutputChangeTime;
void loop()
{
  unsigned long now=micros();
  if (now-lastSampleTime>= sampleInterval)
  {
     lastSampleTime+=sampleInterval;
     if (halfOutputInterval==0) lastOutputChangeTime=now; // restart after 0 Hz output frequency
     halfOutputInterval= // recalculate new output interval here from interrupt counting
  }
  if (halfOutputInterval>0 && now-lastOutputChangeTime>=halfOutputInterval)
  {
     lastOutputChangeTime+=halfOutputInterval;
     digitalWrite(outPin,!digitalRead(outPin)); // swap output state
  }
}

Thanks guys.

I appreciate the help.

So I have figured out how to do my measurement. I decided to use an interrupt routine, measuring the time between pulses on the input in microseconds. Works perfectly.

Now, I generate the output using basically a modified version of blinkwithoutdelay. I use time to next instead of previous time, in order to get slightly better accuracy (less jitter).

I get some funny things happening.

If my input to the blinker is 10Hz, then I get a decent 10.4Hz output, which I guess is fine, but as the frequency increases, I get larger and larger errors.
Input = Output
20Hz = 20.8Hz,
50 = 55.4,
100 = 122.8,
200 = 285-296
300 = 680 - 713
360 = 950 - 1171

I wonder if there is a different way to do what I want.

This is the frequency counter code, which works perfectly!!

// Input from Speed Sensor
void ActOnPulse()
{
  noInterrupts();                                   //Disable interrupts while calculating pulse timing
  PrevPulseTime = NewPulseTime;                     //Last Pulse time stamp
  NewPulseTime = micros();                          //This pulse time stamp
  PulseTime = long(NewPulseTime - PrevPulseTime);   //Calculate delta time
  interrupts();                                     //re-enable pulse input for next pulse
  
  if (PulseTime >= 1000L) {                         //Ignore too fast pulses and negative timing (micros() rollover)
    FrqInHz = long(1000000L / PulseTime) + 1;           //Calculate Input Frequency (convert precission decimal to long)
    TooFast = TooSlow = 0;                          //range check reset
    if (FrqInHz >= 360) {                           // hi range
      TooFast = 1;
      if (DebugOutput == 1)
        Serial.println("Input out of range (>360Hz)"); //Debug
    }
    if (FrqInHz <= 0) { // low range
      TooSlow = 1;
      if (DebugOutput == 1)
        Serial.println("Input out of range (<1Hz)"); //Debug
    }
    if (TooFast == TooSlow) // in range = true
    {
      if (DebugOutput == 1) {
        Serial.print("Input Signal (Hz) ");           //Debug, only useful for single pulses, not for continues input
        Serial.println(FrqInHz);
      }
      FrqNew = FrqInHz; // set new frequency
    }
  }
}

This is the code that I’m having issues with at higher output frequencies

// Frequency output
void OutputFrq()
{
  interval = long((MultiPlier / FrqOutHz) / 2); //Calculate 50% duty cycle interval in microseconds
  TimeNow = micros();
  if ((TimeToNext - TimeNow) <= 1000L)      //Wait until close to next trigger time 1mS accuracy
  {
    TimeToNext = TimeNow + interval;
    if (Output_State == LOW)                    //50% duty cycle state machine
      Output_State = HIGH;
    else
      Output_State = LOW;
    digitalWrite(Out, Output_State);            //Change the output
  }
}

I figured out why it didn’t work as expected.

// Frequency output
void OutputFrq()
{
  interval = long((MultiPlier / FrqOutHz) / 2); //Calculate 50% duty cycle interval in microseconds
  TimeNow = micros();
  if ((TimeToNext - TimeNow) <= 1000L)      //Wait until close to next trigger time 1mS accuracy

This part if ((TimeToNext - TimeNow) <= 1000L) was way too high. I changed my timing error to 20uS instead.

Fiddling with the error timing while looking at the scope gives the following results:

Too high, pulse frequency all over the place, too low, lots of jitter and inaccuracy. 20uS seems to be the sweetspot.

It seems that 300Hz is the upper limit for reproducing within a couple of Hz, going to 400, I start to be 10+ off to either side, just floating around the setpoint.

If any of you have a good idea on other methods to produce a 10 to 400Hz signal to a better accuracy that I can do, then please let me know !

DKGuffe:
This is the frequency counter code, which works perfectly!!

No, it doesn’t work at all.

First problem: Code is incomplete and does not compile at all.

And the next problem is, that you start for the next pulse as a reaction on the pulse:

NewPulseTime = micros();

You react LATER in the loop than the pulse actually occurs.
Your timing is not actually based on the time when the pulses occur, but on the time when the reaction on the pulses occur in your program.

At only 10 Hz, this doesn’t make a big difference and will be very, very close to 10 Hz.
At higher Frequencies that might become a problem.

As you don’t show complete programs, it’s impossible to find out about all the mistakes that may lead to inaccuracy.

DKGuffe:
if ((TimeToNext - TimeNow) <= 1000L) //Wait until close to next trigger time 1mS accuracy

One millisecond inaccuracy? OMG!

DKGuffe:
If any of you have a good idea on other methods to produce a 10 to 400Hz signal to a better accuracy that I can do, then please let me know !

The pseudo code logic I posted above was of no worth for you?

jurs:
No, it doesn't work at all.

First problem: Code is incomplete and does not compile at all.

And the next problem is, that you start for the next pulse as a reaction on the pulse:

NewPulseTime = micros();

You react LATER in the loop than the pulse actually occurs.
Your timing is not actually based on the time when the pulses occur, but on the time when the reaction on the pulses occur in your program.

At only 10 Hz, this doesn't make a big difference and will be very, very close to 10 Hz.
At higher Frequencies that might become a problem.

As you don't show complete programs, it's impossible to find out about all the mistakes that may lead to inaccuracy.

One millisecond inaccuracy? OMG!

The pseudo code logic I posted above was of no worth for you?

I never said that my code was complete, I said, this is the part of the code that receives the pulse and outputs a frequency. It works FLAWLESS...

I'm not really sure why you dont think so? Perhaps you should have a second look at the interrupt code I posted above. (and my comments on the next post after the code I posted...)

I decided to use timing as THIS PULSE and TIME TO NEXT PULSE, instead of THIS PULSE and LAST PULSE as you suggest in your pseudo code previously. I did this since others (in other threads about timing) suggested that Time to next is a better way compared to Time since last, but essentially they are the same.

In terms of getting the input, I use interrupt to catch ANY frequency, simply counting microseconds between each received interrupt. I do probably get a little drift between pulses, and that might result in a bit of jitter. I'm gonna insert an "if" test to determine if the input frequency has changed more than 1% since last pulse, and if not, then choose not to update my incoming frequency, that should completely remove the jitter.

So for now, my code is simple:

/*
Trigger on interrupt, save time.
Trigger on interrupt again (new pulse)
Calculate time difference, output frequency to loop

process frequency from loop
compare with current frequency (10% test + changes etc)

Calculate changes, based on input pins, real world switches (for now anyway for hardware testing)

Output frequency based on above code.
*/

My current process is good. I need to implement some failsafe for external hardware, should I loose pulse input for one reason or another. Maybe a timer running, that defaults to a low "safe" frequency if no pulses are received for several seconds or something. I also need to revisit inputs once everything is tested.

I found that with the current code, I can output solid frequencies when input frequency stops, hence that's why I will implement the 1% or 10% test before changes made.

A potential problem with using interrupts to read frequency is if the signal stops the interrupt does not fire to update the timings. You will need to put extra timing code in loop to do something if the interrupt has not fired for x amount of time. In my sample code attached I increment a counter in the ISR and the main loop checks this.

Frequency_Counter.ino (2.74 KB)