Forum: Mikrocontroller und Digitale Elektronik PINx-Register


von Christoph B. (plcdeveloper)


Lesenswert?

Hallo liebes µC-Forum,

Ich bin gerade eben auf ein Problem gestoßen, was mich an meine Grenzen 
bringt. Ich versuche, einen Taster abzufragen und daraufhin ein 
Lauflicht mit 5 LEDs zu aktivieren.
Es geht um einen digitalen Eingang eines Arduino-Boards, an dem ein 
Taster angeschlossen ist - ich programmiere übrigens in der Arduino IDE 
mit reinem C und bin noch ein blutiger Anfänger. Wie ihr im Code sehen 
könnt, nutze ich für den digitalen Eingang Pin 7. Ich habe den internen 
Pullup des digitalen Eingangs auch aktiviert - also bezieht mein Arduino 
bei geöffnetem Schalter eine logische 1. So müsste doch auch ganz zu 
Beginn das PIND-Register folgendermaßen aussehen: 1000 0000. Oder etwa 
nicht? Es sind ja keine Ausgänge HIGH, also bis auf Pin 7 muss ja alles 
0 sein.

Nach dem Übertragen des Programmes scheint es aber so, also ob das 
PIND-Register != 1000 0000 ist, da er sofort einen Durchlauf der LEDs 
startet. Füge ich jedoch einen Delay ganz zu Beginn in die Loop hinzu, 
funktioniert alles wie gewollt. Der Durchlauf der LEDs startet also erst 
nachdem ich den Taster betätigt habe. Worauf ist das zurückzuführen? 
Oder irre ich mich etwa mit dem Abbild des PIND-Registers?

Hier noch der ein Ausschnitt aus dem Code:
1
int main(void)
2
{
3
  PORTD |= 0b10000000;
4
  DDRD |= 0b01111100;
5
  uint8_t n = 0;
6
7
  while(1)
8
  {
9
    _delay_ms(1);
10
    if((PIND & (1<<7)) != (1<<7))
11
    {
12
    while(n <= 4)
13
    {  
14
      PORTD |= ((1<<n+2) | (PORTD &= ~(1<<n+1))); 
15
      n += 1;
16
      _delay_ms(1000);
17
    }
18
    PORTD &= ~(1<<6);
19
    n = 0;
20
    }
21
    else
22
    {
23
      PORTD = (1<<7); 
24
    }
25
  }
26
}

von hard werker (Gast)


Lesenswert?

Christoph B. schrieb:
> Es geht um einen digitalen Eingang eines Arduino-Boards

Ist es geheim welcher Arduino es ist?

Christoph B. schrieb:
> nutze ich für den digitalen Eingang Pin 7

Eine Arduino Pin-Nummer entspricht im allgemeinen nicht
der Port-Nummer deines Controllers. Stelle klar wo du welchen
Taster und welche LED angeschlossen hast.

Christoph B. schrieb:
> PORTD |= ((1<<n+2) | (PORTD &= ~(1<<n+1)));

Zu wenige Klammern für einen klar verständlichen Code.
Das hier: 1<<n+1

von Christoph B. (plcdeveloper)


Lesenswert?

Ich verwende einen ELEGOO UNO R3. Darauf befindet sich ein ATMEGA328P.

Das mit den Pins und den Registern ist mir bewusst - ich habe es 
wirklich nur falsch formuliert. Aber weißt du, wieso er sofort einen 
Durchlauf macht, wenn sich der Delay nicht im Programm befindet? Sobald 
ich den Delay zu Beginn hinzufüge, findet der erste Durchlauf erst 
statt, wenn ich den Taster betätige.
Das mit den vielen Klammern ist deshalb so, sodass ich die LEDs, die 
zuvor eingeschaltet waren, ausschalte und die nächste LED einschalte.


Vielen Dank für deine Antwort :)

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Wie sieht denn die Schaltung dazu aus?

Oliver

von hard werker (Gast)


Lesenswert?

Christoph B. schrieb:
> Das mit den Pins und den Registern ist mir bewusst - ich habe es
> wirklich nur falsch formuliert.

Aber nicht klargestellt. Sonst können da immer noch Annahme-
Fehler drinstecken. Ich weiss bis jetzt nicht ob du Port D7
meinst oder Arduino Pin 7.

Christoph B. schrieb:
> Das mit den vielen Klammern ist deshalb so

Auch das hast du nicht verstanden.

hard werker schrieb:
> Das hier: 1<<n+1

Die Rangfolge der Operatoren ....

von hard werker (Gast)


Lesenswert?

hard werker schrieb:
> Stelle klar wo du welchen
> Taster und welche LED angeschlossen hast.

Taster kann man vom Port Pin nach Masse oder nach +5V anschliessen.
LEDs kann man vom Port Pin nach Masse oder nach +5V anschliessen.

von Christoph B. (plcdeveloper)


Lesenswert?

Ich LEDs sind zwischen den Pins 2-6 des Arduino Boards und Masse 
geschaltet. Der Taster zwischen Masse und Pin 7 des Arduino Boards.
Da ich also nur die digitalen Pins des Arduino Boards verwende, arbeite 
ich also mit den Registern DDRD, PORTD und PIND, richtig?

von Teo D. (teoderix)


Lesenswert?

Interne Pullups zu schwach fürs PCB-Design!?
Mach mal nen ~10k mit dran.

von HildeK (Gast)


Lesenswert?

hard werker schrieb:
> hard werker schrieb:
>> Das hier: 1<<n+1

Ich nehme mal an, so hat er es gemeint:
   x=(1<<(n+2));
Da die Rangfolge der Addition höher ist als die der Schiebeoperation 
braucht man keine weiteren Klammern.
Der Ausdruck ist also gleichwertig mit
   x=(1<<n+2);
Wenn sie richtig gesetzt sind, schaden sie nicht. Sie sind aber dann 
meist verständlicher ...
Oder hast du was anderes gemeint?

von hard werker (Gast)


Lesenswert?

Christoph B. schrieb:
> arbeite
> ich also mit den Registern DDRD, PORTD und PIND, richtig?

In diesem Fall ja.

Christoph B. schrieb:
> Füge ich jedoch einen Delay ganz zu Beginn in die Loop hinzu,
> funktioniert alles wie gewollt. Der Durchlauf der LEDs startet also erst
> nachdem ich den Taster betätigt habe. Worauf ist das zurückzuführen?

Vermutlich ist der Input für D7 nicht schnell genug hoch gepullt
da du nur den internen Pullup verwendest. Der Controller ist
schneller als der Pullup und sieht noch eine Null.

Versuche einen externen Pullup (1KOhm) am Taster-Pin.

von hard werker (Gast)


Lesenswert?

HildeK schrieb:
> Oder hast du was anderes gemeint?

Nein.

Ich bemäkele grundsätzlich solche fehlende Klammern da man
sich oft (ich öfters) nicht die Rangfolge der Operatoren
merken kann, und bei "Anfängern" kann man sich auch nicht
sicher sein was sie wollen und wissen.

von HildeK (Gast)


Lesenswert?

Christoph B. schrieb:
> Füge ich jedoch einen Delay ganz zu Beginn in die Loop hinzu,
> funktioniert alles wie gewollt. Der Durchlauf der LEDs startet also erst
> nachdem ich den Taster betätigt habe. Worauf ist das zurückzuführen?

Ich könnte mir vorstellen, dass auf Grund parasitärer Kapazitäten (z.B. 
auch durch lange Zuleitungen zum Taster) der interne, hochohmige PU an 
dem Taster den Pegel nicht schnell genug auf HIGH bringt. Oder hast du 
evtl. noch einen 'Entprell-Kondensator' parallel zum Taster?
Wenn das 1ms-Delay vor der While-Schleife es auch zum Funktionieren 
bringt, dann spricht das für meine Vermutung.
Andere Abhilfe: einen externen PU verwenden, der deutlich niederohmiger 
ist. Dafür bin ich bei benutzen Eingängen sowieso.

von Franz M. (elmo64)


Lesenswert?

Christoph B. schrieb:
> Da ich also nur die digitalen Pins des Arduino Boards verwende, arbeite
> ich also mit den Registern DDRD, PORTD und PIND, richtig?

Falsch, die IO pins sind in Gruppen, die maximal so lang sind, wie ein 
Register des AVRs, also 8 Bit, aufgeteilt. Es gibt DDR/PORT/PINB, 
DDR/PORT/PINC und DDR/PORT/PIND. Von diesen ist jedoch nur DDR/PORT/PIND 
8 Bit lang.

von HildeK (Gast)


Lesenswert?

hard werker schrieb:
> und bei "Anfängern" kann man sich auch nicht
> sicher sein was sie wollen und wissen.

Da stimme ich dir zu. Ich schrieb ja:
>> Sie sind aber dann meist verständlicher ...

Und zu einer möglichen Ursache hast du ja, während ich schrieb, die 
selben Überlegungen angestellt (Pullup).

von hard werker (Gast)


Lesenswert?

HildeK schrieb:
> Andere Abhilfe: einen externen PU verwenden

Gratuliere. Dritter Platz.

von Franz M. (elmo64)


Lesenswert?

Weitere Basics findest du hier: Kategorie:Avr-gcc Tutorial

von c-hater (Gast)


Lesenswert?

Christoph B. schrieb:

> Ich bin gerade eben auf ein Problem gestoßen, was mich an meine Grenzen
> bringt. Ich versuche, einen Taster abzufragen und daraufhin ein
> Lauflicht mit 5 LEDs zu aktivieren.
> Es geht um einen digitalen Eingang eines Arduino-Boards, an dem ein
> Taster angeschlossen ist - ich programmiere übrigens in der Arduino IDE
> mit reinem C und bin noch ein blutiger Anfänger. Wie ihr im Code sehen
> könnt, nutze ich für den digitalen Eingang Pin 7. Ich habe den internen
> Pullup des digitalen Eingangs auch aktiviert - also bezieht mein Arduino
> bei geöffnetem Schalter eine logische 1. So müsste doch auch ganz zu
> Beginn das PIND-Register folgendermaßen aussehen: 1000 0000. Oder etwa
> nicht? Es sind ja keine Ausgänge HIGH, also bis auf Pin 7 muss ja alles
> 0 sein.

Das ist schonmal ein Denkfehler. Allerdings einer, der für dein 
konkretes Problem nicht relevant ist. Der Denkfehler besteht darin, dass 
eben nicht alle Ausgänge 0 sind, den drei der "Ausgänge" sind ja gar 
keine Ausgänge, sondern Eingänge. Und für Eingänge hangt der Status 
davon ab, was halt von draussen eingespeist wird und/oder, ob ein 
interner Pullup aktiviert ist. Letzteres ist aber nur für einen Pin der 
Fall. D.h.: der Status von PIND0 und PIND1 ist erstmal durch die 
Software nicht definiert. Dazu kann man also bestenfalls etwas sagen, 
wenn man auch die konkrete Schaltung kennt.

Warum ist der Denkfehler für dein Problem nicht relevant? Ganz einfach, 
weil du das einzige Bit, dessen Status dich wirklich interessiert, 
korrekt ausmaskierst, bevor du den Wert prüfst. Sprich: es ist 
vollkommen Wurscht, welchen Status die anderen 7 Pins haben bzw. hatten.

> Nach dem Übertragen des Programmes scheint es aber so, also ob das
> PIND-Register != 1000 0000 ist, da er sofort einen Durchlauf der LEDs
> startet. Füge ich jedoch einen Delay ganz zu Beginn in die Loop hinzu,
> funktioniert alles wie gewollt.

Tja, das ist sehr wahrscheinlich die Folge einer Sache, die sich 
"Synchronisierung" nennt, in Zusammenarbeit mit einer Sache, die sich 
"Optimierung" nennt. Die Synchronsierung ist ein Mechanismus, mit dem 
die asynchron zum MCU-Takt erfolgenden Ereignisse der Aussenwelt in die 
Synchronität zum MCU-Takt gebracht werden. Durch diesen Mechanismus wird 
aber eine Verzögerung von einem MCU-Takt eingeführt. D.h.: im konkreten 
Fall: die Aktivierung des internen Pullup wird erst einen Takt später an 
PIND7 "sichtbar".
Und nun kommt die Optimierung in's Spiel: wenn die deinen Code so 
durcheinandergewürfelt hat, dass die Pinabfrage (bzw. das Einlesen des 
PIN-Status) direkt nach dem Setzen des Pullup erfolgt, enthält der 
Status den Erfolg noch nicht, weil der erst einen Takt verspätet 
sichtbar wird.

Ob dieser Effekt wirklich Schuld ist (wovon ich mal ausgehen würde), 
sieht man, wenn man sich den Assemblercode anschaut.

Wenn ich richtig liege, sollte es mit einiger Wahrscheinlichkeit helfen, 
einfach die Reihenfolge dieser beiden Zeilen in deinem Code zu tauschen, 
um auf das Delay verzichten zu können:

  PORTD |= 0b10000000;
  DDRD |= 0b01111100;

von Christoph B. (plcdeveloper)


Lesenswert?

c-hater schrieb:
> Wenn ich richtig liege, sollte es mit einiger Wahrscheinlichkeit helfen,
> einfach die Reihenfolge dieser beiden Zeilen in deinem Code zu tauschen,
> um auf das Delay verzichten zu können:
>
>   PORTD |= 0b10000000;
>   DDRD |= 0b01111100;
>> Vielen Dank. Ich hab die zwei Zeilen jetzt vertauscht und das Programm hat 
wunderbar funktioniert. Das merke ich mir für meine nächsten Projekte. Und vielen 
Dank auch an die anderen, die so fleißig mitdiskutiert haben. Ich werde auch eure 
Variante mit dem externen PU ausprobieren.

von c-hater (Gast)


Lesenswert?

Christoph B. schrieb:

>>> Vielen Dank. Ich hab die zwei Zeilen jetzt vertauscht und das Programm hat
> wunderbar funktioniert. Das merke ich mir für meine nächsten Projekte.

Aber merke dir bitte nur den Sachverhalt der Verzögerung um einen Takt, 
aber nicht die konkrete Lösung für deinen Fall. Denn dass die 
funktioniert, ist mehr oder weniger Zufall. Man muss genau wissen, wie 
der Compiler optimiert, um ihn derart austricksen zu können. Geringste 
Änderungen des Codes oder der gewählten Optimierung können dafür sorgen, 
dass es doch wieder nicht funktioniert.

Die korrekt Lösung wäre das Einfügen einer Wartezeit von genau einem 
Takt zwischen den beiden Zeilen und dem weiteren Code.

Also die Zeile:

asm volatile (“nop\n\t”);

Asm rules!

von HildeK (Gast)


Lesenswert?

c-hater schrieb:
> asm volatile (“nop\n\t”);

Nebenläufige Frage: Welchen Grund gibt es für diese Schreibweise?
Ich hatte bisher immer
   asm volatile("NOP");
oder auch
   asm volatile("nop");
verwendet.

Und warum den Tabulator '\t'?
Für mehrere asm-Anweisung in einem Statement leuchtet mir das '\n' ein.

von c-hater (Gast)


Lesenswert?

HildeK schrieb:

> Nebenläufige Frage: Welchen Grund gibt es für diese Schreibweise?

Ganz einfach: wenn ich in meiner normalen Umgebung "nop" schreiben will, 
brauch ich auch einfach nur "nop" zu schreiben.

Das hier war war also C&P.

Einen Sinn hat es aber tatsächlich. Man erkennt ihn, wenn man das *.lss 
ansieht...

Das ist nun wieder eine Sache, die ich durchaus auch öfter machen 
muss...

von HildeK (Gast)


Lesenswert?

Danke!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.