Forum: Mikrocontroller und Digitale Elektronik Unterprogramme bei Signal sofort verlassen


von Michael (Gast)


Lesenswert?

Hallo,

ich habe nun auch mal mit den Mikrocontrollern begonnen und soweit 
klappt auch alles, aber für eine Problematik konnte ich noch keine 
Lösung finden. Hoffe auf ein wenig Anfängerhilfe eurerseits.

Also folgendes Programm gibt es. Ampelsteuerung wie man es kennt. Rot, 
rot/gelb, grün, gelb, rot. Funktioniert prima. Störungsfall der Ampel. 
Gelb blinkt vor sich hin. Funktioniert auch gut. Umschalten soll man den 
"Betriebszustand" mit einem Taster. Funktioniert nicht zufrieden 
stellend.

Normaler Betrieb und Störungsbetrieb sind 2 Unterprogramme. Aber man 
muss zum Umschalten den Taster immer gedrückt halten, bis das 
Unterprogramm durch ist, sonst wird der Modus nicht gewechselt. Und das 
sollte entwerde direkt oder spätestens nach Ablauf des Unterprogramms 
geschehen, auch wenn man den Taster nicht so lang hält. Hier mal der 
Code.
1
$regfile = "attiny13.dat"
2
$crystal = 1200000
3
4
'LED Yellow an PB3
5
Ledy Alias Portb.3
6
Config Ledy = Output
7
8
'LED Green an PB4
9
Ledg Alias Portb.4
10
Config Ledg = Output
11
12
'LED Red an PB0
13
Ledr Alias Portb.0
14
Config Ledr = Output
15
16
'Switch an PB1
17
Sw Alias Pinb.1
18
Config Sw = Input
19
Portb.1 = 1
20
21
Dim Status As Bit
22
Status = 0
23
24
Do
25
   If Sw = 0 Then
26
   Debounce Sw , 0 , Setstatus , Sub
27
   Else
28
   Gosub Run
29
   End If
30
31
32
Loop
33
34
Setstatus:
35
Toggle Status
36
Wait 1
37
Return
38
39
Run:
40
   If Status = 0 Then
41
   Ledr = 1
42
   Wait 5
43
   Ledy = 1
44
   Wait 2
45
   Ledr = 0
46
   Ledy = 0
47
   Ledg = 1
48
   Wait 8
49
   Ledg = 0
50
   Ledy = 1
51
   Wait 2
52
   Ledy = 0
53
   Ledr = 1
54
   End If
55
56
   If Status = 1 Then
57
   Ledr = 0
58
   Ledg = 0
59
   Ledy = 1
60
   Waitms 250
61
   Ledy = 0
62
   Waitms 500
63
   End If
64
Return
65
66
67
End

Wäre super, wenn mir da jemand helfen kann. Konnte selber bisher keine 
Lösung dafür finden und bitte seid umsichtig. Ich fange grad erst damit 
an.

von Kaj (Gast)


Lesenswert?

Bin mir nicht sicher ob ich dein Problem jetzt richtig verstanden habe, 
aber wie wäre es wenn du dafür einen Interrupt nutzt?

Taster drücken -> Interrupt wird aufgerufen

Im Interrupt eine Variable setzen, und diese Variable dann in der 
Hauptschleife abfragen, und dann entsprechend verzweigen.

Grüße

von Patrick B. (p51d)


Lesenswert?

Dein Problem liegt in den vielen Waits...

So als Beispiel:
- Led an
- Warte(10000)ms
- Led aus...

Wird jetzt der Taster während dieser Wartezeit gedrückt (was eine 
Ewigkeit für den Prozessor ist), dann hast du keine Chance das zu 
detektieren.
Lösung wäre, EIN EINZIGES Warten im Programm (etwa 1ms), und dann über 
Zustände die Ausgabe definieren (Zeit abgelaufen, Taster gedrückt...). 
So kannst du einen kleinen MCU auch "Multi-Tasking" fähig machen, da die 
einzelnen Schritte in sehr kleine und vorallem schnelle Blöcke 
unterteilt werden (was für Kommunikation dann unerlässlicht wird).

Pseudo-Code:
[quote]
main
  switch (zustand)
    blinken:
      Wenn (timer < 500)  LED ein
      sonst        LED aus
    rot:
      LED ein
    aus:
      LED aus
  end switch

  Wenn Taster
    ... entprellen ...
    Wenn (zustand > 2)  zustand = 0

  wait 1ms
end:
[/qutoe]

von Michael (Gast)


Lesenswert?

Eine Variable ändert der Taster ja sowieso, von daher klingt die Idee 
schonmal nicht schlecht. Hab schon was über Interrupts gelesen, aber 
muss ich dann wohl nochmal aufmerksamer durchlesen. Hab grad auch mal 
fix ins Datenblatt des Tiny13A geschaut und wenn ich das richtig gelesen 
habe, ist das mit pinb1, also int1 möglich. Mal probieren.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Michael schrieb:
> Hab grad auch mal
> fix ins Datenblatt des Tiny13A geschaut und wenn ich das richtig gelesen
> habe, ist das mit pinb1, also int1 möglich. Mal probieren.

Moin!

Hast du nun einen ATtiny13 oder einen ATtiny13A in Verwendung?
In deinem Quellcode wird die Include-Datei für den 13 verwendet, aber du 
schaust in das Datenblatt des 13A. Das sind sehr ähnliche, aber doch 
unterschiedliche Mikrocontroller.

Soweit ich das in Erinnerung habe, beherrschen beide Typen den 
Pin-Change-Interrupt an allen Pins des PORTB. Du brauchst also deine 
Schaltung nicht anzupassen.

von Thomas D. (thomasderbastler)


Lesenswert?


von Michael (Gast)


Lesenswert?

Hallo,

das mit den Interrupts funktioniert. Zwar wird auch die aktuelle 
Schleife zu ende durchlaufen, danach dann aber umgeschaltet. Egal zu 
welcher Zeit man den Taster betätigt.

Das mit dem Warten ist sicher auch nicht optimal, aber während der 
Wartezeit muss eh nichts Anderes vom Controller gemacht werden. Werde 
die Variante dann aber demnächst auch nochmal testen ohne diese 
Wartezeiten zwischendrin.

Vielen Dank euch erstmal für die schnelle Hilfe.

von W.S. (Gast)


Lesenswert?

Michael schrieb:
> Wäre super, wenn mir da jemand helfen kann.

Du programmierst so, wie man einen sog. "Programmablaufplan" macht. Wenn 
ein µC auf nix anderes zu achten hat und immer nur stur dieses zu tun 
hat, dann geht es ja so. Aber schon das Drücken eines Knopfes ist etwas, 
das den µC beim Abarbeiten deines Programmes eben aus der Bahn wirft: 
Huch, da ist ja was, das ich garnicht vorgesehen habe, weil es asynchron 
zu meinem starren Programmkorsett kommt. Deshalb ist es besser, 
ereignisgesteuert zu programmieren. Nimm einen Timer, der dir die in 
Frage kommenden Zeiten zählen kann und nimm dessen Zähl-Ende als 
Ereignis, das zum Weiterschalten der Ampel benutzt wird. Nimm eine 
Variable her, die als Merkzelle für den aktuellen Zustand dient. Dann 
kannst du am Zähl-Ende je nach gemerktem Zustand die entsprechende 
Aktion durchführen und dir dann den neuen Zustand merken.

Und zwischendurch kannst du nachschauen, ob jemand auf deine Taste 
gedrückt hat und ggf. den aktuellen Zustand entsprechend ändern.

W.S.

von Max H. (hartl192)


Lesenswert?

Wenn du kein anderes Interrupt brauchst könntest du das gesamte 
Notprogramm in der ISR machen.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Ampelanlagen laufen (zumindest in Deutschland) immer im Sekundentakt.

Ich würde diese Tatsache nutzen und das Programm als "State Machine" 
aufbauen.
https://de.wikipedia.org/wiki/Endlicher_Automat
Das Umschalten der Signalbilder passiert dann in einer 
Interrupt-Routine, die im Sekundentakt gestartet wird (siehe "Timer" im 
Datenblatt).

Im Hauptprogramm könntest du dich dann um das Pollen des Tasters 
kümmern, oder – was eleganter ist – du verwendest eine zweite 
Interrupt-Routine für die Tastereingabe und änderst dort einfach den 
zentral gespeicherten Zustand.

Als "Zustand" kannst du die Umlaufsekunde verwenden, also einen Zähler 
von 1 bis z.B. 60. Je nach Sekunde schaltest du dann auf ein bestimmtes 
Signalbild. Zusätzliche Zustände könnten sein:

0 aus
201..220 Einschaltprogramm
221..240 Ausschaltprogramm
255 Gelb blinken

von c-hater (Gast)


Lesenswert?

Michael schrieb:

> das mit den Interrupts funktioniert.

Dafür sind die Dinger ja auch gedacht: Den normalen Ablauf zu jeder Zeit 
unterbrechen zu können.

> Zwar wird auch die aktuelle
> Schleife zu ende durchlaufen, danach dann aber umgeschaltet. Egal zu
> welcher Zeit man den Taster betätigt.

Auch das könnte man vermeiden, wenn man in Assembler programmiert. Dann 
könnte man dafür sorgen, daß jede Interruptauslösung an den Beginn der 
Hauptschleife zurückkehrt statt an die unterbrochene Stelle des 
Programms.

Allerdings ist eine solche Technik höchstens für sehr spezielle 
Anwendungen empfehlenswert, also vergiß' am besten gleich wieder, daß 
diese Möglichkeit überhaupt existiert, so erstrebenswert sie dir im 
Moment auch erscheinen mag.

Erlerne statt dessen erstmal die ereignisorientierte Programmierung, die 
ohne WAITs auskommt, so wie es dir schon im Thread empfohlen wurde. 
Damit kann man die allermeisten Probleme hinreichend gut umsetzen und 
dabei die Programmierroutine erwerben, die nötig ist, um auch 
exotischere Programmstrukturen sicher zu beherrschen.

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Also das mit dem Timer hab ich hinbekommen. Hätte mir auch mal jemand 
sagen können, dass man damit nen counter hochzählt, weil der timer 
ansich viel zu schnell ist. Aber das läuft nun. Allerdings lässt sich 
mein Status nicht mehr umschalten. Der Taster bewirkt grad nur noch, 
dass das ganze Programm stehen bleibt.

Assembler werde ich mit Sicherheit nicht lernen. Auch wenn in diesem 
Bascom Basic hier jegliche Form von Zeichen für Variablen oder 
Beendigung eines Befehls fehlen, so hat es wenigsten noch eine gewisse 
Ähnlichkeit mit anderen Programmiersprachen die ich so benutze. Das 
Assembler mit den Hexcodes ist mir echt zu kryptisch.

Möglicherweise ists nur nen kleiner Fehler, weshalb der Taster nicht die 
gewünschte Funktion übernimmt, aber ich finde Ihn einfach nicht. 
Quellcode ist im Anhang, falls mal wer schauen mag.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Michael schrieb:
> Also das mit dem Timer hab ich hinbekommen. Hätte mir auch mal jemand
> sagen können, dass man damit nen counter hochzählt, weil der timer
> ansich viel zu schnell ist.

Gut! Damit hast du eine wichtige Hürde geschafft. Der ATtiny13/A hat 
leider nur einen 8-Bit-Timer, der ist dann natürlich zu schnell. Aber du 
kannst ja mit dem eigenen Zähler immer bis 1172 und in allen Fällen, in 
denen dieses Maximum noch nicht erreicht ist, die Interruptprozedur 
sofort wieder verlassen.

> Assembler werde ich mit Sicherheit nicht lernen. Auch wenn in diesem
> Bascom Basic hier jegliche Form von Zeichen für Variablen oder
> Beendigung eines Befehls fehlen, so hat es wenigsten noch eine gewisse
> Ähnlichkeit mit anderen Programmiersprachen die ich so benutze. Das
> Assembler mit den Hexcodes ist mir echt zu kryptisch.

Hm... ich komm mit Assembler besser klar als mit Bascom. Bei Assembler 
weiß ich genau, was der Mikrocontroller tut. Aber das ist natürlich 
Geschmacksache. :-)

> Möglicherweise ists nur nen kleiner Fehler, weshalb der Taster nicht die
> gewünschte Funktion übernimmt, aber ich finde Ihn einfach nicht.
> Quellcode ist im Anhang, falls mal wer schauen mag.

Ich weiß nicht, ob ich das alles richtig verstehe, aber du solltest 
"counts" auf 0 setzen, wenn du auf "Gelb Blinken" umschaltest.

Gedacht hatte ich es so, dass du direkt in der Interruptprozedur 
schaust, welche Umlaufsekunde gerade ist und dann davon abhängig die 
richtige Farbe schaltest. Gibt aber sicher auch viele andere Wege.

von W.S. (Gast)


Lesenswert?

Markus Weber schrieb:
> Gut! Damit hast du eine wichtige Hürde geschafft. Der ATtiny13/A hat
> leider nur einen 8-Bit-Timer, der ist dann natürlich zu schnell.

Huch? Wieso das? Hat dieser Timer denn keinen Prescaler?

Normalerweise würde ich den Timer nen Interrupt auslösen lassn und in 
der ISR einfach ne Zählzelle hochzählen. Wenn die genug Zählerstand für 
eine Sekunde hat, dann woanders den Sekundenzähler inkrementieren und 
das ganZe Spiel von vorn.

Im Hauptprogramm kann man dann ganz einfach schreiben(hier nur 
symbolische Notation in ner Art QuasiBasic):

Loop:
if Sekunde <> gemerkteSekunde
then
  gemerkteSekunde = Sekunde;
  AmpelBedienen;
endif
if TasteGedrückt and unentprellt
then
  Ampelzustend = mein_spezieller_Zustand;
  AmpelAktualisieren;
  unentprellt = true;
endif
if TasteEinPaarmalUngedrückt
then
  unentprellt = false;
endif
goto Loop;


W.S.

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.