La manipulación directa del puerto da un resultado aleatorio: ¿Retraso necesario?

Configuración (Arduino Pro Mini ATmega328p 16MHz, 5V, sin pin conectado):

Foto del Arduino en una placa de prueba, con FTDI adjunto

Cuando se llama desde el setup(), esto funciona:

pinMode(11, INPUT_PULLUP);
bool identifiesAsFast = digitalRead(11); // always true

Esto no:

const uint8_t identificationPinBitNumber = PB3; // pin 11
const byte identificationPinBit = bit(identificationPinBitNumber);
DDRB &= ~identificationPinBit;
PORTB |= identificationPinBit;
bool identifiesAsFast = PINB & identificationPinBit; // occasionally false

El pin 11 no está conectado, pero, como puede ver arriba, lo tengo en alto. Así que esperaría que siempre tenga un valor de 1.

¿Qué me estoy perdiendo al hacer la manipulación directa del puerto?

Obtengo el comportamiento con cuatro Arduino Pro Minis diferentes. Todos son Pro Minis originales, diseñados por Sparkfun, fabricados en EE. UU. y comprados a Digi-Key. Uno de ellos lo saqué recién sacado de la bolsa original. Es posible que tenga que quitar e insertar el adaptador FTDI unas treinta veces, pero finalmente puedo reproducir el problema.

Agregar un retraso de 100 ms entre tirar del pin 11 y leerlo parece resolver el problema. Sin embargo, no entiendo por qué ese retraso sería necesario.

PB3para el pin 11 es correcto, como comprobé conectando un LED y haciéndolo parpadear. Consulte también el diagrama de asignación de pines que se puede encontrar en otros lugares de la web:

Asignación de pines

Código completo:

// Felix E. Klee <felix.klee@inka.de>

const uint8_t ledPinBitNumber = PB5; // pin 13
const byte ledPinBit = bit(ledPinBitNumber);

const uint8_t identificationPinBitNumber = PB3; // pin 11
const byte identificationPinBit = bit(identificationPinBitNumber);

unsigned long delayDuration; // ms

void turnLedOn() {
  PORTB |= ledPinBit;
}

void turnLedOff() {
  PORTB &= ~ledPinBit;
}

void setDelayDuration() {
#if 0
  pinMode(11, INPUT_PULLUP);

  bool identifiesAsFast = digitalRead(11); // always true
#else
  // Input pullup:
  DDRB &= ~identificationPinBit;
  PORTB |= identificationPinBit;

  bool identifiesAsFast = PINB & identificationPinBit; // occasionally false
#endif

  delayDuration = identifiesAsFast ? 300 : 3000;
}

void setup() {
  DDRB |= ledPinBit; // Set up LED pin for output
  setDelayDuration();
}

void loop() {
  turnLedOn();
  delay(delayDuration);
  turnLedOff();
  delay(delayDuration);
}

Respuestas (2)

Por extraño que suene, tu código es demasiado rápido .

El pin (y la parte de la protoboard a la que está enchufado) tiene algo de capacitancia. Se necesita tiempo para que el pull-up resistivo en el AVR levante el pin.

El Arduino pinMode()y digitalRead()las funciones son lo suficientemente lentos como para que, cuando lleguen a leer el pin, el pullup haya hecho su trabajo. Su código está accediendo a los registros directamente, lo cual es considerablemente más rápido, por lo que es posible que el pin aún esté bajo cuando lo lea.

Sin embargo, la cantidad de retraso que necesita es bastante mínima. 50 microsegundos (¡no milisegundos!) deberían ser suficientes.

Hablando de la influencia de la protoboard: con el Arduino prístino, me tomó mucho más tiempo reproducir el problema. Es decir, tuve que quitar e insertar el FTDI cerca de treinta veces. En comparación con los otros tres Arduinos, el Arduino prístino no estaba en una placa de pruebas. Ni siquiera tenía encabezados.
Además, en la versión de hoja de datos DS40001984A que ahora encontré en la página 102: "Al leer un valor de pin asignado por software, se debe insertar una instrucción nop como se indica en la siguiente figura". Esto tiene que ver con el tiempo, no con la capacitancia. De hecho, mientras que agregar un solo NOP tiene un efecto positivo, todavía hay un valor incorrecto ocasional.

¡Lo estás haciendo de la manera incorrecta!

Un puerto Arduino consta de 8 pines, por ejemplo PB0, PB2, PB3, ..., PB7.

Si desea manipular un registro o un bit o algunos bits, puede que tenga que hacer esto:

PORTB |= (1 << PB5) // Set a bit

PORTB &= ~(1 << PB5) // Clear a bit

Su línea clara de un bit debería decir PORTB &= ~(1 << PB5) // Clear a bitNo puedo editar ya que es un cambio de un solo carácter.
No. Sustituyendo bit(definido en Arduino.h ) y constantes, vemos que es PORTB |= identificationPinBitigual a PORTB |= 1ul << PB3. El asunto está en otra parte.
También puede usar la macro _BV() nongnu.org/avr-libc/user-manual/FAQ.html#faq_use_bv
@feklee;) ¡No puedo creer que haya alguien que profundice tanto! Ok, creo que definese usa en algún lugar de la máquina interna de Arduino, lo que encuentro sobre bit()la función puede referirse a esto .