Co się stanie, gdy użyję nieprawidłowego numeru PIN?

9

Powiązane z: Co się stanie, jeśli wystąpi błąd środowiska wykonawczego?

To pytanie jest podobne do powyższego, ale jest to sytuacja alternatywna:

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

Co by się stało w tym przypadku? Kompilator może go złapać, ale jeśli użyjesz losowej liczby, czy IDE go złapie?

Anonimowy pingwin
źródło

Odpowiedzi:

9

Kompilator nie wykryje żadnego błędu, a kod zostanie skompilowany i wykonany. Dlatego, aby zobaczyć, co się stanie, musimy odkryć magię zakulisową. Aby podsumować, przejdź do końca.


Druga linia w twoim kodzie to miejsce magii i tam musimy się skupić.

pinMode(pin, OUTPUT);

Część pinModeistotna dla tej dyskusji to:

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
}

(Pełną implementację można znaleźć w wiring_digital.c )

digitalPinToBitMaskWydaje się , że tutaj używa się pindo obliczenia bitu pośredniego. Dalsza eksploracja digitalPinToBitMaskto zdefiniowane makro, Arduino.hktórego definicją jest ten jednoliniowy:

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

Ta dziwnie wyglądająca jedna wkładka wykonuje bardzo proste zadanie. Indeksuje Pty element w tablicy digital_pin_to_bit_mask_PGMi zwraca go. Ta tablica digital_pin_to_bit_mask_PGMjest zdefiniowana w pins_arduino.hlub mapie pinów dla używanej konkretnej płytki.

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

Ta tablica ma w sumie 20 elementów, więc nie mamy szczęścia. 999 zindeksuje miejsce w pamięci flash poza tą tablicą, co prowadzi do nieprzewidzianych zachowań. Czy to będzie?

Nadal mamy inną linię obrony przed anarchią środowiska uruchomieniowego. Jest to następny wiersz funkcji pinMode:

uint8_t port = digitalPinToPort(pin);

digitalPinToPortprowadzi nas podobną ścieżką. Jest on definiowany jako makro wraz z digitalPinToBitMask. Jego definicja to:

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

Teraz indeksujemy Pty element, digital_pin_to_port_PGMktórego tablica jest zdefiniowana w mapie pinów:

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

Ta tablica zawiera 20 elementów, więc 999 znów jest poza zasięgiem. Ponownie to polecenie odczytuje i zwraca wartość z pamięci flash, której wartości nie możemy być pewni. To znów doprowadzi do nieprzewidzianych zachowań.

Jest jeszcze jedna ostatnia linia obrony. To jest ifsprawdzenie pinModewartości zwracanej digitalPinToPort:

if (port == NOT_A_PIN) return;

NOT_A_PINjest zdefiniowany jako 0 cali Arduino.h. Tak więc, jeśli zwracany bajt z digitalPinToPortma wartość zero, wówczas pinModepo cichu zawiedzie i zwróci.

W każdym razie pinModenie może nas ocalić od anarchii. 999 ma doprowadzić do zguby.


TL; DR, kod zostanie wykonany, a jego wynik będzie nieprzewidywalny. Najprawdopodobniej żaden pin nie zostanie ustawiony na OUTPUTi digitalWritezawiedzie. Jeśli zdarzy ci się wyjątkowo niefortunnie, wtedy losowy pin może zostać ustawiony na OUTPUT, i digitalWritemoże to ustawić HIGH.

asheeshr
źródło
Ciekawe, że nie ma sprawdzania granic. digitalWrite i tak jest tak powolny i nieporęczny, że nie byłoby niewygodne włączenie kontroli czasu kompilacji lub wykonania.
Cybergibbons,
Jeśli wszystkie piny arduino znajdują się w ciągłym zakresie, to czy nie mogłyby zastąpić portu == nie sprawdzanie pinu za pomocą pinu> Sprawdzanie BOARD_MAX_PIN, gdzie maksymalny pin karty jest zdefiniowany w pliku nagłówkowym na podstawie jakiegoś ifdef, który wykrywa płytkę?
EternityForest
Zapominasz, że 999 nie może być reprezentowane w uint8_ttak, że najpierw zostanie przekonwertowane na 231 przez wywołanie kodu pinMode. Wynik końcowy jest taki sam: pinModei digitalWritebędzie mieć nieprzewidywalne zachowanie i może sprać losowe części pamięci, jeśli nazywają je ze złym pin argument.
David Grayson
3

W bibliotekach standardowych znajdują się makra przeznaczone do konwersji pinów na porty, które są używane w asemblerze. Tutaj są dla Uno z 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)))

Jest ich więcej, ale nie pokażę ich tutaj.

Wierzę, że twój program odejmie 14 od 999, co wciąż byłoby zbyt duże dla brogramu. Następnie próbowałby wskazać 985. element digital_pn_to_bit_mask_PGMtablicy, który zawiera tylko 20 elementów. Najprawdopodobniej doprowadziłoby to do spieprzenia Arduino, wskazując losowe miejsce w programie.

Doktor
źródło