Go Down

Topic: Frequency Divider Using Uno (Read 141 times) previous topic - next topic

gpelon

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:

Code: [Select]
/* *** 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);
}

adwsystems

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.

Deva_Rishi

either way you will have to use 'micros()' for your timing and hope that the Arduino is quick enough
Code: [Select]
 
  digitalWrite(pino,HIGH);
  delayMicroseconds(Tout2);
  digitalWrite(pino,LOW);
  delayMicroseconds(Tout2);
Executing a command like digitalWrite() takes time, you assume it happens instantly.
To 'Correct' you have to be Correct. (and not be condescending..)

DVDdoug

A hardware frequency divider is better.   For example, a Type-T flip-flop 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.

Smajdalf

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.

MarkT

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:

Code: [Select]

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
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

wvmarle

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).
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Go Up