Reading and generating square waves at the same time.

I am trying to read the frequency of a variable square wave from a speed sensor and then output a square wave that is the original multiplied by some percent. What I am running into is at high frequencies I am getting inaccuracies. Above 800hz the period starts to jump around. I'm thinking that the way the code is written it is taken longer to get back to the flip state part then the period of the sign wave. As the frequency gets higher the time is a bigger part of the period.

This is all with the output i am reading the wave great.

I think I need to use timer interrupts but I'm not sure the best way to go. I need to read from 0 to 5khz, And the output will be changed on the fly per the input frequency, Also I might not need interrupts on input as the output being stable is more important. If it takes 500ms to update that is fine.

here is the code

#include <FreqPeriodCounter.h>

const byte Speed_output = 7;      // the output pin to speedometer
const byte Speed_input = 3; 
const byte counterInterrupt = 1; // = pin 3
unsigned long previoustime = 0;    // will store last time output was updated
volatile unsigned long period ;
unsigned long interval  = 0 ;      // interval to toggle output
double x = 1;        // Correction Factor of Output to input
int POT = 0;

FreqPeriodCounter counter(Speed_input, micros,0);

void setup() 
{
  pinMode (Speed_input,INPUT_PULLUP);  // Pull up input pin 3 to 5 volts 
  pinMode(Speed_output, OUTPUT);      // Output pin 7 to sink current from speedometer
   
  attachInterrupt(counterInterrupt, counterISR, CHANGE);

}

void loop()
{ 
  
  if(counter.ready()) period = counter.period;
    interval=(period*x/2);  
 
  unsigned long currenttime = micros();
  if(currenttime - previoustime >= interval) 
  {
     previoustime = currenttime;   // save the last time you change state

     PIND = 0b10000000; // toggle output pin D7 
     
       
  }


}

void counterISR()
{ counter.poll();
}

moderator update: added code tags ==> # button above the smileys

I'm not familiar with that FreqPeriodCounter library, so I don't know whether you're using it correctly. Is it actually producing an accurate and consistent measurement? Is the FreqPeriodCounter object designed to be used in an interrupt context? I'd expect it to need some care to deal with volatile data and disabling interrupts around access to them. If it doesn't do that correctly, you can end up with spurious behaviour. If you get garbage in, nothing else is going to work.

Since the loop() function looks pretty fast, you could probably just poll the input pin state and from the name I guess that the poll() method was designed to do that. That's a hint it might not be designed to be used in an interrupt context.

There is no need to make period volatile since it is never accessed in an interrupt context.

There's a minor bug in your output timing code which could cause the output to slip slightly - I don't think this is causing your problem but I suggest you correct it anyway by changing line 32 to:

    previoustime += interval;

Good call, I was getting some drift with the output v input, I changed the code and now they are acting like that are locked in phase. NICE! I guess the way i had it any errors were compounding over time where now each time it is resetting. I did not even think about that.

But the problem is still there, I think you are right i don't need to use interrupts for the input, The library does state it is made to use with interrupts.

I think jitter in the output would be a more accurate description of what i am having problems with. At 120hz i am getting .9hz jitter and at 500hz i am getting 10HZ. I need it to have less than 1hz jitter at 5khz. It seems the jitter gets worse as the frequency gets higher.

Is there a more accurate way to make a square wave that i can change on the fly?

Is the jitter caused on the input side or the output side? If it's an input problem, obviously you'd be wasting your time trying to improve the output mechanism.

It seems to be an output problem, If i turn off the frequency generator so there is no new input it still has jitter, I did have an anlong read in the code and that also made the jitter and accuracy worse. when i took it out it got better. I was poling the input also in previous code and since i changed to interrupts it got better.

Ill do this and get back to everyone in a few mins, Ill take the input code out and just enter different values and see what the output does to take input jitter out of the equation.

What i am thinking about doing is using timer 1 to do an output interrupt but can i do that and change the frequency on the fly or once its set is it set?

I manually set the interval and it still has jitter, at 360hz it has 1.3hz of jitter. I guess more accurately that is 2.3 Micro seconds of jitter although my scope is showing about 10 micro seconds jitter in the period. So it seems to be the limit of using the blink without delay type code.

Is there a more accurate way to generate a variable frequency square wave?

Update i set up timer interrupts to toggle the output pin. and at 100 hz i am getting .6 hz jitter, but at 5k hz i am getting 20hz jitter, so a Lot better at higher frequency. It looks like that is the limit of the arduino, That range will actually work. what i need it for i need less than 2hz at 360hz or less than 32hz at 5kz.

Would a stronger micro controller do better?

Ill post up the code in a little while i am still working on it.

With this approach there will inevitably be some latency in the output, determine by the execution time of your loop. The smaller you need the jitter to be, the shorter the loop execution time needs to be. You need to figure out how much jitter is acceptable, and what the maximum loop execution time is.

From first post:

...the output being stable is more important. If it takes 500ms to update that is fine.

If 500 ms to update the output is OK, then at 5 KHz input you have 2,500 interval measurements to work with. Just adding those 2,500 readings then calculating the output will reduce jitter by a factor of 2,500.

I re wrote the code using timer interrupts and it is good up to 2.5khz with verry little jitter but above 2.5khz it stops outputing. It is getting better and thanks for all the help.

Here is the current code. Im sure its full or errors as its the first time i have used interrputs.

#
#include <FreqPeriodCounter.h>

const byte Speed_output = 7;      // the output pin to speedometer
const byte Speed_input = 3; 
const byte counterInterrupt = 1; // = pin 3

unsigned long hz = 0 ;
double x = 1;        // Correction Factor of Output to input
 

FreqPeriodCounter counter(Speed_input, micros,0);

void setup() 
{
  pinMode (Speed_input,INPUT_PULLUP);  // Pull up input pin 3 to 5 volts 
  pinMode(Speed_output, OUTPUT);      // Output pin 7 to sink current from speedometer
   
  attachInterrupt(counterInterrupt, counterISR, CHANGE);
  
cli();//stop interrupts
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  
  // set compare match register for ?hz increments
  OCR1A = 65535; // = (16*10^6) / (64*hz) - 1 (must be <65536)
  TCCR1B |= (1 << WGM12);// turn on CTC mode
  TCCR1B |= (1 << CS11) | (1 << CS10);  // Set CS11 and CS10 bits for 64 prescaler
  TIMSK1 |= (1 << OCIE1A);// enable timer compare interrupt
sei();//allow interrupts

}

ISR(TIMER1_COMPA_vect){//timer1 interrupt Hz/2 toggles output
  PIND = 0b10000000; // toggle output pin D7 
 }

void loop()
{ 
  
  if(counter.ready()) hz=counter.hertz();
      
  OCR1A = (((F_CPU/(64*hz))-1)/2*x);

}


void counterISR()
{ counter.poll();
}

dlloyd:
From first post:

...the output being stable is more important. If it takes 500ms to update that is fine.

If 500 ms to update the output is OK, then at 5 KHz input you have 2,500 interval measurements to work with. Just adding those 2,500 readings then calculating the output will reduce jitter by a factor of 2,500.

I am reading down to 2hz would that not delay the reading a long time at low frequency?

Yes, but your code already knows the input frequency, so at 2 Hz, only 1 sample is necessary. 100 Hz in --> 50 samples, 1000 Hz --> 500 samples, etc. So there would always be 2 Hz update in changing the output frequency, giving low jitter throughout the whole range 2 Hz -5 KHz input.

EDIT: In effect, you're keeping the response (latency) regulated to 500 ms in order to maintain low jitter. Note that at 2 Hz input and 500,000 us measurement time, 50 us of jitter is only 0.01% change in value. Originally, with 5Khz input you have 2000 us measurement time. So 50 us of jitter here represents 2.5% change in value.

dlloyd:
Yes, but your code already knows the input frequency, so at 2 Hz, only 1 sample is necessary. 100 Hz in --> 50 samples, 1000 Hz --> 500 samples, etc. So there would always be 2 Hz update in changing the output frequency, giving low jitter throughout the whole range 2 Hz -5 KHz input.

good point, Thanks for the help, I will try that.

I am about to pull my hair out over this. I found a different library that uses the input capture function of timer one to measure frequency and it works great....for reading the frequency that got rid of all my input jitter and is accurate to less than .5 HZ at 6kh...good enough for me. But now i cant use timer 1 to generate the square wave output. I am using it with the blink without delay code and it is causing a glitch every time an edge of the square wave from the input and out put happen at the same time. The library seems to pause the code for a second while it is taking a measurement.

I have been through 10 revisions on the code and I can either get the input solid or the out put solid but not both at the same time. I have tried 3 different ways to read input, Timer 1 input capture, Poling and Using hardware interrupts. And of the 3 input capture is working the best but on its own. With the output I have tried Timer 1 CTC mode(works great), Blink without delay(jitter when code get to long), and timer 2(not enough resolution).

The best i have so far is Input capture timer 1 with blink without delay output but over 1khz the latency and jitter gets bad. The best option would be to use Input capture with timer 1 interrupts on the output but i do think i can use timer 1 twice?

Does anyone have any ideas? My best idea now is to use a DDS generating chip controlled by the arduino to generate the output and the arduino to read the input frequency and do the math. Although i would like to do it all with the arduino.

Here is the code using blink without delay and timer 1 input capture.

#include <FreqPeriod.h>

double inputfrequency;
long int inputperiod;

const byte Speed_output = 8; // the output pin to speedometer

unsigned long previoustime;
unsigned long timeout;

unsigned long period ;
unsigned long interval  = 0 ;      // interval to toggle output
double x = 1.194;        // Correction Factor of Output to 

void setup() 
{  
  pinMode(Speed_output, OUTPUT);      // Output pin 7 to sink current from speedometer
  FreqPeriod::begin(); 
}

void loop()
{ 
  
   inputperiod=FreqPeriod::getPeriod();
   
  if (inputperiod )
  {
    inputfrequency= 15972400.0 / inputperiod;
    timeout =millis();
    period = (inputperiod/16);
    interval=(period/2);  
  }
  
  unsigned long currenttime = micros();
  if(currenttime - previoustime >= interval) 
   {
      previoustime += interval;   // save the last time you change state
      PINB = 0b00000001; // toggle output pin D8 
   }
  
  unsigned long CT = millis(); // time out if no signal
   if (CT - timeout > 600)
  {
   interval=1000000000;
  } 
}