9

Related to: What happens if there is a runtime error?

This question is similar to the one above, however this is an alternate situation:

int pin = 999;
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);

What would happen in this instance? The compiler might catch it but if you used a random number would the IDE catch it?

9

The compiler will not detect any error and the code will compile and execute. Hence, to see what happens we need to explore the behind-the-scenes magic. For a summary, skip to end.


The second line in your code is where the magic will happen and thats where we need to focus.

pinMode(pin, OUTPUT);

The portion of pinMode relevant to this discussion is:

void pinMode(uint8_t pin, uint8_t mode) 
{

    uint8_t bit = digitalPinToBitMask(pin); //The first instance where pin is used
    uint8_t port = digitalPinToPort(pin);

    if (port == NOT_A_PIN) return;

//Do something
}

(The complete implementation can be found in wiring_digital.c)

So, here, digitalPinToBitMask seems to be using pin to compute an intermediate bit. Exploring further, digitalPinToBitMask is a macro defined in Arduino.h whose definition is this one-liner:

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

This weird looking one liner does a very simple task. It indexes the Pth element in the array digital_pin_to_bit_mask_PGM and returns it. This array digital_pin_to_bit_mask_PGM is defined in pins_arduino.h or the pin map for the specific board being used.

const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
    _BV(0), /* 0, port D */
    _BV(1),
    _BV(2),
    _BV(3),
    _BV(4),
    _BV(5),
    _BV(6),
    _BV(7),
...
};

This array has 20 elements in total, so we are out of luck. 999 will index a memory location in the flash memory outside of this array, thereby leading to unpredictable behavior. Or will it?

We still have another line of defense against runtime anarchy. Its the next line of the function pinMode:

uint8_t port = digitalPinToPort(pin);

digitalPinToPort takes us along a similar path. It is defined as a macro along with digitalPinToBitMask. Its definition is:

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

Now, we index the Pth element of digital_pin_to_port_PGM which is an array defined in the pin map:

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

This array contains 20 elements, so 999 is again out of range. Again, this command reads and returns a value from flash memory of whose value we cannot be certain. This will again lead to unpredictable behavior from here on.

There is still one last line of defense. That is the if check in pinMode on the return value of digitalPinToPort:

if (port == NOT_A_PIN) return;

NOT_A_PIN is defined as 0 in Arduino.h. So, if the returned byte from digitalPinToPort happens to be zero, then pinMode will silently fail and return.

In any case, pinMode cannot save us from anarchy. 999 is destined to result in doom.


TL;DR, the code will execute and the result of this will be unpredictable. Most likely, no pin will be set to OUTPUT, and digitalWrite will fail. If you happen to have exceptionally bad luck, then a random pin may get set to OUTPUT, and digitalWrite may set it to HIGH.

  • It's interesting there is no bounds checking. digitalWrite is so slow and bulky anyway, it wouldn't be awkward to put in either compile time or run time checks. – Cybergibbons Feb 18 '14 at 8:34
  • If all arduino pins are in a contiguous range, than couldn't they replace the port==not a pin check with a pin>BOARD_MAX_PIN check, where board max pin is defined in some header file based on some ifdef that detects the board? – EternityForest Feb 22 '14 at 12:05
  • You are forgetting that 999 cannot be represented in a uint8_t so it would first be converted to 231 by the code calling pinMode. The end result is the same: pinMode and digitalWrite will have unpredictable behavior and could clobber random parts of memory if you call them with a bad pin argument. – David Grayson Jan 21 '15 at 19:28
3

In the standard libraries, there are macros designed for converting pins to ports, which are used in assembly. Here they are for the Uno from Arduino 1.0.5:

#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0))
#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1))
#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)0))))
#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14)))

There are more, but I won't show them here.

I believe your program would subtract 14 from 999, which would still be too big for the brogram. It would then try to point to the 985th element of the digital_pn_to_bit_mask_PGM array, which only contains 20 elements. This would most likely end up screwing the Arduino by pointing to a random spot in progmem.

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.