Pulse train frequency problem

Hello All,

Greetings! This is my first time programming an Arduino, and am having fun! The code is supposed to generate a sequence of pulses that have variable frequency, duty cycle, and total pulse number. I modified some servo controller code I found online to produce the following results.

With frequency = 2000 Hz and dutyCycle = .25, I actually get out (scoped from pin 7) 1926Hz and 25.35% duty cycle. As shown in the first picture it's pretty close!

The problem arises when I try to increase the frequency and duty cycle to 20kHz, and 0.95 duty cycle. As shown in the second picture, I am way off, 14.47kHz, and 78.3%!!

The error steadily increases as I increase frequency. Is there a problem in my arithmetic, or what data type I used to define the variables? Am I doing something stupid? Any help would be greatly appreciated. Thanks!

Pictures coming on the next post...

Hi Everyone, here is my code for the previous post. Sorry to split it up like this.

//Adapted from http://todbot.com/
//Generates pulse train of specified pulse number, frequency, and duty cycle
//Pin 7 output 

int pulseNumber = 2500;          // Number of pulses in pulse train                 
double frequency = 2000;            //frequency in Hz                              
float dutyCycle = .25;                //duty cycle                                     
unsigned long seconds = 1;        //delay between pulse sets                         

float on;
float off;                      
float period;


void setup() {
  pinMode(7, OUTPUT);          // set outPin pin as output
}

void loop() {
period = (1 / frequency) * 1000000;
on = dutyCycle * period;
off = period * (1-dutyCycle);

  // call Pulse function for n = pulseNumber
  for (int i=1; i<=pulseNumber; i++) {  
    Pulse(on, off);
  }
  
  delay(seconds * 1000UL);  // delay between pulse sets
} 

void Pulse(double on, double off) {
  digitalWrite(7, HIGH);       // set Pin high
  delayMicroseconds(on);      // waits "on" microseconds
  digitalWrite(7, LOW);        // set pin low
  delayMicroseconds(off);      //wait "off" microseconds
}

Picture 1

Picture 2

digitalWrite can take almost 5 microseconds to turn a pulse on and off, this may be a cause of the error you are seeing.

Below is some code that uses direct port io that take 125 nanoseconds to turn the pulse on and off. It uses an arduino friendly macro called fastWrite. The rest of the sketch can be the same as the one posted above.

#define fastWrite(_pin_, _state_) ( _pin_ < 8 ? (_state_ ?  PORTD |= 1 << _pin_ : PORTD &= ~(1 << _pin_ )) : (_state_ ?  PORTB |= 1 << (_pin_ -8) : PORTB &= ~(1 << (_pin_ -8)  )))
// the macro sets or clears the appropriate bit in port D if the pin is less than 8 or port B if between 8 and 13

 void Pulse(double on, double off) {
  fastWrite(7, HIGH);       // set Pin high
  delayMicroseconds(on);      // waits "on" microseconds
  fastWrite(7, LOW);        // set pin low
  delayMicroseconds(off);      //wait "off" microseconds
}

The other thing that is slowing your sketch down is the floating point. Try it by passing an int or long to Pulse.

Thanks mem! I will give that a try.

Hello all,

FastWrite is definitely faster than digitalWrite. I have taken mem's suggestions and have documented the progress below.

First change: use fastWrite macro instead of digitalWrite to generate a 20kHz pulse train. Using fastWrite, the pulses are now at 16.47kHz (better than 14.47kHz using digitalWrite)......but still not 20kHz.

Second change: use int instead of float to define variables "on" and "off". The pulses are now zipping out of pin 7 at 19.61kHz! Thats close enough for me.


Sweeeeeeeet.

The final code is shown below.

//Generates pulse train of specified pulse number, frequency, and duty cycle
//Pin 7 output 

int pulseNumber = 25000;          // Number of pulses in pulse train                 
double frequency = 20000;            //frequency in Hz                              
double dutyCycle = .25;          //duty cycle                                        
unsigned long seconds = 1;        //delay between pulse sets                         

int on;
int off;                    
double period;  

#define fastWrite(_pin_, _state_) ( _pin_ < 8 ? (_state_ ?  PORTD |= 1 << _pin_ : PORTD &= ~(1 << _pin_ )) : (_state_ ?  PORTB |= 1 << (_pin_ -8) : PORTB &= ~(1 << (_pin_ -8)  )))
// the macro sets or clears the appropriate bit in port D if the pin is less than 8 or port B if between 8 and 13


void setup() {
  pinMode(7, OUTPUT);          // set outPin pin as output
}

void loop() {
period = (1 / frequency) * 1000000;
on = dutyCycle * period;
off = period * (1-dutyCycle);

  // call Pulse function for n = pulseNumber
  for (int i=1; i<=pulseNumber; i++) {  
    Pulse(on, off);
  }
  
  delay(seconds * 1000UL);  // delay between pulse sets
} 

void Pulse(int on, int off) {
  fastWrite(7, HIGH);       // set Pin high
  delayMicroseconds(on);      // waits "on" microseconds
  fastWrite(7, LOW);        // set pin low
  delayMicroseconds(off);      //wait "off" microseconds
}

Those two suggestions did the job. Thanks again for the help.

Good to hear you have it going :slight_smile:

You could get it even closer if you moved the code in the Pulse function into the for loop.

Out of curiousity what are you using as an oscilliscope?

I splurged last year for my birthday and got a used Tektronix TDS3034 off ebay. Those pictures you saw were screen captures saved to the good ole onboard floppy drive. Funny story, I recently purchased a dell desktop, and had to pay extra for a floppy drive. I opted for it thinking "how am I going to get screen captures from my scope with no floppy?" We have one of the latest tek scopes at work, the TDS 3052C, and it has a usb jack for a thumb drive instead of a floppy! nice!

I used http://www.picamatic.com/ to link to the pics.

Great. I am after a cheapo oscilliscope myself. I will check out what's on eBay.