Что произойдет, если я использую неверный пин-код?

9

Связано с: Что произойдет, если возникнет ошибка во время выполнения?

Этот вопрос похож на приведенный выше, однако это альтернативная ситуация:

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

Что будет в этом случае? Компилятор может поймать его, но если вы используете случайное число, то его поймает IDE?

Анонимный пингвин
источник

Ответы:

9

Компилятор не обнаружит никаких ошибок, а код скомпилируется и выполнится. Следовательно, чтобы увидеть, что происходит, нам нужно исследовать закулисную магию. Для краткого изложения, пропустите до конца.


Вторая строка в вашем коде - это то, где произойдет волшебство, и именно на этом мы должны сосредоточиться.

pinMode(pin, OUTPUT);

Часть, pinModeотносящаяся к этому обсуждению:

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
}

(Полная реализация может быть найдена в wiring_digital.c )

Итак, здесь, digitalPinToBitMaskкажется, используется pinдля вычисления промежуточного бита. Далее digitalPinToBitMaskрассматривается макрос, в определении Arduino.hкоторого указана эта строка:

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

Этот странно выглядящий лайнер выполняет очень простую задачу. Он индексирует P- й элемент в массиве digital_pin_to_bit_mask_PGMи возвращает его. Этот массив digital_pin_to_bit_mask_PGMопределен в pins_arduino.hили карте выводов для конкретной используемой платы.

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),
...
};

Всего в этом массиве 20 элементов, поэтому нам не повезло. 999 будет индексировать область памяти во флэш-памяти за пределами этого массива, что приведет к непредсказуемому поведению. Или это будет?

У нас все еще есть другая линия защиты от анархии во время выполнения. Это следующая строка функции pinMode:

uint8_t port = digitalPinToPort(pin);

digitalPinToPortведет нас по тому же пути. Он определяется как макрос вместе с digitalPinToBitMask. Его определение таково:

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

Теперь мы индексируем P- й элемент, digital_pin_to_port_PGMмассив которого определен в карте выводов:

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

Этот массив содержит 20 элементов, поэтому 999 снова выходит за пределы допустимого диапазона. Опять же, эта команда читает и возвращает значение из флэш-памяти, значение которого мы не можем быть уверены. Это снова приведет к непредсказуемому поведению.

Есть еще одна последняя линия защиты. Это ifпроверка pinModeвозвращаемого значения digitalPinToPort:

if (port == NOT_A_PIN) return;

NOT_A_PINопределяется как 0 в Arduino.h. Таким образом, если возвращенный байт from digitalPinToPortокажется равным нулю, pinModeпроизойдет молчание и произойдет возврат.

В любом случае, pinModeне может спасти нас от анархии. 999 суждено привести к гибели.


TL; DR, код будет выполнен, и результат этого будет непредсказуемым. Скорее всего, ни один из выводов не будет установлен на OUTPUTи digitalWriteвыйдет из строя. Если вам случается исключительно неудача, то случайный пин-код может быть установлен на OUTPUTи digitalWriteможет быть установлен на HIGH.

asheeshr
источник
Интересно, что нет проверки границ. В любом случае, digitalWrite настолько медленный и громоздкий, что было бы неудобно вставлять проверки времени компиляции или выполнения.
Cybergibbons
Если все контакты Arduino находятся в смежном диапазоне, то не могли бы они заменить порт ==, а не проверку вывода на проверку pin> BOARD_MAX_PIN, где максимальный вывод платы определен в некотором заголовочном файле на основе некоторого ifdef, который обнаруживает плату?
EternityForest
Вы забываете, что 999 нельзя представить в виде, uint8_tпоэтому сначала он будет преобразован в 231 с помощью вызова кода pinMode. Конечный результат то же: pinModeи digitalWriteбудет иметь непредсказуемые и может затирать случайные части памяти , если вы их называете с плохим контактным аргументом.
Дэвид Грейсон
3

В стандартных библиотеках есть макросы, предназначенные для преобразования выводов в порты, которые используются в сборке. Вот они для Uno от 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)))

Есть еще, но я не буду показывать их здесь.

Я полагаю, что ваша программа вычтет 14 из 999, что все равно будет слишком большим для программы. Затем он попытается указать на 985-й элемент digital_pn_to_bit_mask_PGMмассива, который содержит только 20 элементов. Скорее всего, это приведет к закручиванию Arduino, указывая на случайное место в программе.

Доктор
источник