2

I'm working on an Arduino program but am getting bogged down on what is the 'correct' implementation. I understand that multiple versions may work, so for my needs the 'correct' version will be the fastest (i.e. executing in the fewest clock cycles)

I need to set three analog input pins on my Mega 2560 - let's call them PF2-0.

  • I could use pinMode() - but that performs some extra checks that I want to leave out.
  • I could use _BV() in the process, but that's another function call I wish to avoid.

So, using port manipulation, I can write:

DDRF = 0x07; //PF2, PF1, PF0 as input
PORTF = 0x07; // Disable pullups

This should configure my three pins as inputs - and reduce any extraneous function calls. This should even be faster than writing:

DDRF = (1 << PF2) | (1 << PF1) | (1 << PF0)

Is this the fastest implementation?

*To be honest, I'm not sure how to profile this to know for sure.

  • Fastest way is to write nothing: all the pins start as inputs by default. – Edgar Bonet Apr 8 '16 at 19:25
  • 1
    I could use _BV() in the process, but that's another function call I wish to avoid. - it isn't a function, it's a macro. And the compiler optimizes the results. As Edgar Bonet points out, using it three times in one line and ORing the results together, gives you a single machine instruction which executes in one cycle (plus another to write to the register). You can't improve on that. – Nick Gammon Apr 8 '16 at 23:32
  • 1
    DDRF = 0x07; //PF2, PF1, PF0 as input - surely that makes them outputs? – Nick Gammon Apr 8 '16 at 23:35
  • @NickGammon, you're correct of course - I was confused. – Ramrod Apr 10 '16 at 21:35
4

The standard Arduino way of doing this is to use pinMode(). It's not fast, but it's easy to understand, especially for beginners. And it's portable across architectures.

If you want to go fast, then you should skip the Arduino functions and use port manipulation, and avr-libc. The standard avr-libc way of doing what you want is:

DDRF = _BV(PF2)   // set PF2 as output
     | _BV(PF1)   // also PF1
     | _BV(PF0);  // and PF0

This compiles to two assembly instructions: one for loading a CPU register with the constant, and the other to copy the register to the DDRF IO port. Notice that you set the bits for the pins you want to set as outputs, and you clear the bits for the inputs. By default, all the pins start as inputs.

The _BV() here is not a function call: it's a macro that expands to (1 << (x)). The way you write the expression to the right of the = sign is irrelevant as long as it is recognised to be a compile-time constant. If you want to see for yourself, you can disassemble the generated binary with the command

avr-objdump -S -C my_prog.elf > my_prog.lst

You get an assembly listing interspersed with your source. Doing this on the example above yields:

    DDRF = _BV(PF2)   // set PF2 as output
         | _BV(PF1)   // also PF1
         | _BV(PF0);  // and PF0
100:    87 e0           ldi r24, 0x07   ; 7
102:    80 bb           out 0x10, r24   ; 16

Here, the ldi instruction means “load immediate”, i.e. load a register (r24) with a constant value (0x07). The “out” instruction copies that register into port 0x10 (namely DDRF). Both are single cycle instructions, so the whole sequence takes two CPU cycles.

0

AFAIK the compiler should be smart enough to use bit manipulation instructions when in presence of _BV().

When unsure, you can always add -save-temps to the gcc options, to see what the compiler actually does. It will not give you profiling information, but you can see in detail how your C gets turned into assembly.

Remember to pay attention to what level of optimization you use. You should use the same that will be employed in the final version of your code (probably O2).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.