Frequency Divider Using Uno

Hello all,

I have a frequency divider program that almost works. I’m trying to measure an incoming pulse, and divide the frequency by a designated value “divider” then output the new frequency.

I said it almost works because it seems like the instructions in the loop is causing a delay which is making the output signal unable to have a 50% duty cycle. I’ve tried lengthening and shortening the calculations done in the loop, and I’m pretty sure thats the issue.

Can someone please help me account for this delay? Also, if I were to modify this in the future by using an Arduino Micro, how would this delay differ?

Here’s the code:

/* *** Frequency Divider *** */

unsigned long freq=0;
unsigned long T[]={0};
unsigned long Tavg=0;
unsigned long Tout=0;
unsigned long Tout2=0;
double widthHigh=0;
double widthLow=0;
int divider=10;

#define pini 5
#define pino 7

void setup() {
  Serial.begin(115200);
  pinMode(pini,INPUT);
  pinMode(pino,OUTPUT);
  //noInterrupts();
}

void loop() {
  for (int i=0; i<1; i++){
    Tavg=0;
    widthHigh=pulseIn(pini,HIGH);  // microseconds
    //Serial.print("High: "); Serial.println(widthHigh);
    widthLow=pulseIn(pini,LOW);    // microseconds
    //Serial.print("Low: "); Serial.println(widthLow);
    T[i]=widthHigh+widthLow;   // microseconds
    Tavg=+T[i];
  }
  Tavg=Tavg/1;
  Serial.print("Tavg: "); Serial.println(Tavg);
  Tout=Tavg*divider; // freq divider
  Tout2=Tout/2;   // 50% duty cycle 
  Serial.print("Tout: "); Serial.println(Tout);
  digitalWrite(pino,HIGH);
  delayMicroseconds(Tout2);
  digitalWrite(pino,LOW);
  delayMicroseconds(Tout2);
}

Because you are using the delayMicroseconds() function there will be no variability there. There remainder of the instructions will be faster/slower depending on the clock speed, 8Mhz vs 16Mhz.

I hate to say this, as I don't know how much experience you have plus the general feeling on this forum, but I would suggest looking into the interrupts. Pin interrupt for detecting the edges of the pulse and the timer interrupt for changing the state of the output pin. If you want to get fancier, then another timer interrupt for timing the incoming pulse. A word of caution, the timer setpoints will vary with (or you will need to vary/compensate for) the different clock speeds (8MHz vs 16MHz); which is different than the (blocking) delay functions you are currently using.

either way you will have to use 'micros()' for your timing and hope that the Arduino is quick enough

  digitalWrite(pino,HIGH);
  delayMicroseconds(Tout2);
  digitalWrite(pino,LOW);
  delayMicroseconds(Tout2);

Executing a command like digitalWrite() takes time, you assume it happens instantly.

A hardware frequency divider is better. For example, a [u]Type-T flip-flop[/u] toggles the output-state on every falling-edge on the input. That cuts the frequency exactly in half and puts-out a square wave. And, there are frequency-divider/counter chips which work similarly.

You may use a Timer/Counter (Timer1 is likely the best option). You may set it to be clocked from an external source and generate the divided frequency on an Output Compare pin.

All you need to do to divider a frequency by an integer ratio is count edges. Use a ISR if you
like for best response time, something like this:

byte N = 3 ; // divider ratio, 1 or more, unsigned

void pin_handler()
{
  static volatile byte phase = 0 ;
  static volatile boolean outstate = false ;

  phase += 1 ;
  if (phase == N)
  {
    phase = 0 ;
    digitalWrite (outpin, (outstate = !outstate)) ;
  }
}

void setup()
{
  attachInterrupt (channel, pin_handler, CHANGE) ;  // catch every edge
}

Details are probably random ,this is a suggestion of method, not working or tested code

For another example doing something similar look at AC phase cutting code. It uses interrupts to detect the zero crossing, and then sets timer interrupts to produce an output pulse.

Interrupts are a good use for this, also much higher resolution than the delayMicroseconds() function can offer: you can go down to 62.5 ns resolution (single clock pulse on a 16 MHz Arduino).