As I explained in my comment, the reason for this glitch is the Timer 0
overflow interrupt kicking in every 1024 µs. This interrupt is used
by the Arduino core library for timekeeping. It is the basis of
millis()
, micros()
and delay()
. The interrupt is enabled by the
init()
function in
wiring.c.
There are several approaches to avoid that:
Avoid the Arduino core initialization
This may be overkill, but since it seems you are already familiar with
directly accessing the IO ports of the ATmega chip, it may well suit
you. You can use the Arduino IDE without the Arduino core initialization
simply by defining your own main()
:
#include <util/delay.h>
int main(void)
{
DDRD |= _BV(PD2); // PD2 as output
for (;;) {
PORTD |= _BV(PD2); // PD2 high
PORTD &= ~_BV(PD2); // PD2 low
_delay_us(0.625); // 10 CPU cycles, loop time = 16 cycles
}
}
Notice the extra delay: without it, the loop would complete in 6 CPU
cycles (2 per port access, 2 more to loop back). Not doing the Arduino
core initialization means you have no timekeeping and no analogRead()
or analogWrite()
. Other Arduino functions should work normally but
some, like Serial.begin()
will bring their own interrupts.
Disable interrupts
As suggested by Majenko, this may be the simplest way to have a steady
waveform:
void setup()
{
pinMode(2, OUTPUT);
noInterrupts();
}
void loop()
{
PORTD |= _BV(PD2); // PD2 high
PORTD &= ~_BV(PD2); // PD2 low
}
Without interrupts, you have no timekeeping, no serial communications
and, obviously, no attachInterrupt()
. Most other Arduino functions
should work.
Use a hardware timer
This allows you to generate the signal without relying on the CPU. You
can keep interrupts enabled and do other work in the program while the
waveform goes undisturbed. The simplest way to generate such a waveform
is with analogWrite()
. The resulting frequency is quite low though,
like 0.5 to 1 kHz. If you want 1 MHz, you will have to
configure the timer the hard way. For example:
void setup()
{
DDRD |= _BV(PD3); // OC2B = PD3 as output
OCR2A = 15; // period = 16 clock cycles
OCR2B = 1; // high during 2 clock cycles
TCCR2A = _BV(COM2B1) // non-inverting PWM
| _BV(WGM20) // fast PWM, TOP = OCR2A
| _BV(WGM21); // ditto
TCCR2B = _BV(WGM22) // ditto
| _BV(CS20); // clock at f_CPU
}
void loop() { }
The output is on pin PD3 = digital 3. This configuration will disable
analogWrite()
on pins 3 (PD3) and 11 (PB3).
OCR2A = 1; OCR2B = 0;
. – Edgar Bonet Apr 7 '16 at 20:04