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:
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
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 :)
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 ....
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.
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?
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?
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.
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.
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.
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.
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).
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;
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.
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!
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.
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...