Forum: Mikrocontroller und Digitale Elektronik Taste einmal drücken: Unterprogramm1, zweimal drücken, Unt


von Karl (Gast)


Lesenswert?

Hallo zusammen,

ich hätte noch mal eine Frage:

Ich habe nun drei verschiedene Lauflichter, die ich in Unterprogramme
gegossen habe.
Jeder Aufruf des UP ist EIN Lauflichtdurchlauf. D.h. im Kernprogramm
muss in einer Schleife ständig mein Lauflichtunterprogramm aufgerufen
werden.

Nun würde ich gerne folgendes realisieren: Wenn ich eine bestimmte
Taste drücke, (aber nicht gedrückt halte) soll ab diesem Zeitpunkt das
erste Lauflicht in einer Loop aufgerufen werden, wenn ich dann die
gleiche Taste nocheinmal kurz drücke, soll das andere Lauflichtprozedur
in der Loop aufgerufen werden und das gleiche beim dritten Lauflicht.
Beim vierten Tastendruck sollte wieder das erste Lauflicht in der Loop
aufgerufen werden. So könnte ich durch Tastendrücke durch die einzelnen
Programme durchsteppen.

Ich nehme an, man braucht einen kleinen Zähler, der beim drücken der
Taste hochgezählt wird, ab drei dann wieder auf Null gesetzt wird usw.
Und in der Loop wird je nach Zählerstand die eine oder andere  Prozedur
angesprungen.

Aber wie kann ich dieses durchsteppen realisieren? Möglichst ohne
Interrupts, weil ich da noch gar nicht durchblicke.

Vielen Dank
Karl

von Peter D. (peda)


Lesenswert?

"Möglichst ohne Interrupts, weil ich da noch gar nicht durchblicke."

Dann ist das doch ein guter Zeitpunkt, damit anzufangen.
Die externen Interrupts braucht man eher selten, aber die
Timerinterrupts sind unverzichtbar, will man in seinem Programmablauf
den Überblick behalten.

In Deinem Fall brauchst Du ihn, um die Tasten zu entprellen und das
hintereinander Drücken zu erkennen.

Der Rest Deiner Aufgabe ist eine Statemaschine, z.B. Du hast eine
Variable, die die verschiedenen Zustände kennzeichnet und je nach
Zustand auf einen anderen Wert (=Zustand) wechseln kann.

Am elegantesten könnte man beides mit meinem Sheduler-Beispiel machen,
aber da sollte man schon etwas Erfahrung mit den Timern und mit C
haben.


Peter

von ...HanneS... (Gast)


Lesenswert?

Hi...

Du wirst um den Timer-Interrupt nicht herum kommen...
Ansonsten wird das viel zu kompliziert.

Versuchs zuerst mit dem Timer0-Overflow, da ist das Wenigste zu
beachten. Stell den so ein, dass der alle paarhundert Takte ausgelöst
wird (erster Versuch: (Vorteiler)64 mal (Zählumfang)256). Wenn du mit
dem klar kommst, wirst du sehen, dass ein Programm mit Interrupts viel
einfacher wird als diese "WaitXYZ"-Aufrufe. Außerdem schafft der
Timer-Int die Basis dafür, mehrere verschiedene Programmteile
(scheinbar) gleichzeitig abzuarbeiten. Hängst du bei einem Programm mit
Wait-Aufrufen in der Warteschleife, dann kannst du nix Anderes machen,
der Prozessor ist mit der Warteschleife voll ausgelastet. Vergiss also
diese Programmiermethode... (möglichst schnell)

Deine Taste braucht übrigens neben dem Zähler noch eine Entprellung.
Der Externe Interrupt ist dazu denkbar ungeeignet, der würde bei einem
(mechanisch erzeugten) Tastendruck etliche male aufgerufen.

Geh also andersrum an das Programm ran:
- Bau kein Programm, in das du zum Verzögern Zeitschleifen
  aufrufst,
- Bau einen Zeittakt (Timer-Int), in dem du Programm abarbeitest...

Dazu ist das "ganz spezielle Lauflicht" allerdings denkbar
ungeeignet...

Grundlagen zum Timer-Int findest du im oben erwähnten Blink-Programm.

Fang klein an:
- Int-Vektoren zu Programmbeginn anlegen (siehe Datenblatt
  oder Include-Datei ganz unten),
- Reset-Routine schreiben, in der Folgendes gemacht wird:
  - Stackpointer initialisieren,
  - Ports initialisieren,
  - Timer0 am Prozessortakt 1:64, (3 in tccr0 schreiben, Datenblatt
    Seite 70)
  - Timer0-Overflow aktivieren, (toie0 in timsk setzen, DB S.70)
  - mit SEI Interrupts freischalten,

- Ein Hauptprogramm (Schleife) schreiben, in dem (vorerst) nix
  gemacht wird:
    main:      rjmp main
  Später kommt da noch Programm dazu, ist das fertig, dann schickt
  es den AVR schlafen...

- eine ISR (Interrupt-Service-Routine) schreiben, dort musst du:
  - SREG sichern,
  - (später mal) Timer-Reload-Wert in tcnt0 schreiben,
  - (später mal) ein Register hochzählen, welches vom Hauptprogramm
    als "Stopuhr" genutzt werden kann (akt. Wert merken,
    Wartezeit addieren, warten bis zum Gleichstand, geht auch mit
    zwei Registern (16 Bit) für längere Wartezeiten),
  - den Code meiner Hauptschleife (außer Warteschleife, denn das
    macht jetzt der Timer, indem er den Programmteil periodisch
    aufruft) einfügen (ohne Rücksprung am Ende zum Codebeginn),
  - später mal weiteren Code einfügen, z.B. Tastenabfrage, aber
    bitte nix mit Verzögerung! (Weitere Tricks folgen später, die
    würden dich jetzt nur unnötig verwirren),
  - SREG wiederherstellen,
  - die ISR mit RETI abschließen.

Du musst allerdings darauf achten, dass zwischen den einzelnen Aufrufen
des Timer-Int genügend Zeit zur Abarbeitung der ISR bleibt (und auch
noch für das spätere Hauptprogramm!). Aber dabei ist der Simulator des
AVR-Studios dein Freund...

Wenn das alles läuft, kannst du ja mal darüber nachdenken, wie man
andere (ganz besondere?) Lauflichter so umstrukturiert, dass sie bei
periodischem Aufruf des Programmteils funktionieren.

Die Denkweise ist also völlig anders:
Der Timer-Interrupt gibt dir einen (recht genauen, zumindest von der
Länge der ISR unabhängigen) Takt vor, in dem der Programmteil, den man
sonst in einer Schleife (mit "Bremse") abarbeiten würde, zyklisch
aufgerufen wird. Brauchst du "Bremsen", so musst du halt die
ISR-Aufrufe zählen und deinen Programmteil überspringen, falls eine
Wartepause aktiv ist.

Viel Erfolg...
...HanneS...

von ...HanneS... (Gast)


Lesenswert?

Ups...

@Peter: Das kommt davon, wenn man abgelenkt wird (Insulin-Spritzzeit,
essen) und daher über eine Stunde zum Schreiben des Beitrags
braucht...

;-)

...HanneS...

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.