¿Qué sucederá cuando use un número PIN no válido?

9

Relacionado con: ¿Qué sucede si hay un error de tiempo de ejecución?

Esta pregunta es similar a la anterior, sin embargo, esta es una situación alternativa:

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

¿Qué pasaría en este caso? El compilador podría atraparlo, pero si usara un número aleatorio, ¿lo atraparía el IDE?

Pingüino anónimo
fuente

Respuestas:

9

El compilador no detectará ningún error y el código se compilará y ejecutará. Por lo tanto, para ver qué sucede, necesitamos explorar la magia detrás de escena. Para un resumen, salte al final.


La segunda línea en su código es donde sucederá la magia y ahí es donde debemos enfocarnos.

pinMode(pin, OUTPUT);

La porción de pinModerelevante para esta discusión es:

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
}

(La implementación completa se puede encontrar en cableado_digital.c )

Entonces, aquí, digitalPinToBitMaskparece estar usando pinpara calcular un bit intermedio. Explorando más a fondo, digitalPinToBitMaskhay una macro definida en Arduino.hcuya definición es esta línea:

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

Este forro de aspecto extraño hace una tarea muy simple. Indiza el elemento P th en la matriz digital_pin_to_bit_mask_PGMy lo devuelve. Esta matriz digital_pin_to_bit_mask_PGMse define en pins_arduino.hel mapa de pin para la placa específica que se utiliza.

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

Este conjunto tiene 20 elementos en total, por lo que no tenemos suerte. 999 indexará una ubicación de memoria en la memoria flash fuera de esta matriz, lo que conducirá a un comportamiento impredecible. O lo hará?

Todavía tenemos otra línea de defensa contra la anarquía en tiempo de ejecución. Es la siguiente línea de la función pinMode:

uint8_t port = digitalPinToPort(pin);

digitalPinToPortnos lleva por un camino similar. Se define como una macro junto con digitalPinToBitMask. Su definición es:

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

Ahora, indexamos el elemento P th del digital_pin_to_port_PGMcual es una matriz definida en el mapa pin:

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

Esta matriz contiene 20 elementos, por lo que 999 está nuevamente fuera de rango. Nuevamente, este comando lee y devuelve un valor de la memoria flash de cuyo valor no podemos estar seguros. Esto conducirá nuevamente a un comportamiento impredecible de aquí en adelante.

Todavía hay una última línea de defensa. Esa es la ifverificación pinModedel valor de retorno de digitalPinToPort:

if (port == NOT_A_PIN) return;

NOT_A_PINse define como 0 in Arduino.h. Entonces, si el byte devuelto por digitalPinToPortresulta ser cero, entonces pinModefallará silenciosamente y regresará.

En cualquier caso, pinModeno puede salvarnos de la anarquía. 999 está destinado a resultar en la fatalidad.


TL; DR, el código se ejecutará y el resultado será impredecible. Lo más probable es que no se establezca ningún pin OUTPUTy digitalWritefallará. Si tiene una suerte excepcionalmente mala, entonces se puede establecer un pin aleatorio en OUTPUT, y digitalWritepuede establecerlo en HIGH.

Asheeshr
fuente
Es interesante que no haya verificación de límites. digitalWrite es tan lento y voluminoso de todos modos, que no sería incómodo realizar comprobaciones en tiempo de compilación o tiempo de ejecución.
Cybergibbons
Si todos los pines arduino están en un rango contiguo, ¿no podrían reemplazar el puerto == no una verificación de pin con un pin> BOARD_MAX_PIN check, donde el pin máximo de la placa se define en algún archivo de encabezado basado en algún ifdef que detecta la placa?
EternityForest
Olvida que 999 no se puede representar en un, uint8_tpor lo que primero se convertiría en 231 mediante la llamada de código pinMode. El resultado final es el mismo: pinModey digitalWritetendrá un comportamiento impredecible y podría darle una paliza partes aleatorias de la memoria si los llama con un argumento mal alfiler.
David Grayson
3

En las bibliotecas estándar, hay macros diseñadas para convertir pines en puertos, que se utilizan en el ensamblaje. Aquí están para el Uno de 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)))

Hay más, pero no los mostraré aquí.

Creo que su programa restaría 14 de 999, lo que aún sería demasiado grande para el brograma. Luego trataría de señalar el elemento 985 de la digital_pn_to_bit_mask_PGMmatriz, que solo contiene 20 elementos. Lo más probable es que termine atornillando al Arduino señalando un punto aleatorio en el programa.

TheDoctor
fuente