Go Down

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

#### shift1001

##### Nov 10, 2008, 05:11 am
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

#1
##### Nov 10, 2008, 05:13 am
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 amLast 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

#3
##### Nov 11, 2008, 06:31 am
Thanks mem!  I will give that a try.

#### shift1001

#4
##### Nov 12, 2008, 10:24 am
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 13void 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

#5
##### Nov 12, 2008, 10:52 am
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

#6
##### Nov 12, 2008, 02:43 pm
Out of curiousity what are you using as an oscilliscope?

#### shift1001

#7
##### Nov 13, 2008, 06:33 am
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

#8
##### Nov 13, 2008, 02:08 pm
Great. I am after a cheapo oscilliscope myself. I will check out what's on eBay.

Go Up