multiply sync. an input frequency

Hello,

as a part of a bigger program I need to multiply an input frequency in form of pulses by 4 and among other things, I have to write this to an output pin as well.

I had several starting points but I always end there how to synchronously write the input frequency multiplied by to an output pin.

since the input pulse rate is not so high I have to use the time passing between the falling signal to the rising signal as the base for the calculation of the input frequency.

How can I realize the (almost) synchronously output based on the time passed between the input pulses?

Thanks for the ideas!

I have to use the time passing between the falling signal to the rising signal as the base for the calculation of the input frequency.

That sounds wrong. Should you not be timing between either consecutive rising pulses or falling pulses ?

UKHeliBob:
That sounds wrong. Should you not be timing between either consecutive rising pulses or falling pulses?

You mean to get the time included when the pin is on high, so the whole period. Sure that is right, I am now just wondering why I so often see the measurement between the falling and the next rising signal when it comes to frequency.
Actually complete right would be to use the time of one whole period. I haven`t mentioned that I need to update the frequency variable every second. To get smooth results an averaging would also be useful in this case like a circular buffer for example. The frequency should be sent through a bus to another device often as well it shell show the result multiplied on a gauge.

EDIT:

I just came on that I would need both, the low level period time & the high level period time. By this I could just divide both by 4 an write that to a digital output.
Will that work?

Will this work? I can not test at the moment!

It should count the microseconds of the high state and divide that time by four. Then it should write the pin to high as long the result of divided time. The rest of the time the pin shall be low.
By the way, I am using a Teensy 3.2 & 3.5 regarding the interrupt.

volatile int pulseHigh_time = 0;
volatile int prevHigh_time = 0;
int freqOut = 6;
int freqDivider = 4;

void setup() {
  attachInterrupt(19, rising, RISING);
}

void loop() { }

void rising() {
  attachInterrupt(19, falling, RISING);
  prevHigh_time = micros();
}

//******** Wait for next puls from flow sensor is going from 1 to 0 and calculate the time left since
void falling() {
  attachInterrupt(19, rising, FALLING);
  pulseHigh_time = micros() - prevHigh_time;
}

void generateFreq() {
  int currentMicros = micros();										// set currentMicros to actual micros()
  int pulseHighOut_time = (pulseHigh_time / freqDivider);			// divide the puls high time by frequency divider
  if (currentMicros - pulseHighOut_time <= (pulseHighOut_time + currentMicros)) {		// conditional comparing
    // save the last time you blinked the LED
    digitalWrite(freqOut, HIGH);									// set the pin to high as long the condition is not true
  }
  else
    // set the LED with the ledState of the variable:
    digitalWrite(freqOut, LOW);										// While condition is true set the pin to low
}

You are not calling generateFreq() anywhere. Presumably from loop() as often as possible?

(currentMicros - pulseHighOut_time <= (pulseHighOut_time + currentMicros)) has currentMicros on both side of the comparison, you are effectivly comparing if negative pulseHighOut_time is less than its positive ...which it always will be. 8) You need a static unsigned long StartPulseTime and rewrite the timing (see the LED without delay example)

When you access the volitile int variables outside the interrupts routines, you should bracket that code with noInterrupts()/interrupts() to avoid the value changing while you calculate. Using interrupts is a VERY tricky thing - all sort of subtelties in the timing has to be thought through. What happens if you get a new pulse whilst still "outputting" the previous ?

I think you can do this without interrupts: Wait for input to high. Wait for input to go low. Note time taken. Set output pin high. wait (even using delay() :astonished: ) until Time Taken/4. Set output pin low. Of course if this is part of a greater program or some other unmentioned constraint the interrupts MAY be necessary.

3dprinter:
You are not calling generateFreq() anywhere. Presumably from loop() as often as possible?

(currentMicros - pulseHighOut_time <= (pulseHighOut_time + currentMicros)) has currentMicros on both side of the comparison, you are effectivly comparing if negative pulseHighOut_time is less than its positive ...which it always will be. 8) You need a static unsigned long StartPulseTime and rewrite the timing (see the LED without delay example)

When you access the volitile int variables outside the interrupts routines, you should bracket that code with noInterrupts()/interrupts() to avoid the value changing while you calculate. Using interrupts is a VERY tricky thing - all sort of subtelties in the timing has to be thought through. What happens if you get a new pulse whilst still "outputting" the previous ?

I think you can do this without interrupts: Wait for input to high. Wait for input to go low. Note time taken. Set output pin high. wait (even using delay() :astonished: ) until Time Taken/4. Set output pin low. Of course if this is part of a greater program or some other unmentioned constraint the interrupts MAY be necessary.

Thanks man! I know I have still a lot to learn...
The interrupt is necessary because of the continuously sending the result through a CAN bus (NMEA2K).

I`ll try to get this right this time and repost..

So I hope this is better. I added now all the functions to the loop because not just generateFreq needs to be called right?
I do not see the point why startPulseTime should be static?

let`s see, I hope this is better...

unsigned long pulseHigh_time = 0;
unsigned long prevHigh_time = 0;
int freqOut = 6;
int freqDivider = 4;
unsigned long StartPulseTime = 0;

void setup() {
  attachInterrupt(19, rising, RISING);
}

void loop() {
  rising();
  falling();
  generateFreq();
}

void rising() {
  attachInterrupt(19, falling, RISING);
  prevHigh_time = micros();
}

//******** Wait for next puls from flow sensor is going from 1 to 0 and calculate the time left since
void falling() {
  attachInterrupt(19, rising, FALLING);
  pulseHigh_time = micros() - prevHigh_time;
}

void generateFreq() {
  unsigned long currentMicros = micros();
  StartPulseTime = ((currentMicros + pulseHigh_time) / freqDivider);

  if (currentMicros - StartPulseTime <= currentMicros) {		// conditional comparing
    digitalWrite(freqOut, HIGH);									// set the pin to high as long the condition is not true
  }
  else
    digitalWrite(freqOut, LOW);										// While condition is true set the pin to low
}

There came something in my mind!

If I measure the time of the impulse from the input and divide the time the of the high period by 4 and write that time to an output high, this does not multiply the frequency, this would just give a shorter pulse, right?

Will this work?

// Based on FreqMeasure example

#include <FreqMeasure.h>
byte freqDivider = 4;
volatile float freqOut;

void setup() {
   FreqMeasure.begin();
}

double sum = 0;
byte count = 0;

void loop() {
  FreqCount();
  FreqOutput();
}

void FreqCount() {
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 30) {
      float frequency = FreqMeasure.countToFrequency(sum / count);
      freqOut = (frequency / freqDivider);
      sum = 0;
      count = 0;
    }
  }
}

void FreqOutput() {
  unsigned long highTime = 	((1000000 / freqOut) / 2);			// calculate the time of high level in microseconds
  unsigned long currentTime = micros();
  if (currentTime - (highTime * 2) <= currentTime) {
    digitalWrite(6, HIGH);
  }
  else
    digitalWrite(6, LOW);
}

(brief reply from phone) there is no problem in measuring a high-low-high cycle, and simultaneously output a squarewave at 4 times the frequency (or 1/4 period), up to about a few hundred kilo hHz (standard Arduino). The averaging and second to see new inputfrequency is "easy", too. Maybe I am getting confused what you want - ignoring all code for the moment.

I just came on that I would need both, the low level period time & the high level period time.

You only need both if you want the output to have the same duty cycle as the input. Is that a requirement in your project ?

To make the function needed clear:

I get a input from a hall effect sensor of pulsating format with up to 110 times/sec.

So now I need to take this frequency and forward it through CAN to another device.

Simultaneously I need to generate this frequency multiplicated by 4 to use this then to output this to an analog gauge which needs as input 4 times the original frequency to show correct data.
So this output signal needs to have the correct timing in low and high periods to show the correct value.

The CAN bus is an NMEA2K network which shows the data on a multi-functional display and the gauge is a RPM meter.

The puls input signal is generated by an engine and generate 1 pulse per round.

I hope this makes it clear what I try to create.

Yes, this is a clear specification.

So sketching out your code requirements, you can set up an interrupt to detect the rising edge. When triggered it will store the interval since the previous edge, and store the current time.

You main loop will use a separate timervariable, to toggle an output pin at a frequency, given by a frequency-variable. Lastly the loop will look at the input interval value, maybe do a running average, and update the outputfrequency variable. This last bit should be controlled by a 3rd timer system to run as often/rarely as required.

So lets do some pseudocode!

setup: set the interrupt routine

Interrupt: InputInterval = micros() - LastEdge ; LastEdge = micros()  

Loop: OutputPulses() ; AdjustPulses() ; TheOtherCanBusStuff() ;

OutputPulses:
 if ( micros() - OutTimer > OutputInterval2 ) {
  digitalWrite(OutPin, digitalRead(OutPin)==LOW?HIGH:LOW ) ; // note1
  OutTimer = micros() ; }

AdjustPulses: 
  if ( millis() - AdjustTimer > AdjustInterval ) {
    OutputInterval2 = InputInterval / 8 ;
    AdjustTimer = millis() ; }

(1) The "X?T:F" code construct is the trenary operator. On some Arudino/Pins the software allows you to read an outputpin, ie it returns the last value you set it. Yo may need to expand that with an if and lastwritten value.

The OutputInterval2 is half as we need to toggle the output twice, but only measure the input once for a full wave. And this will not copy the dutycycle of the input, the output will be equally on/off.

The running average code is left as an exercise :slight_smile:

If you don't really need the output frequency to be synchronized this is a quick and dirty way to make a frequency multiplier.

const byte InputPin = 2;
const byte OutputPin = 3;


void setup()
{
  pinMode(InputPin, INPUT);
  pinMode(OutputPin, OUTPUT);
}


void loop()
{
  unsigned long highTime = pulseIn(InputPin, HIGH);
  unsigned long lowTime = pulseIn(InputPin, LOW);
  unsigned long cycleTime = highTime + lowTime;  // Microseconds
  unsigned long frequency = 1000000UL / cycleTime;  // Hz (constrained by tone() function limits)
  tone(OutputPin, constrain(frequency*4, 31UL, 65535UL));
}

Thanks for all your attention!

I used now the whole day to figure out all the edges.
My conclusion, I can not use the FreqMeasure library!
It is giving me just stupid output and all with a huge delay.

So I just programmed an Arduino to output simply high/low with fixed high-level time (tested between 100 - 150 ms) and a variable low-level phase adjusted by pot. So I could simulate 10 - 100 Hz. FreqMeasure did not count right!

This tells me now I have to use my own routine based on an interrupt to get the pulses per second (pulses per minute which will equal RPM).

So let the interrupt routine trigger a counter within a specific time. Push all these values into a ring buffer of a couple of measurements to smooth this a bit.

My fastest pulse rate will be at approx 4000 RPM which is equal to 67 Hz (pulses/sec) with a period time of 15 ms.
I made a small sheet for the timing and frequency to overview:

RPM Frequency period time
600 10,00 Hz 100000 µs
1000 16,67 Hz 60000 µs
2000 33,33 Hz 30000 µs
3000 50,00 Hz 20000 µs
4000 66,67 Hz 15000 µs
5000 83,33 Hz 12000 µs
6000 100,00 Hz 10000 µs

So how long does the maximum counter time have to be? According to the times from the sheet, I have to use at least 100ms. That would be ok since I could update the data 10/sec on the CAN bus. BUT, what about my smoothing with ring buffer which I guess is necessary?! When I have a minimum counter time of 100ms and a buffer size of 10 then the average is available after 1 second which is too long. Sure it is a delay of one second but this can be long when it comes to RPM of an engine right? At least I do not have a so huge delay in my car?!

Well, not exactly for what you ask, but this is the times4 multiplier code.

const int InputPin = 2 ;
const int OutputPin = 7 ;

void setup() {
  pinMode(OutputPin,OUTPUT) ;
  pinMode(InputPin,INPUT) ;
  attachInterrupt(digitalPinToInterrupt(InputPin), LevelDetector, RISING) ;
}

volatile unsigned long InputInterval ;
unsigned long LastEdge ;
void LevelDetector() {
  InputInterval = micros() - LastEdge ;
  LastEdge = micros() ;
}

unsigned long Output2Interval ;
unsigned long OutputTimer ;
unsigned long AverageTimer ;
void loop() {
  if ( micros() - OutputTimer > Output2Interval ) {
    digitalWrite(OutputPin,digitalRead(OutputPin)==HIGH?LOW:HIGH) ;
    OutputTimer = micros() ;
  }
  if ( millis() - AverageTimer > 1000UL ) {
    // Running average missing 
    noInterrupts() ; Output2Interval = InputInterval / 8 ; interrupts() ;
    AverageTimer = millis() ;
  }
}

Tested. Attached are two waveforms from the logic analyzer - top is input bottom is output. Note that the output changes a bit after the input - due to the once-a-second update you asked for.

A "simple, yet powerfull" :roll_eyes: averaging code, that does not need a circular buffer is

Average = Average*0.9+Input*0.1 ;

This gives an exponential curve following the input. Adjust the 0.9 and 0.1 to, f.ex. 0.8 0.2 for a faster average, and so on.

3dprinter:
Well, not exactly for what you ask, but this is the times4 multiplier code.

const int InputPin = 2 ;

const int OutputPin = 7 ;

void setup() {
  pinMode(OutputPin,OUTPUT) ;
  pinMode(InputPin,INPUT) ;
  attachInterrupt(digitalPinToInterrupt(InputPin), LevelDetector, RISING) ;
}

volatile unsigned long InputInterval ;
unsigned long LastEdge ;
void LevelDetector() {
  InputInterval = micros() - LastEdge ;
  LastEdge = micros() ;
}

unsigned long Output2Interval ;
unsigned long OutputTimer ;
unsigned long AverageTimer ;
void loop() {
  if ( micros() - OutputTimer > Output2Interval ) {
    digitalWrite(OutputPin,digitalRead(OutputPin)==HIGH?LOW:HIGH) ;
    OutputTimer = micros() ;
  }
  if ( millis() - AverageTimer > 1000UL ) {
    // Running average missing
    noInterrupts() ; Output2Interval = InputInterval / 8 ; interrupts() ;
    AverageTimer = millis() ;
  }
}



Tested. Attached are two waveforms from the logic analyzer - top is input bottom is output. Note that the output changes a bit after the input - due to the once-a-second update you asked for. 

A "simple, yet powerfull" :roll_eyes: averaging code, that does not need a circular buffer is 


Average = Average0.9+Input0.1 ;



This gives an exponential curve following the input. Adjust the 0.9 and 0.1 to, f.ex. 0.8 0.2 for a faster average, and so on.

Thank you! Does this work also if the pulse high period is much shorter then the low period?

I did a lot of testing over the weekend and a lot of "finished" code for RPM is just giving wrong values if the RPM is getting higher or do not fit at all.

I decided now to split this out of the main code and use a separate teensy device to do the RPM stuff and transfer this than through wire to the CAN NMEA2K bus connected teensy. This will solve some problems.

Still, I have the problem to get a functional code to get the RPM based on the pulse frequency period, special when the pulse high time is much shorter than the low time. I created different using interrupts, all of them created wrong values at low or high RPM.