0

I found out the hard way today that pins A6 and A7 can't be used as digital outputs, but what actually happens when you try to read/write them?

Some backstory: I soldered some headers onto a pro mini and I wanted to test all the pins to make sure they worked. I wrote a test program that looped through all the digital pins once a second and toggled their state by doing digitalWrite(pin, !digitalRead(pin);. I then added a second loop that went through pins A0 through A7 and toggled them using the same method. After adding this loop the built in LED on pin 13 no longer toggled on and off every second. Instead, it would just flicker on very briefly. At first I assumed there was something wrong with my loops (like an off by one) that was causing the strange issue, so I completely rewrote things to use a single loop that iterated over all the pins. The resulting code should have been effectively equivalent to the original code, but now pin 13's LED was toggling properly again.

After much experimenting, I was able to whittle it down to the following test program to demonstrate the strangeness:

#define WORKS_CORRECTLY 1

int pins[] = {13, A7};
#define SIZE (sizeof(pins) / sizeof(int))

void setup()
{
#if WORKS_CORRECTLY
    for(int i = 0; i < SIZE; i++)
        pinMode(pins[i], OUTPUT);
#else
        pinMode(pins[0], OUTPUT);
        pinMode(pins[1], OUTPUT);
#endif
}

void loop()
{
    for(int i = 0; i < SIZE; i++)
    {
        int pin = pins[i];
        int pinVal = digitalRead(pin);
        digitalWrite(pin, !pinVal);
    }

    delay(1000);
}

With WORKS_CORRECTLY defined as 1 the LED on pin 13 toggles state every second like one would expect. With it defined as 0, however, it just flickers on very briefly every second. This is very perplexing.

Ultimately the answer is to not try to do a digitalWrite to A7 (or A6), but I'd like to understand why the behavior changes based on seemingly irrelevant differences.

2 Answers 2

3

You are calling digitalWrite and digitalRead with argument A7.

According to the file pins_arduino.h, A7 is defined as 21.

Now, the two functions are defined in file wiring_digital.c, and the most important part is that both of them use this code to understand which register and bit to use:

uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);

Those two are not functions, but macros defined in the Arduino.h file:

#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )

This means that the positions are stored into two arrays, and each pin data is an offset from the beginning of the array itself. This means that the pin 0 is located on register digital_pin_to_port_PGM[0] (i.e. port D) and is in position digital_pin_to_bit_mask_PGM[0] (so bit 0).

Now, what about pin A7? It is in index 21. But look at the file pins_arduino.h:

const uint8_t PROGMEM digital_pin_to_port_PGM[] = {
    PD, /* 0 */
    PD,
    PD,
    PD,
    PD,
    PD,
    PD,
    PD,
    PB, /* 8 */
    PB,
    PB,
    PB,
    PB,
    PB,
    PC, /* 14 */
    PC,
    PC,
    PC,
    PC,
    PC,
};

The array has only 20 entries! This means that A6 and A7 are NOT mapped. When you call the function with A7, you are going beyond the array limits.

This means that this leads to an unexpected behavior. In THIS particular case you are getting

PORT: _BV(1) = 2 which is equal to PB in Arduino.h
BIT: NOT_ON_TIMER = 0

So when you read from bit A7 you will check for

*portInputRegister(port) & bit
*portInputRegister(PB) & 0 => 0 for every value

so it always reads a LOW.

When you write any value to it, you will apply this code:

if (val == LOW) {
    *out &= ~bit;
} else {
    *out |= bit;
}

bit is always 0, so if you write a LOW value you will write a HIGH on ALL bits connected to port B, otherwise you will write a LOW on all of them.

Different is the access to A6: the port will be mapped to 0, which is translated to NOT_A_PORT when detecting the register. This will try to read and write from/to register #0, which I admit I do not know what means. According to the ATMEGA328P register map it is reserved, so better not to use it.

Short answer: never try to use values which are not supposed to be used, otherwise you will incur in strange behaviors

2
  • 1
    Somehow it didn't occur to me to go digging through the arduino source code. Thanks for the very quick and detailed answer and apologies for the delay in accepting it.
    – Pak
    Mar 8, 2017 at 17:36
  • @Pak Well, you accepted it so.. thank you to take the time to come back and accept it ;)
    – frarugi87
    Mar 8, 2017 at 20:56
0

pins A6 and A7 can't be used as digital outputs.

that shows the importance of reading the datasheet (and the schematic): there is no port / pins associated ADC6/A6 and ADC7/A7 on the chip. Absolutely no way of writing to / reading from those pins digitally.

None whatsoever.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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