Ampelsteuerung mit 8051 Assembler

Servus, ich habe ein Problem mit meiner Ampelsteuerung(s.u.) Es ist eine normale Kreuzung, an der einen Seite ist dabei ein Fußgängerüberweg der per Taster ausgelöst werden kann. Die Zeitschleife für die jeweiligen Phasen muss ich mit einem Timer umsetzen, das verlangt die Aufgabe. Den Taster muss ich mehr oder weniger mit einem Interrupt0 bearbeiten, da es sich nur um einen Drucktaster und keinen Schalter handelt(also wie real). Dabei liegt bei mir das Problem. Einen Timer0 arbeitet ebenso mit einem Interrupt und es scheint, als würden die sich nicht mögen. Man kann ja die Prioritäten verändern. Wenn ich nun die Priorität des Timers höher setze als die des Interrupts funktioniert nur noch der Timer, andersrum funktioniert nur noch der Interrupt, was kann ich da tun? Wo liegt bei mir da der Fehler? Also der Controller ist von Atmel, T89C51RD2 so weit ich weiß.
mfg

INCLUDE C51RD2.INC
code at 0
SJMP init
ORG 0003h
CLR TR0
SETB C
RETI
SJMP init
ORG 000Bh
INC A
RETI
init:
SETB EA
SETB EX0
SETB ET0
SETB TF0
MOV TMOD,#00000001b
SETB IE0
SETB IT0
MOV IPL0,#00000001b
MOV TL0,#0
MOV TH0,#0
MOV P2,#10110110b
haupt:
MOV P2,#10110110b
LCALL zeit1
MOV P2,#10100110b ;#Rot1Rot2Gelb2Rot3
LCALL zeit1
MOV P2,#10011110b ;#Rot1Grün2Rot3
LCALL zeit4
MOV P2,#10101110b ;#Rot1Gelb2Rot3
LCALL zeit2
MOV P2,#10110110b ;#Rot1Rot2Rot3
JC taster
LCALL zeit1
MOV P2,#10110100b ;#Rot1Gelb1Rot2Rot3
LCALL zeit2
MOV P2,#10110011b ;#Grün1Rot2Rot3
LCALL zeit4
MOV P2,#10110101b ;#Gelb1Rot2Rot3
LCALL zeit2
MOV P2,#10110110b ;#Rot1Rot2Rot3
JC taster
LCALL zeit1
LJMP haupt
taster:
LCALL zeit1
MOV P2,#10110110b ;#Rot1Rot2Rot3
LCALL zeit1
MOV P2,#01110110b ;#Rot1Rot2Grün3
LCALL zeit4
MOV P2,#10110110b ;#Rot1Rot2Rot3
CLR C
SJMP haupt
zeit1:
SETB TR0
CJNE A,#00000101b,zeit1
CLR A
CLR TR0
RET
zeit2:
SETB TR0
CJNE A,#00001000b,zeit2
CLR A
CLR TR0
RET
zeit4:
SETB TR0
CJNE A,#00010011b,zeit4
CLR A
CLR TR0
RET

END

Servus, ich habe ein Problem mit meiner Ampelsteuerung(s.u.)

Hallo,

dazu gäbe es an vielen Stellen etwas zu beanstanden, nur das Wichtigste:

Soweit ich das verstehe, ohne extra nachzuschlagen, wird in der Interrupt-Service-Routine für den Taster der Timer angehalten. Damit läuft aber die ganze Maschine wahrscheinlich in einer Endlosschleife, weil das Zählerende nicht mehr erreicht wird.

Gruss Reinhard

Erstmal danke für die Antwort. Eigentlich sollte das Programm soweit in Ordnung sein, bis auf den Fehler mit dem Interrupt. Sollte die Priorität des Tasters dann höher gestellt sein und falls er dann in den Taster-Interrupt springt, danach den Timer erneut starten?
mfg

Hallo,

sei froh, wenn sich keine unbeabsichtigten Nebenwirkungen ergeben, und führ nicht noch selbst welche ein. Die Taster-ISR soll den Tastendruck erfassen und sonst nichts. Regel: Probleme separieren. Die Zeitsteuerung sollte ebenfalls ganz unabhängig laufen, es ist eigentlich schon nicht gut, den Timer ständig anzuhalten und neu zu starten, das gibt nur zusätzliche Fehler.

Wenn die Programmierung korrekt ist, hat die Priorität praktisch keinen Einfluss: man könnte damit nur erreichen, dass der Tastendruck ein paar µsec früher erfasst wird, aber bei Handeingaben kommt es auf Zehntelsekunden nicht an. Und an einer Ampel muss man u.U, eine Minute warten, bis es grün wird, da nützen mich die paar Microsekunden garnichts.

Gruss Reinhard

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Und wie genau sollte ich das dann umsetzen? Sollte ich den Timer zu Beginn starten und in der Zeitschleife den Akku auf 0 setzen, danach den Akku auf die bestimmte Konstante abfragen? Was meinen Sie mit den vielen kleinen Problemen???
mfg

Den Taster muss ich
mehr oder weniger mit einem Interrupt0 bearbeiten, da es sich
nur um einen Drucktaster und keinen Schalter handelt(also wie
real).

Versteh ich nicht. Frag den Zustand des Tasters fortlaufend z. B. alle 20 ms ab (*) und speichere ihn in einer Variablen – fertig. Der Glaube, dass man Taster über Interrupts betreiben muss (warum denn?) , ist Anfängerdenken. Das muss man nämlich (fast) nie.

(*) Nein, Du brauchst nicht für jeden Taster einen eigenen Timer, sondern wahrscheinlich nur einen Timer (ja: nur einen einzigen Timer!) für Dein komplettes Programm (einverstanden?).

Gruß
Martin

Und wie genau sollte ich das dann umsetzen? Sollte ich den
Timer zu Beginn starten

Du richtest einen Timer ein, der z. B. mit 20 ms vor sich hin „tickt“. Dieser Timer wird niemals angehalten oder sein Zählerstand verändert oder irgendwie anderweitig „manipuliert“. Er läuft während der gesamten Programmlaufzeit. Dein komplettes Programm „hängst“ Du an diesem 20 ms-Timertick auf. Dazu zählst Du Variablen (für jede Aufgabe eine) bei jedem Timertick rauf oder runter und führst bei Erreichen bestimmter Zählerstände die zugehörigen Aktionen aus (diese Zähler stellen sozusagen „Software-Timer“ dar).

Und wie genau sollte ich das dann umsetzen? Sollte ich den
Timer zu Beginn starten und in der Zeitschleife den Akku auf 0
setzen, danach den Akku auf die bestimmte Konstante abfragen?
Was meinen Sie mit den vielen kleinen Problemen???
mfg

Hallo,

wie schon gesagt, der Timer wird auf einen festen Wert programmiert und läuft dann endlos, wobei er z.B. alle 1 msec einen Interrupt auslöst. Dann kann man in der ISR bis 1000 zählen und dann die Uhrzeit um eine Sekunde erhöhen, usw. usw. Starten und Stoppen des Timers ist von Zufällen abhängig und ergibt ungenaue Zeitmessungen.

Ein Beispiel dafür, wie man es nicht macht: eine Schleife mit der Abfrage auf Equal zu beenden, ist ein Anfängerfehler. Es kann in einem System mit Interrupt vorkommen, dass ein Schritt nicht erfasst wird, oder man ändert was am Programm und zählt immer gleich um 2 hoch. Wenn man also auf 100 testet und das zufällig überspringt, so dass also auf 99 gleich 101 folgt, so zählt die Schleife bis in alle Ewigkeit weiter oder zumindest bis zum Überlauf des Zählers. Die korrekte Abbruchbedingung muss heissen Greater or Equal, das ist eine Frage der defensiven Programmierung. Man darf einfach keinen Platz für Fehler lassen, von 0 bis 100 läuft die Schleife, jeder beliebige andere Wert muss zum Abbruch führen. Einfach zu behaupten, > 100 kommt nicht vor, ist schlechte Programmierarbeit und irgendwann wird man von der Realität eines anderen belehrt.

Gruss Reinhard

Meiner Meinung nach kann man den Timer gar nicht beeinflussen, also keinen festen Wert für den auszulösenden Interrupt festlegen. Wir haben das in der Schule mal ausgerechnet, da kam was von 65,ms raus. Ich habe das jetzt auf jeden Fall so hinbekommen. Ich zähle bei jedem Timer-Überlauf den Akku um eins hoch und lösche dann einfach beim Start des UP der Zeitschleife den Akku wieder bis er den Wert erreicht hat. Habs ausprobiert und es hat geklappt. Bin euch auf jeden Fall mal dankbar für die Hilfe.
Bin jetzt nur froh, dass das Programm geht, da es schließlich meine GFS wird ^^
dankeschön mal

mfg
Manuel

Hallo,

Meiner Meinung nach kann man den Timer gar nicht beeinflussen,
also keinen festen Wert für den auszulösenden Interrupt
festlegen. Wir haben das in der Schule mal ausgerechnet, da
kam was von 65,ms raus.

Das gilt nur, wenn Du den Timer einfach durchlaufen lässt. Du kannst aber den Startwert des Timers setzen auf 0-x. Dann wird nach x-maligem Erhöhen des Timerwertes der Interrupt ausgelöst. Und damit kannst Du dann jede beliebige Interruptzeit erreichen, sofern das mit den möglichen Zählfrequenzen zu machen ist.
Gruß
Axel

ah, ok, wusste ich bisher noch nicht, habe dazu auch leider die betreffenden Zeilen auch nicht. Aber so passt dat ja auch. Wenn ich sowieso den Timer mit einer Variablen vergleichen muss, bzw die Interrupts, die der Timer dann ausführt.
mfg

soweit klar, aber wie würde programmtext aussehen, wenn du alle 20ms den Taster abfragen würdest? Bei einem längeren Programmtext, der dazu noch verschachtelt ist wird es dann nicht gerade einfach, bzw. logisch.
mfg

soweit klar, aber wie würde programmtext aussehen, wenn du
alle 20ms den Taster abfragen würdest? Bei einem längeren
Programmtext, der dazu noch verschachtelt ist wird es dann
nicht gerade einfach, bzw. logisch.

Doch, der wird gerade dann einfach und logisch.

Du richtest einen Timer ein, der mit 20 ms vor sich hin tickt. Das heißt, es gibt eine Interrupt-Service-Routine (Timer-Overflow-Interrupt, oder Timer-Output-Compare-Interrupt), die fortwährend im Abstand von 20 ms aufgerufen wird (während der gesamten Programmlaufzeit). Dort hinein schreibst Du die Tastenabfrage. Und nicht nur die, sondern alles (!) andere auch.

In dem Programm darf es dann natürlich keine einzige „Delayschleife“ nach Art von „tue 100000 mal {}“ mehr geben, oder so Geschichten wie „warte solange bis jemand die Taste drückt“. Aber wenn Du deine Software timergesteuert aufziehst, besteht dazu auch gar keine Notwendigkeit mehr.

Du musst nur dafür sorgen, dass die Gesamtheit aller Aufgaben, die Dein µC alle 20 ms erledigen muss, auch im schlimmsten Fall nicht mehr Rechenzeit als ebendiese 20 ms in Anspruch nehmen (sonst würden Timerinterrupts „verlorengehen“). Diese Forderung stellt aber meistens überhaupt kein Problem dar, denn während 20 ms kann der µC schließlich noch verdammt viele Instruktionen abarbeiten (rechne doch mal aus, wieviele es bei der von Dir gewählten Taktfrequenz sind).