Read signal (5v wave) get it's frequency, output converted frequency x2

Hi

I need to convert square wave 12v signal from car engine ecu, calculate it's frequency and output converted PWM signal with frequency multiply by a factor.

I tried several timer libraries but no success up to now.

  1. Read PWMinput
  2. Calculate frequency of PWMinput
  3. Generate PWMoutput signal with PWMinput x someFactor

Any ideas ?

Thanks !
David

PWM signals usually have a constant frequency and a variable mark-space ratio. Your outline does not mention measuring the mark-space ratio.

Do you really have a PWM input signal or just a square wave of varying frequency?

Do you want to output a square wave at twice the input frequency or do you want to output a PWM signal with a mark-space ratio that reflects the frequency of the incoming signal?

Or what?

Any of these things should be doable. I doubt if any of the Timer libraries will be necessary.

...R

Hi Robin

The input is analog 12v square wave signal which is an input to rpm gauge cluster.
Due to engine conversion (different number of cylinders) i need to correct the signal to display the actual rpm.

I can lower the signal to 5V using voltage divider.
The rpm gauge works with arduino 5V pwm signal frequency between 10-220 HZ (tested)

Thanks
David

motorica:
The rpm gauge works with arduino 5V pwm signal frequency between 10-220 HZ (tested)

It seems unlikely that you tested what you describe above, since it's neither easy nor necessary to generate a variable frequency PWM signal in this case.

All you need is to
(1) measure the incoming pulse length in microseconds.
(2) halve that value.
(3) use the technique demonstrated in blink without delay to output a square wave of that pulse length

The whole sketch would probably be about a dozen lines of code.

motorica:
The input is analog 12v square wave signal which is an input to rpm gauge cluster.
Due to engine conversion (different number of cylinders) i need to correct the signal to display the actual rpm.

I can lower the signal to 5V using voltage divider.
The rpm gauge works with arduino 5V pwm signal frequency between 10-220 HZ (tested)

You need to be more careful in your use of technical terms - both your for your benefit and ours.

What you describe in the first two lines is a common problem and has nothing to do with PWM.

What you say in the last line is a mystery because you use the word PWM in the same sentence as variable frequency. But the Arduino does not produce a variable frequency PWM signal when you use analogWrite(). Please show the code you used to test the gauge with the Arduino.

If, as seems likely, all you need to do is produce a square wave with a different frequency @PeterH has described how to do it.

...R

What i did up to now is activate the gauge using pwm.h library , not a variable pwm but a fixed.

Thanks for guiding me to drop the PWM , it did helped a lot !

Certainly you do not need any form of PWM. Or library!

Automotive timing is readily managed in straightforward software running on the MCU. And don't even think about trying to use interrupts.

You use a code loop that records the time between successive transitions of the input signal. On each transition, it begins an output pulse whose length is defined as one quarter of the length of the sum of the last two transition durations (in other words, a quarter of the current input cycle, considered in alternating direction). The code, whilst waiting for the next transition, times out that pulse.

This might have to be modified if the input waveform is not "square" but in fact consists of narrow pulses, but I suspect it is designed to be "square" if it is to control an analog gauge.

Hi guys

I tried the most simple flow you suggested, but with no success..
The rpm needle is responding to engine rpm but rpm is lower , no matter what is the conversion value of the duration i'm measuring using pulsein method.

I have also tried interrupt and it works great ! but i can't change the output signal, only output what the exact input signal.

Any ideas ?

Thanks !
David

pulsein method

#define OUT_PIN 13
#define IN_PIN 7 

    
unsigned long duration = 0; 
int rpmFactor = 100;
int convertedDuration;
unsigned long readTimeout = 10000;
int state=0;

void setup(){ 
  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, INPUT);
  Serial.begin(9600);
}

void loop()
{ 
  duration = pulseIn(IN_PIN, HIGH);
  digitalWrite(OUT_PIN, !digitalRead(OUT_PIN));
  delayMicroseconds(duration);
  Serial.println(duration);
}

Interrupt

int pin = 13;
volatile int state = LOW;

void setup()
{
  pinMode(pin, OUTPUT);
  attachInterrupt(0, blink, CHANGE);
  Serial.begin(9600);
  int state = 0;
}

void loop()
{
    digitalWrite(pin, state);    
}

void blink()
{
  state = !state; 
}

The first problem with your two sketches is a conceptual one. In both cases the output is exactly governed by the input so it simply can't be different in the way you want.

@PeterH described the general princple that should be used in your first sketch.

In this case I think it will be more difficult usin interrupts because for every interrupt you need to produce 2 outputs that are evenly spaced between the inputs.

You need to use pulseIn to measure the width of the pulse and save it after dividing by 2. Then you need a separate function to generate the output pulses based on those half widths. Something like this pseudo code

void loop() {
    curMicros = micros();
    getAndSavePulseWidth();
    makeNewPulses();
}

void makeNewPulses() {
   if (curMicros - prevPulseMicros >= halfPulseWidth) {
      pinState = ! pinState
      digitalWrite(pulsePin, pinState);
      prevMicros += halfPulseWidth;
   }
}

A possible (probable ?) problem with this is that the pulseIn function blocks the CPU until it finishes. If that does cause a problem you may need to use interrupts in place of pulseIn.

If you have attachInterrupt(0, measurePulse, CHANGE);

your ISR could be something like this (not tested)

void measurePulse() {
   pulseMicros = micros();
   halfPulseWidth = (pulseMicros - prevMicros) / 2;
   prevMicros = pulseMicros;
}

...R

Hi Robin

My first example don't include a change in the measured time i've recorded but i did tried to convert this value and it didn't influence the output to the gauge.

I also thinks that the pulsein hangs the cpu and cause the code not to work as expected, this is why i tried interrupts just for proof of concept. It doesn't include any change for the output signal just a copy.

So i guess interrupts are do needed, i will try your idea to measure the time within interrupt and will update.
Maybe there is a need for 2 interrupts, one for measuring and another for generating..

Thanks !
David

motorica:
Maybe there is a need for 2 interrupts, one for measuring and another for generating..

I don't think so. Once you have a suitable value for the halfPulseWidth any simple code will produce the output.

I guess you could use one of the hardware timers (Timer1 or Timer2) to create the output using something derived from the measured halfPulseWidth to set the timing interval. I think the timers can cause a pin change directly.

...R

I just tried to measure the pulse within the interrupt, it doesn't work.
I see on the serial window that on each 14 readings the value jumps from around 1800 to 28,000 .. (computed halfPulseWidth)

int out_pin = 13;
volatile long unsigned pulseMicros;
volatile long unsigned halfPulseWidth;
volatile long unsigned prevMicros = 0;;

void setup()
{
  pinMode(out_pin, OUTPUT);
  attachInterrupt(0, measurePulse, CHANGE);
  Serial.begin(9600);
}

void loop()
{
  makeNewPulses();
}

void makeNewPulses() 
{
  digitalWrite(out_pin, !digitalRead(out_pin));
  delayMicroseconds(halfPulseWidth);
  Serial.println(halfPulseWidth);
}


void measurePulse() 
{
   pulseMicros = micros();
   halfPulseWidth = (pulseMicros - prevMicros)/2 ;
   prevMicros = pulseMicros;
}

What happens if you use my code for making the pulses. It does not depend on delay().

What you are experiencing is the fun and frustration of using interrupts. They can be very tiresome to debug.

Are you quite sure that the value of halfPulseWidth should not vary like your numbers indicate?

Are you quite sure that I wrote the code correctly?

What happens if you just print halfPulseWidth and don't bother with the digitalWrite() statements?

Maybe you should just interrupt on RISING or FALLING rather than CHANGE.

These are all things I would expect you to think of trying yourself without any prompting. Each of them only take a few minutes to try. The Arduino is a great device for learning by doing.

...R

motorica:
I just tried to measure the pulse within the interrupt, it doesn't work.

You are not listening, are you?

No interrupts, no libraries.

// Pulse Doubler
// Public Domain  Paul B.

#define OUT_PIN 13
#define IN_PIN 7

// Concept: Look for transitions; need state memory
byte in_was = LOW;
// Read once per cycle
byte in_now;
// Time of last transition up
unsigned long wentup = 0;
// Time of last transition down
unsigned long wentdn = 0;
// Output pulse duration
unsigned long pulsetime = 0;
// Time last pulse started
unsigned long pulseis = 0;

// Initiate an output pulse
void pulseon(unsigned long length) {
  digitalWrite(OUT_PIN, HIGH);
  pulsetime = length >> 2;  // A quarter of the last interval
  pulseis = micros();
}

void setup(){ 
  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, INPUT);
  digitalWrite(IN_PIN, HIGH);  // Input pullup
}

void loop()
{ 
  // Read pin once
  in_now = digitalRead(IN_PIN);
  // Now, were we high or low?
  if (in_was == LOW) {
    if (in_now == HIGH) {
      // So, has transitioned from low to high
      in_was = HIGH;
      pulseon(micros() - wentup);
      wentup = micros();
    }
  }
  else {
    if (in_now == LOW) {
      // So, has transitioned from high to low
      in_was = LOW;      
      pulseon(micros() - wentdn);
      wentdn = micros();
    }
  }

  delay(15); // Very crude debounce for proof of concept only
  // Now perform the pulse delay
  if (pulsetime != 0) {
    if (micros() - pulseis >= pulsetime) { 
      pulsetime = 0;
      digitalWrite(OUT_PIN, LOW);
    }
  }
}

So, here is some proven code written - especially for you! :astonished:

Note it is test code; proof of concept using a crude debounce delay and an input pullup so that you can test it with a button on pin 7.

Prove it for yourself, then for your tacho, remove the delay() and do what you like with the input pullup.

One little warning, the duty cycle of the input must be between about 35 and 65% (because the pulse width is set at 25%).


Edit: First tested using millis(), but no point not using micros() for the demo anyway!

Actually it isn't that simple to check , as i need to start the car (very noisy) each time i make a change

Anyway i will check any new idea and will update, i'm sure i will help in the future as this project is relevant for anyone that wants to correct speedometer reading, tachometer reading, on board car computer and so on.

Thanks guys !

David

motorica:
Actually it isn't that simple to check , as i need to start the car (very noisy) each time i make a change

If you have a second Arduino you could program it to produce a square wave output that simulates the car and use that as input for th early stages of testing.

...R

It's an option and i thought about it .. but i have just one board ' at least for the moment.

Sorry for bringing up such an old post, but did you end up getting this working? im doing exactly same thing just with my Speedometer

@robbie98 this is a very old Thread. I suggest you just start your own Topic and tell us in detail what equipment and program you have and what the problem is.

...R

robbie98:
Sorry for bringing up such an old post, but did you end up getting this working? im doing exactly same thing just with my Speedometer

Did you try out my code?