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.
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
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]
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.
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.
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.
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.
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
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.
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.
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.
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.