Direkte Port-Manipulation liefert zufälliges Ergebnis: Verzögerung notwendig?

Setup (Arduino Pro Mini ATmega328p 16MHz, 5V, kein Pin verbunden):

Foto des Arduino auf einem Steckbrett mit angeschlossenem FTDI

Bei einem Aufruf aus der setup()funktioniert das:

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

Dies nicht:

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

Pin 11 ist nicht angeschlossen, aber - wie man oben sieht - habe ich ihn hochgezogen. Ich würde also erwarten, dass es immer einen Wert von 1 hat.

Was übersehe ich bei der direkten Port-Manipulation?

Ich bekomme das Verhalten mit vier verschiedenen Arduino Pro Minis. Alle sind originale Pro Minis, entworfen von Sparkfun, hergestellt in den USA und gekauft von Digi-Key. Eine davon habe ich frisch aus der Originaltüte gezogen. Möglicherweise muss ich den FTDI-Adapter ungefähr dreißig Mal entfernen und wieder einsetzen, aber schließlich kann ich das Problem reproduzieren.

Das Hinzufügen einer Verzögerung von 100 ms zwischen dem Hochziehen von Pin 11 und dem Lesen scheint das Problem zu lösen. Ich verstehe jedoch nicht, warum diese Verzögerung notwendig sein sollte.

PB3für Pin 11 ist korrekt, wie ich verifiziert habe, indem ich eine LED angeschlossen und zum Blinken gebracht habe. Siehe auch das Pin-Mapping-Diagramm, das an anderer Stelle im Internet zu finden ist:

Pin-Mapping

Vollständiger Code:

// 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);
}

Antworten (2)

So seltsam das auch klingen mag, Ihr Code ist zu schnell .

Der Stift (und der Teil des Steckbretts, an den er angeschlossen ist) hat eine gewisse Kapazität. Es dauert einige Zeit, bis der resistive Pullup im AVR den Pin hochzieht.

Das Arduino pinMode()und digitalRead()die Funktionen sind langsam genug, dass der Pullup seine Arbeit erledigt hat, wenn sie zum Lesen des Pins kommen. Ihr Code greift direkt auf Register zu, was erheblich schneller ist – daher ist es möglich, dass der Pin beim Lesen immer noch niedrig ist.

Die benötigte Verzögerung ist jedoch ziemlich gering. 50 Mikrosekunden (nicht Millisekunden!) sollten ausreichen.

Apropos Einfluss des Steckbretts: Mit dem reinen Arduino habe ich viel länger gebraucht, um das Problem zu reproduzieren. Dh ich musste den FTDI fast 30 mal aus- und einstecken. Im Vergleich zu den anderen drei Arduinos befand sich der ursprüngliche Arduino nicht in einem Steckbrett. Es hatte nicht einmal Kopfzeilen drauf.
Auch in der Datenblattversion DS40001984A, die ich jetzt auf Seite 102 gefunden habe: „Beim Zurücklesen eines von der Software zugewiesenen Pinwerts muss eine nop-Anweisung eingefügt werden, wie in der folgenden Abbildung angegeben.“ Das hat mit dem Timing zu tun, nicht mit der Kapazität. Tatsächlich hat das Hinzufügen eines einzelnen NOP zwar einen positiven Effekt, aber es gibt immer noch gelegentlich einen falschen Wert.

Du machst es falsch!

Ein Arduino-Port besteht aus 8 Pins, zum Beispiel PB0, PB2, PB3, ..., PB7.

Wenn Sie ein Register oder ein Bit oder einige Bits manipulieren möchten, müssen Sie möglicherweise wie folgt vorgehen:

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

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

Ihre Zeile zum Löschen eines Bits sollte lauten: PORTB &= ~(1 << PB5) // Clear a bitIch kann nicht bearbeiten, da es sich um eine einzelne Zeichenänderung handelt.
Nein. Wenn wir bit(in Arduino.h definiert ) und Konstanten ersetzen, sehen wir, dass das PORTB |= identificationPinBitgleich ist PORTB |= 1ul << PB3. Das Problem liegt woanders.
@feklee ;) Kann nicht glauben, dass es jemanden gibt, der so tief gräbt! Ok, ich denke, das definewird irgendwo in der Arduino-internen Maschine verwendet, was ich über bit()die Funktion finde, kann sich darauf beziehen .