1

Consider this code:

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

On an Arduino Mega 2560, running 16MHz (=0.06us), I would expect the width of the pulse to be somewhere around 0.1us.

However, when measuring with my oscilloscope, I get around 4us high and 5-6 us low. I understand it could take some cycles to run the digitalWrite code, but this seems quite a lot.

How is that difference explained?

jsotola
  • 1,554
  • 2
  • 12
  • 20

2 Answers2

3

You do not need to go all the way down to assembly in order to get that speed. You can do direct port access from your .ino file:

void setup() {
    DDRA |= _BV(PA0);  // pin 22 = PA0 as output
    for (;;) {
        PINA |= _BV(PA0);  // toggle PA0
    }
}

void loop(){}

This compiles to something that is almost equivalent to your assembly code. Actually, it is a bit faster, as it uses rjmp instead of the slower jmp instruction.

Edit: A few notes

  1. As pointed out by timemage in a comment, you can save another CPU cycle by writing PINA = instead of PINA |=.

  2. This code, as well as your two examples, will exhibit a glitch every 1,024 µs. This is caused by the periodic timer interrupt used by the Arduino core for timekeeping (millis(), micros() and delay()). You can avoid the glitch by disabling interrupts before going into the tight loop. Alternatively, if you do not use the Arduino core at all, you can define a function called main() instead of setup() and loop(): this will completely remove the Arduino core initialization for your compiled program.

  3. arduino-cli is useful for sparing you the complexity of the Arduino build system (automatic installation of the cores and libraries, libraries in multiple places that depend on the core you use...). If you do not use the Arduino core, arduino-cli is of little use: a very simple Makefile that calls avr-gcc and avrdude is all you need for basic AVR development.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
0

I fixed it by using assembly code to run the program. Because I am using Linux, I couldn't use Atmel Studio, but you can easily use AVR assembly with arduino-cli.

Make your .ino like this:

extern "C" void myAssemblyMain();

void setup() { myAssemblyMain(); } void loop() { }

then add a pulse.S file with the assembly code:

#define __SFR_OFFSET 0

#include "avr/io.h"

.global myAssemblyMain

myAssemblyMain: sbi DDRA, 0 ; Set PA0 as output

blink: sbi PINA, 0 ; Toggle PINB jmp blink

Using this code, I get a ~400ns pulse. Which is more than enough for my needs.