O que acontecerá quando eu usar um número de PIN inválido?

9

Relacionado a: O que acontece se houver um erro de tempo de execução?

Esta pergunta é semelhante à acima, no entanto, é uma situação alternativa:

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

O que aconteceria neste caso? O compilador pode capturá-lo, mas se você usasse um número aleatório, o IDE o capturaria?

Pinguim anônimo
fonte

Respostas:

9

O compilador não detectará nenhum erro e o código será compilado e executado. Portanto, para ver o que acontece, precisamos explorar a mágica dos bastidores. Para um resumo, pule para o final.


A segunda linha do seu código é onde a mágica acontecerá e é aí que precisamos nos concentrar.

pinMode(pin, OUTPUT);

A parte pinModerelevante para esta discussão é:

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
}

(A implementação completa pode ser encontrada em fiação_digital.c )

Então, aqui, digitalPinToBitMaskparece estar usando pinpara calcular um bit intermediário. Explorando ainda mais, digitalPinToBitMaské uma macro definida em Arduino.hcuja definição é esta linha única:

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

Este forro de aparência estranha faz uma tarefa muito simples. Ele indexa o elemento P th na matriz digital_pin_to_bit_mask_PGMe o retorna. Essa matriz digital_pin_to_bit_mask_PGMé definida no pins_arduino.hmapa de pinos da placa específica que está sendo usada.

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

Essa matriz possui 20 elementos no total, então estamos sem sorte. 999 indexará um local de memória na memória flash fora dessa matriz, levando a um comportamento imprevisível. Ou será?

Ainda temos outra linha de defesa contra a anarquia de tempo de execução. É a próxima linha da função pinMode:

uint8_t port = digitalPinToPort(pin);

digitalPinToPortnos leva por um caminho semelhante. É definido como uma macro junto com digitalPinToBitMask. Sua definição é:

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

Agora, indexamos o elemento P th , digital_pin_to_port_PGMque é uma matriz definida no mapa de pinos:

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

Essa matriz contém 20 elementos, portanto, 999 está novamente fora de alcance. Novamente, este comando lê e retorna um valor da memória flash cujo valor não podemos ter certeza. Isso levará novamente a um comportamento imprevisível a partir de agora.

Ainda existe uma última linha de defesa. Esse é o ifcheck-in pinModeno valor de retorno de digitalPinToPort:

if (port == NOT_A_PIN) return;

NOT_A_PINé definido como 0 pol Arduino.h. Portanto, se o byte retornado de digitalPinToPortfor zero, pinModefalhará e retornará silenciosamente.

De qualquer forma, pinModenão pode nos salvar da anarquia. 999 está destinado a resultar em desgraça.


TL; DR, o código será executado e o resultado será imprevisível. Provavelmente, nenhum pino será definido OUTPUTe digitalWritefalhará. Se você tiver uma sorte excepcionalmente ruim, um pino aleatório pode ser definido como OUTPUTe digitalWritepode ser definido como HIGH.

asheeshr
fonte
É interessante que não haja verificação de limites. O digitalWrite é tão lento e volumoso de qualquer maneira, não seria estranho colocar em tempo de compilação ou em verificações de tempo de execução.
Cybergibbons
Se todos os pinos do arduino estiverem em um intervalo contíguo, eles não poderiam substituir a porta == não uma verificação de pinos por um pino> verificação BOARD_MAX_PIN, em que o pino máximo da placa é definido em algum arquivo de cabeçalho com base em algum ifdef que detecta a placa?
EternityForest
Você está esquecendo que o 999 não pode ser representado em um, uint8_tpelo que seria primeiro convertido em 231 pela chamada de código pinMode. O resultado final é o mesmo: pinModee digitalWriteterá um comportamento imprevisível e poderá estragar partes aleatórias da memória se você as chamar com um argumento incorreto.
David Grayson
3

Nas bibliotecas padrão, existem macros projetadas para converter pinos em portas, que são usadas na montagem. Aqui estão eles para o Uno do 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)))

Há mais, mas não vou mostrá-las aqui.

Acredito que seu programa subtraia 14 de 999, o que ainda seria muito grande para o submarino. Ele tentaria apontar para o 985º elemento da digital_pn_to_bit_mask_PGMmatriz, que contém apenas 20 elementos. Provavelmente, isso acabaria atrapalhando o Arduino, apontando para um ponto aleatório no programa.

O médico
fonte