Go Down

Topic: Pulse train frequency problem (Read 7895 times) previous topic - next topic

shift1001

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...




shift1001

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



Code: [Select]
//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




mem

#2
Nov 10, 2008, 06:09 am Last Edit: Nov 10, 2008, 06:13 am by mem Reason: 1
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.
Code: [Select]

#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.

shift1001

Thanks mem!  I will give that a try.  


shift1001

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.  

Code: [Select]
//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.  




mem

Good to hear you have it going  :)

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

zooto68

Out of curiousity what are you using as an oscilliscope?

shift1001

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.  

zooto68

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

Go Up