Help with frequency multiplier

Hi everyone

I have 2 existing circuits which i need to combined.

One is a speedometer that requires a 0v to 8v square wave with a frequency of 2.225hz per mph. The other is a GPS speed generator that outputs a 0v to 12v square wave with a frequency of 1hz per mile.

Could a arduino be programmed to multiply the frequency and if so how accurate would it be?

Thank you

What is the accuracy You need?

Needs to be accurate to within 2mph.

What's the essential speed range? For slow speed you measure the pulse distance, for faster speed the pulse frequency.

Is it a distance or speed indicator? Speed should include a time frame, e.g. mph.

Okey. GPS devices show speed and they are likely rather exact but they have a latency when speed changes.

Can You produce data, schematics of speedometer? That's needed to design the 8 volt sending circuitry.

DrDiettrich the GPS outputs 1hz per mph or 3600 pulses per mile.

The speedometer is a analog moving coil gauge with a stepper motor to move the total mileage. All this is controlled by a stm32 cpu and mosfets to drive the motors.

The speedometer goes from 0mph to 140mph.

Maximum frequency required to drive the speedometer at 140mph is 311.5hz.

Rather than think of this as multiplying a frequency, think of measuring one frequency and generating another. A resistive divider is needed for the 12V signal, converting to 5V for a digital input pin. Using micros() to time the gaps between transitions on this pin provides a way to sense frequency.

To generate a frequency you can use a DDS (direct digital synthesis) loop - you update a phase accumulator on a regular basis, like this:

unsigned unsigned int phase_acc = 0 ;  // keeps track of phase
unsigned unsigned int phase_inc = 0 ;  // amount to increment phase each time
const unsigned int TOP_BIT = 0x8000 ;

unsigned unsigned long update_time = 0 ;
const unsigned long update_period_us  = 200; // checking 5000 times a second, good to 2.5kHz output rate.

void set_frequency (float freq) // call when frequency changes.
{
  phase_inc = 65536.0 * freq * update_period_us / 1e6 ;
}

void handle_dds ()  // call often, from loop()
{
  if (micros() - update_time > update_period_us)
  {
    update_time += update_period_us ;  // schedule next check
    phase_acc += phase_inc ;   // increment phase accumulator
    digitalWrite (output_pin, (phase_acc & TOP_BIT) != 0) ; // use top bit as output
  }
}

The DDS technique can be used for other waveforms by table-lookup if you want, but square waves is easy by just taking the top bit of the phase accumulator. A phase accumulator wraps around every 360 degrees, or in other words is a fixed-point representation of the fractional turn.

I have no idea what your going in about with that code.

I'm only at the beginning of understanding frequencies.

Then you'll either need to understand how to read and generate frequencies, or find a project that's already done a very similar thing.

Do you understand the relationship between period and frequency?

Or explain what you need to do this for.

I think i understand that the period is time between each pulse and the frequency is how many pulses there are in a second.

You really need to explain what you are up to. I assume that you want to "equalize" the two frequencies for comparison? Multiplication of the frequency prior to frequency calculations, is almost certainly not the way to do that, if you are measuring both of them separately in your program.

If, in your program you have calculations that produce a frequency for each input, then a simple arithmetic multiplication by some scale factor will do it, easily. One line of code.

You say you have "circuits that need to be combined". What circuits? A frequency multiplier, per se, is usually required where some device other than the system that you are running code on, needs to be driven with a signal. I strongly suspect this is not your case.

The quality of the answers you get here, depends on the quality of the questions.

Technically, how many pulses in a given time period. In the MKS system, the time period is specified as a second.

So you know how to measure frequency from timings of input voltage transitions...

The only time I've measured a frequency it was with a frequency counter.

Okay, so you have no response to reply #12? From your reply #15 it seems like you have a long way to go before you can even "multiply a frequency". Since, you are saying that you haven't managed to even measure a frequency. So you should immediately clarify the current state of your project by clearly expressing the purpose, requirements and also what you have done so far.

I managed to guess from your not very clear description, you want the Arduino to accept pulses from the GPS speed sensor, and then convert the speed data and output it to the speedometer by producing a pulse train that controls the speedometer. Is that correct? If you start answering the questions that are asked, this will go a lot faster.

Here's an attempt using interrupts on a Mega2560 to help time in the incoming GPS pulses and polling to do the output pulses. Based on scope testing this seems reasonably accurate; if you want more accuracy you'll need to use hardware timer functions like input capture and PWM outputs.

(FWIW, I used pin 7 to generate a test pulse, fed to the GPS input pin for bench testing...)

/*
 * Sketch:  sketch_oct16e
 * Target:  Mega2560
 *      
 */

const uint32_t K_TST_GPS_TIME_LO    = (700000ul >> 1);  //uS    toggle period of test signal pinTest - slow speed
const uint32_t K_TST_GPS_TIME_HI    = (6667ul >> 1);    //uS    toggle period of test signal pinTest - fast speed
const uint32_t K_TEST_GPS_STEP      = 1000ul;           //uS    toggle sweep step size

const uint32_t K_MPH_MIN_US     = 149812ul;             //uS    MPH = 224719.10/K blanking threshold

const uint8_t pinGPS = 2;   //GPS input
const uint8_t pinMPH = 3;   //2.225x MPH output

const uint8_t pinTest = 7;  //connect to pin 2 for bench testing

bool
    bMPHEn;
volatile bool    
    g_isr_bFirst,
    g_isr_bEvt;
volatile uint32_t
    g_isr_u32deltaGPS,
    g_isr_u32timeGPS;
uint32_t
    g_MPHuS;
    
void setup() 
{    
    pinMode( pinGPS, INPUT_PULLUP );
    attachInterrupt( digitalPinToInterrupt( pinGPS ), isr_GPS, RISING );
    pinMode( pinMPH, OUTPUT );
    pinMode( pinTest, OUTPUT );

    bMPHEn = false;
    digitalWrite( pinMPH, LOW );
    
    g_isr_bFirst = true;
    g_isr_bEvt = false;  
    g_MPHuS = K_MPH_MIN_US;  

}//setup

void loop() 
{
    genTestGPS();
    calcMPH();
    mphHandler();    
    
}//loop


void genTestGPS( void )
{
    static uint32_t
        timeToggle = K_TST_GPS_TIME_LO,
        timeTest = 0ul;
    static bool
        bDir = false;
    uint32_t
        timeNow = micros();

    if( timeNow - timeTest >= timeToggle )
    {
        digitalWrite( pinTest, digitalRead( pinTest ) ^ HIGH );        
        timeTest = timeNow;

        switch( bDir )
        {
            case    false:
                timeToggle -= K_TEST_GPS_STEP;
                if( timeToggle <= K_TST_GPS_TIME_HI )
                    bDir = true;
                    
            break;

            case    true:
                timeToggle += K_TEST_GPS_STEP;
                if( timeToggle >= K_TST_GPS_TIME_LO )
                    bDir = false;
                    
            break;
        }//switch
        
    }//if
    
}//genTestGPS

void isr_GPS( void )
{
    static uint32_t
        lastGPS;
    uint32_t
        nowGPS;

    nowGPS = micros();
    
    if( g_isr_bFirst )
    {
        g_isr_bFirst = false;
        lastGPS = nowGPS;
        
    }//if
    else
    {
        g_isr_u32deltaGPS = nowGPS - lastGPS;        
        lastGPS = nowGPS;
        
        g_isr_bEvt = true;
        
    }//else
    
}//isr_GPS

void mphHandler( void )
{
    static uint32_t
        timeMPH = 0ul;
    uint32_t        
        tNow = micros();

    if( g_MPHuS < K_MPH_MIN_US )
    {
        if( (tNow - timeMPH) >= g_MPHuS )
        {
            digitalWrite( pinMPH, digitalRead( pinMPH ) ^ HIGH );
            timeMPH = tNow;
            
        }//if

    }//if
                
}//mphTimer

void calcMPH( void )
{
    bool
        bTmp;
    uint32_t
        u32Tmp;

    noInterrupts();
    bTmp = g_isr_bEvt;
    interrupts();
    
    if( bTmp )
    {
        noInterrupts();
        g_isr_bEvt = false;
        u32Tmp = g_isr_u32deltaGPS;
        interrupts();

        float fGPSSpeed = 1.0 / ((float)u32Tmp / 1.0E6);
        float fMPH = 2.225 * fGPSSpeed;
        g_MPHuS = (uint32_t)((1.0e+6 / fMPH) / 2.0);        

    }//if
    
}//calcMPH

Would this work with other boards or just the mega2560

The only "unique" thing that might differentiate between "Arduinos" is the pin-change interrupt on pin 2. Pin 2 is pretty common across virtually all platforms as being usable for attachInterrupt() this so it should be portable.

I just put 2560 there because that's what I wrote it for and tested it on.

Do you mean external interrupt? PCINT works with almost all pins.