Duty Cycle of generated signal too low

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:

void loop(){
  digitalWrite(7,HIGH);
  digitalWrite(7,LOW);
}

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!

signal_001.jpg

signal_002.jpg

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

Lefty

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?

signal_003.jpg

a variation you can try

#include <avr/io.h>

void setup()
{
    DDRD |=  (1<<PIND7);// declare output
}

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

robtillaart:
a variation you can try

#include <avr/io.h>

void setup()
{
   DDRD |=  (1<<PIND7);// declare output
}

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.

maybe some asm instruction

robtillaart:
maybe some asm instruction

Repeat for edited prior port:

I think for example PINB = 0x20; toggles a bit in a single AVR instruction if the pin has been set to output mode.

Lefty

How does

void loop(){
  while (1){
    PORTD |= (1<<PIND7);//Set PIND7 to HIGH
    PORTD &= ~(1<<PIND7);//Set PIND7 to LOW
  }
}

look?

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

signal_noLoop.jpg

insert some assembly code to balance the duty cycle

#include <avr/io.h>

void setup()
{
    DDRD |=  (1<<PIND7);// declare output
}

void loop()
{
  uint8_t mask1 = (1<<PIND7);//Set PIND7 to HIGH
  uint8_t mask2 = ~(1<<PIND7);//Set PIND7 to LOW
  while(1)
  {
    PORTD |= mask1;
    asm("nop");          // dont know the exact syntax
    asm("nop");
    PORTD &= mask2;
  }
}

robtillaart:

#include <avr/io.h>

asm("nop");

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!

none of which seems to hold.

That's because all the other stuff added by arduino.

Try instead:

  PORTD ^=  (1<<PIND7);//Flip PIND7

If you have no use for the other arduino stuff, try this:

void loop(void) {
  while (1) PORTD ^= (1<<PIND7); //flip pd7
}

You will see a much faster signal on pin7.

dhenry:

none of which seems to hold.

That's because all the other stuff added by arduino.

Which other things in particular?

dhenry:
Try instead:

  PORTD ^=  (1<<PIND7);//Flip PIND7

I tried it and it yields a duty-cycle of 50.0% at 532kHz.

Removing the loop method, i.e. using the code:

void loop()
{
  const uint8_t mask = (1<<PIND7);
  while(1){
    PORTD ^=  mask;//Flip PIND7
  }
}

leads to a signal with frequency 1.597MHz and DC 50.0%.

dhenry:

none of which seems to hold.

That's because all the other stuff added by arduino.

Try instead:

  PORTD ^=  (1<<PIND7);//Flip PIND7

Or even:

PIND = (1<<PIND7);//Flip PIND7

That eliminates the read of PORTD

just for the fun of it, you might replace the conditional while with the unconditional goto.

#include <avr/io.h>

void setup()
{
    DDRD |=  (1<<PIND7);
}

void loop()
{
  uint8_t mask = (1<<PIND7); 
label:
  PORTD ^=  mask;
goto label;
}

What are the numbers? Or was de compiler already optimizing to this level?

leads to a signal with frequency 1.597MHz and DC 50.0%.

The frequency is higher due to not having to deal with the arduino stuff - see the generated main.c/cpp for example.

The duty cycle is exactly 50% because it is simply flipping a pin - so perfectly symmetrical.