I wrote the following code for my Arduino Uno rev3 which is supposed to generate a high-frequency pulse at pin 7. Here's my code:
#include <avr/io.h>
void setup(){
DDRD |= (1<<PIND7);// declare output
}
void loop(){
PORTD |= (1<<PIND7);//Set PIND7 to HIGH
PORTD &= ~(1<<PIND7);//Set PIND7 to LOW
}
I was expecting two properties for the generated signal none of which seems to hold. First, that the duty cycle is 50%. The duty cycle of the generated signal is 13% (far from what I expected). And second that the frequency would be close to 8MHz (half the frequency of the crustal that drives the chip). I attach the file signal_001.jpg which depicts the signal I get from my oscilloscope.
Second, I did the same using the command digitalWrite of Arduino. My loop is now as simple as:
The duty cycle of my signal is now close to 50% (46.7%). But the frequency is just 116.8kHz! See the attached file signal_002.jpg.
I am wondering why the duty cycle of the signal produced by the first program is so low... I know that the best way to produce signals is to use timer interrupts, but, again, to me it doesn't make sense why this code performs so poorly!
In your first example what you are seeing is the time period between the two output statements (the high portion) while the low portion is showing the time requirement for the main loop function to cycle around again to the first output statement. There are better methods to generate squarewave output signals using the AVR on chip timers if highest frequency and good duty cycle balance are required. Your second method is indirectly showing you how slow the digitalWrite statements really are that the main loop cycle time is only negatively effecting the duty cycle balance by 3-4%!
retrolefty:
In your first example what you are seeing is the time period between the two output statements (the high portion) while the low portion is showing the time requirement for the main loop function to cycle around again to the first output statement.
I verified that the time needed for the loop to restart is greater than the time between arguments to execute. I tried out the following piece of code:
void loop(){
PORTD |= (1<<outputPin); //output is initially off
PORTD &= ~(1<<outputPin); //output is initially off
PORTD |= (1<<outputPin); //output is initially off
PORTD &= ~(1<<outputPin); //output is initially off
}
and this is what I get (signal_003.jpg)...
Still, it seems strange to me that in the first example that I presented the frequency is 1.000MHz! Why 1??? Could we have predicted this frequency somehow?
void loop()
{
uint8_t mask1 = (1<<PIND7);//Set PIND7 to HIGH
uint8_t mask2 = ~(1<<PIND7);//Set PIND7 to LOW
while(1)
{
PORTD |= mask1;
PORTD &= mask2;
}
}
this removes the function call to loop() and precomputes the masks
please post the graph/ % if possible
Good job, that's nice, cleaver, and simple to understand. Also isn't there a 'trick' I/O instruction using the pins statement while the pin is in output mode that simply toggles the output bit in a single instruction, that might double the speed, no? I think for example PINB = 0x20; toggles a bit in a single AVR instruction if the pin has been set to output mode.
robtillaart:
This removes the function call to loop() and precomputes the masks.
Please post the graph/ % if possible
Thanks for the tip! Indeed, this removes the loop. As a result, the duty cycle increases to 33% (+/-0.50%) which I guess is because of the overhead imposed by the while-loop. The frequency has now increased to about 2.66MHz (+/- 0.35%). I attach the relevant graph.
Update: the following code:
while(1) {
PORTD &= mask2;
PORTD |= mask1;
}
,where we apply first mask2 and then mask1, yields - as expected - a duty cycle of 66.7%. It is the while-loop after all...
This is kind of a workaround to use the no-operation assembly directive to achieve a duty-cycle that is closed to the desired one. Using two nops however yields the desired 50% DC!