Search code examples
cavr

AVR define pins with struct or #define


I've seen many people say "always use a #define to define the pins and ports of something in a library" and currently, this is what i do, too. But i am considering using a struct instead of this method.

I use the example of a shiftregister and it's library

What i see as pro

  • Easy to handle many shiftregister with one library
  • You don't need to change anything in the source-files of the library

What i see as con

  • Takes up space (unlike a #define)
  • Could take more time to compute because you have to consider that different ports could be used for each shiftregister and you'd have to check that

Do you know any more pros or cons for using a struct instead of some #defines? And would you consider the cons as something that overweights the pros heavily enough, so that you would never use a struct (not even when it is not time or space-critical)?


Solution

  • The following code sets an AVR pin (PD1) to drive high (assuming it was already enabled as an output):

    PORTD |= (1 << 1);
    

    The AVR GCC compiler is good at compiling code like this into a single AVR assembly instruction (named sbi), but only if it knows at compile time exactly what register you are writing to (PORTD), and what bit you are setting (bit 1).

    There are many advantages to using one instruction instead of mulitple instructions to set the bit:

    • It takes less code space.
    • It runs faster.
    • It is safe to do even if an interrupt might run which modifies the same register, since the read, modify, and write steps are all happening in a single instruction that cannot be interrupted.

    If you use preprocessor macros defined with #define to define your pins, then after the preprocessor runs, your code will look just like the code above, and should compile to a single instruction.

    On the other hand, if you use structs to define pins, and pass those structs dynamically to your library, there is a good chance that the compiler will not be able to do this optimization.

    Here is a link where you can see both methods of setting a pin and see that the struct method is a lot less efficient:

    https://godbolt.org/g/mkxHc2

    Note that there are many other ways to specify and manipulate pins besides the two methods discussed here. Arduino libraries usually represent pins as a single number, and then call functions like pinMode and digitalWrite to manipulate the specified pin. (The implementations of pinMode and digitalWrite are pretty inefficient and have to disable interrupts, but they could be improved.)

    A more advanced technique is to use C++ template parameters to specify pins, like the FastGPIO library does.