Що станеться, коли я використаю недійсний номер PIN-коду?


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. Отже, якщо повернутий байт з digitalPinToPortнуля буде нульовим, то pinModeвін мовчки вийде з ладу і повернеться.

У будь-якому випадку pinModeне може врятувати нас від анархії. 999 призначено призвести до приреченості.


TL; DR, код буде виконуватися, і результат цього буде непередбачуваним. Швидше за все, жоден штифт не буде встановлено OUTPUT, і digitalWriteвін не вийде. Якщо у вас є надзвичайно невдача, то випадковий штифт може встановитись OUTPUTі digitalWriteможе встановити його HIGH.


Цікаво, що немає перевірки меж. digitalWrite все одно настільки повільний і громіздкий, що було б незручно вводити ні час компіляції, ні запуску перевірок часу.
Кібергіббони

Якщо всі шпильки arduino знаходяться в суміжному діапазоні, то не могли б вони замінити порт == не штифт перевірити шпилькою> BOARD_MAX_PIN check, де 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, вказавши на випадкове місце в програмі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.