Erroneous pin numbering on Arduino Leonardo

I’m using the newest version of PlatformIO with VS Code on Ubuntu 18.04.5. My project targets Arduino Leonardo, here is the configuration file:

[env:leonardo]
platform = atmelavr
board = leonardo
framework = arduino
lib_deps = 
	bogde/HX711@^0.7.4
	platformio/Streaming@0.0.0-alpha+sha.5
	rocketscream/Low-Power@^1.6

In this code snippet:

#include <Arduino.h>
#include <Streaming.h>
#include "HX711.h"
#include "LowPower.h"

constexpr int ENA = PB6;
constexpr int IN1 = PE6;

both PB6 and PE6 have value 6, but PB6 represents pin 30 and PE6 represents pin 1, according to https://www.arduino.cc/en/Hacking/PinMapping32u4. How can I fix this problem?

I created an issue on github: Erroneous pin numbering on Arduino Leonardo · Issue #4025 · platformio/platformio-core · GitHub.

  1. The pin numberings have nothing to do with PlatformIO, why open an issue there? If at all, it would be an issue in the Arduino core.
  2. You are using the Arduino core in a wrong way, or rather, expecting macros to have a certain value when they don’t. Ctrl+Click on “PB6” or “PE6” and see where they go to – They go in the toolchain, iom32u4.h
#define PORTB _SFR_IO8(0x05)
#define PORTB0 0
#define PORTB1 1
#define PORTB2 2
#define PORTB3 3
#define PORTB4 4
#define PORTB5 5
#define PORTB6 6
#define PORTB7 7

..
#define PORTE _SFR_IO8(0x0E)
#define PORTE2 2
#define PORTE6 6
#elif defined(PORTB6) && !defined(PB6)
#  define PB6 PORTB6
#endif
...
#elif defined(PORTE6) && !defined(PE6)
#  define PE6 PORTE6
#endif

As you can see these are definitions for the special-function-register (SFR) regarding the PORTx register.

They have nothing to do with the Arduino pin labeling. You must not use PB6 or PE6 and similiar as arguments to Arduino-core functions like pinMode(), digitalWrite() etc.

Look at the documentation for any of these functions, say pinMode().

Parameters

  • pin: the Arduino pin number to set the mode of.
  • mode: INPUT, OUTPUT, or INPUT_PULLUP. See the Digital Pins page for a more complete description of the functionality.

They want the Arduino pin number. The Arduino core has a pin-remapping logic in place where you need to use their Arduino pin numbering system, and internally they will remap it to the right PORTx and pin value.

This is publically visible in the implementation. Take e.g.

The Arduino pin number pin is used as argument in digitalPinToPort and digitalPinToBitMask.

These macros read from certain remapping arrays, defined per-variant, e.g. digital_pin_to_port_PGM for digitalPinToPort. So for the variant/leonardo/pins_arduino.h variant we can read

Thus you can see that the index = 7 maps, by digital_pin_to_port_PGM, to Port E, and via digital_pin_to_bit_mask_PGM, to _BV(6) which is (1 << 6), which refers to the 6th bit from the right in a bitmask, aka, the 6th pin. In total we get the “PE6” pin (but not that macro!). Since index 7 maps to these pins, we just found out that “Arduino pin digital 7” maps to PE6.

And this information is exactly what is relayed to you in the link that you’ve posted.

So, don’t write

If you are using these with Arduino API functions, write

constexpr int ENA = 10; //On the Leonardo, digital pin 10 is PB6;
constexpr int IN1 = 7; //On the Leonardo, digital pin 7 is PE6;

And finally as 3.,

There are literally no restrictions on what numbers to use for representing a pin, in particular it doesn’t have to be a package pin number. Arduino chose the way of refering to pins as “digital pin number X” from 0 to n, and internally remapping them. All Arduino cores work this way. On some others, like the STM32 one, they do have nicer convienience macros and additional types for the pins – only there, macros like PB2 actually are also the right pin number macros to refer to that pin. Not so in AVR.

1 Like

Related question where people thought that the Arduino APIs and pin macros behave the same way

The same explaining done for an ATTiny chip

1 Like

Thank you. I was just guessing with these macros. I was expecting better naming uniformity, e.g. D0, D1, … and A0, A1, … as used in the documentation of Leonardo clone I’m working with right now. When that failed and a few sources I checked didn’t contain pin naming information, I thought the macros are equivalent to pin names used in microcontroller datasheets. I think repurposing standard pin names is a bad design decision.