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