Forum: Mikrocontroller und Digitale Elektronik Ampelsteuerung mit 80C552 und µvision3 in Assembler


von Kay -. (kingganja)


Angehängte Dateien:

Lesenswert?

Hallo,

ich bräuchte mal eure Hilfe, ich bekomme zuhause meine Keil-Software mit 
Windows7 nicht zum Laufen.

Ich muss eine Ampelsteuerung in Assembler mit einem 80C552 realisieren.

Ich habe da auch etwas geschrieben (s. Anhang) aber da ich es nicht 
ausprobieren kann, möchte ich euch bitten mal drüber zu schauen ob es 
klappen kann.

Da ich nur 8 Ausgänge zur Verfügung habe, gibt es eine Ampel für die 
Hauptstraße, eine für die Nebenstraße und eine Fußgängerampel.

Die Aufteilung des Ports seht Ihr in der Datei.


Folgendes habe ich mir Überlegt, ich möchte die Fußgängerampel mit einem 
Timer-Interrupt auf grün schalten. Dafür starte ich direkt am Anfang 
einen 60s Timer (die Werte für die Timer sind erst mal fiktiv)
In dem Unterprogramm ISRT1 sage ich dann, solange Register 1 nicht null 
ist, springe nicht zu M10. M10 ist dann der Punkt, an dem die Grünphase 
für die Fußgänger beginnt.



Bin mal gespannt ob ich völlig auf dem Holzweg bin

Danke schon mal im Voraus für die Hilfe

von Kay -. (kingganja)


Lesenswert?

hmmm, das ist quatsch was ich da geschrieben habe.

Die Fußgängerampel muss doch in ein Unterprogramm oder?
Und mit dem Timer-Interrupt löse ich das Unterprogramm aus?!?!??

von Route_66 (Gast)


Lesenswert?

Hallo!

1.Tip:
Du darfst die Timer-Initialisierungen natürlich nicht einfach 
"überspringen".

von oldmax (Gast)


Lesenswert?

Hi
Ich kenne weder den Controller noch das benutze Programmiergerät / Tool. 
Aber trotzdem denke ich dir helfen zu können.
Wenn du ein Programm über eine Ampelsteuerung schreiben willst, dann 
überlege, wieviele Schritte du benötigst. Für eine einfache 
Fußgängerampel sind es sechs. Bei einer Kreuzung ein paar mehr. Schreibe 
dir im Klartext einmal  auf, wie diese Schritte aussehen
Grundstellung= Schritt 1
Schritt 1: Hauptstraße hat grün. Fußgänger hat rot
Schritt 2: Hauptstraße hat gelb. Fußgänger hat rot
Schritt 3: Hauptstraße hat Rot.  Fußgänger hat rot
Schritt 4: Hauptstraße hat Rot.  Fußgänger hat grün
Schritt 5: Hauptstraße hat Rot.  Fußgänger hat rot
Schritt 6: Hauptstraße hat Rot und gelb.  Fußgänger hat rot
Schritt 7 = Schritt 1
nun brauchst du einen Timer, sagen wir mal, der alle Sekunden ein UP 
aufruft
In diesem UP reduzierst du einen vorbesetzte Wartezeit (Zähler) und wenn 
diese 0 ist setzt du deinen Schrittzähler weiter, definierst die neue 
Wartezeit, z. B. wenn du die Fußgängerampel in Schritt 4 für 10 sek. auf 
Grün schaltest dann setze die Wartezeit auf 10. Dadurch kannst du die 
Zeiten unterschiedlich lang parametrieren.
Anschließend rufst du eine Auswerte-Routine auf, die erfragt, welcher 
Schritt aktuell ist und entsprechend die Ausgänge setzt. Nun, diese 
Steuerung würde auch ohne Fußgänger die Ampelsequenz ständig 
durchlaufen, also brauchst du etwas, welches diese Sequenz freigibt und 
in Schritt 6 zurückgenommen wird. Da du alles in kleine Unterprogramme 
packen kannst, ist es relativ einfach nachvollziehbar.
Dein Hauptprogramm besteht nun nur noch aus
Start:
        Lies Eingabe
        ist Start_Ampelzyklus dann zu Schrittkette
        Ist Taster dann setze Flag Start_Ampelzyklus
        Setze Wartezeit
Schrittkette:
        Ist_Schrittwechsel dann Auswertung ( und Ausgabe)

        Springe zu Start

Auswertung:
        Ist_Schrittwechsel zurücksetzen
        Inc Schrittzähler
        Ist Schrittzähler =7 dann Schrittzähler =1
        ist Schrittzähler = 1  dann Schritt 1
        Ist Schrittzähler = 2 dann Schritt 2
        usw
        Ret

Schritt 1:
        StartAmpelzyklus zurücksetzen
        Ausgabemuster der Lampen ausgeben
        Ret
Schritt 2:
        nächste Wartezeit eintragen
        Ausgabemuster der Lampen ausgeben
        Ret
etc.
Timer :                       ( aufruf im Sekundentakt)
        Start_AmpelZyklus dann zur Wartezeit
       Reti

Wartezeit:                    ( wenn Taster dann Aufruf im Sekundentakt)
        Dec Wartezeit
        Wartezeit=0 dann Setze Ist_Schrittwechsel
       Ret

Dadurch erhälst du übersichtliche UP's und dein Programm ist auch noch 
für andere Aufgaben gewappnet, da der Timer nicht das Programm aufhält.
Gruß oldmax

von Peter D. (peda)


Lesenswert?

Kay --- schrieb:
> Ich habe da auch etwas geschrieben (s. Anhang) aber da ich es nicht
> ausprobieren kann, möchte ich euch bitten mal drüber zu schauen ob es
> klappen kann.

Nimm einen Simulator und probier es aus.

http://www.dontronics.com/zip/sim51.zip


Wenn Die Autofahrer aber rauskriegen, daß Du es bist, der sie 60s warten 
läßt, wirst Du regelmäßig zerstochene Reifen haben.


Eine ISR mit RET ist ganz schlecht.
Und eine ISR mit LJMP zum Main ist ganz ganz schlecht.


Peter

von KingGanja (Gast)


Lesenswert?

Hmm, ich glaube das ich mir das alles noch mal durch den Kopf gehen 
lassen muss  am Kopf kratz

Aber Danke erst mal für die Anregungen.

von Peter D. (peda)


Lesenswert?

Kay --- schrieb:
> In dem Unterprogramm ISRT1 sage ich dann, solange Register 1 nicht null
> ist, springe nicht zu M10. M10 ist dann der Punkt, an dem die Grünphase
> für die Fußgänger beginnt.

Ich verstehe nicht den Sinn, wozu soll das Springen gut sein?

Auch die StVO verbietet springen. Die Ampelphasen haben immmer in einer 
bestimmten Reihenfolge zu erfolgen!

Wie schon gesagt, ausm Interrupt gibt es nur einen Ausgang und der heißt 
RETI.
Alles andere (DJNZ, LJMP, RET) ist verboten!


Hier mal ein Beispiel, wie ich das machen würde. Ich würde die Delays 
und LED-Muster in eine Tabelle packen, dann kann man sie leicht ändern.
1
;            Beschreibung      
2
;               Port1
3
;          
4
;    HS     Fuß    NS
5
;    |      |      |
6
;  |-------|  |---|  |-------|
7
;  7  6  5  4  3  2  1  0
8
;  rt  ge  gr  gr  rt  gr  ge  rt
9
;
10
CSEG at 8000h
11
Start:
12
  mov  dptr, #ampel_tab  ; start of table
13
main:
14
  clr  a
15
  movc  a, @a+dptr       ; get delay time [sec]
16
  jz  start              ; delay = 0: end of table
17
  call  delay_sec
18
  inc  dptr
19
  clr  a
20
  movc  a, @a+dptr       ; get lights after delay
21
  mov  p1, a
22
  inc  dptr
23
  jmp  main
24
;-------------------------------------------
25
ampel_tab:
26
  db  2,  10001100b      ; Hauptstraße (HS) rot, Fußgänger (Fuß) rot, Nebenstraße (NS) grün
27
  db  10, 11001010b      ; HS rot/geld, Fuß rot, NS gelb
28
  db  2,  00101001b      ; HS grün, Fuß rot, NS rot
29
  db  10, 01001011b      ; HS gelb, Fuß rot, NS rot/gelb
30
  db  2,  10001100b      ; HS rot, Fuß rot, NS grün
31
  db  10, 10001010b      ; HS rot, Fuß rot, NS gelb
32
  db  2,  10010001b      ; HS rot, Fuß grün, NS rot
33
  db  10, 10001011b      ; HS rot, fuß rot, NS rot/gelb
34
  db  0                  ; 0 = end of table
35
;--------------------------------------------
36
delay_val  equ  50000    ; 50.000 cycle = 50ms at 12MHz
37
38
;input: A = delay [1..255sec]
39
delay_sec:
40
  mov  r5, #20           ; 20 * 50ms = 1s
41
_dse1:
42
  mov  r6, #high(delay_val/2+253)
43
  mov  r7, #low(delay_val/2-2)
44
_dse2:
45
  djnz  r7, _dse2
46
  djnz  r6, _dse2
47
  djnz  r5, _dse1
48
  djnz  acc, delay_sec
49
  ret
50
51
52
end


Peter

von Kay -. (kingganja)


Lesenswert?

Hallo Peter,

vielen Dank für deinen Vorschlag, dass mit den Tabellen hatte ein 
Schulkollege auch in der letzten Stunde auf dem Tisch. Da war unser 
Pauker nicht von begeistert, da.....zu fortgeschritten.... nennen wir es 
mal so.


Meine erste Idee ist wirklich quatsch, denn ich kann nicht einfach 
mitten drin den Interrupt auslösen, die Ampelschaltung muss erstmal 
"ablaufen"

Ich denke das man die Fußgängeranforderung speichern muss, und dann an 
geeigneter Stelle das Unterprogramm "Fußgänger" starten sollte.


Ich habe das mal mit

[code]
ISR:      ; Unterprogramm für die Interrupt-Service-Routine

  anl P3.2  ; abfragen des Port 3.2
  setb 20h.0  ; setzen des Speicherbits    20h.0
  reti    ; Rücksprung aus der Interupt-Service-Routine

[/code}

Und an anderer Stelle frage ich dann ab

jnz 20h.0, Fussgaenger  ; springe wenn 20h.0 nicht =0 ist zu "Fußgänger"


aber da meckert er dann beim Build.

Ob das mit dem 20h.0 richtig ist, wage ich allerdings zu bezweifeln, 
dass hat mir ein Schulkollege gesagt.


Gruß
Kay

von Karl H. (kbuchegg)


Lesenswert?

Kay --- schrieb:
> Hallo Peter,
>
> vielen Dank für deinen Vorschlag, dass mit den Tabellen hatte ein
> Schulkollege auch in der letzten Stunde auf dem Tisch. Da war unser
> Pauker nicht von begeistert, da.....zu fortgeschritten.... nennen wir es
> mal so.

Dann bleibt immer noch die Möglichkeit die Tabelle zu 'unrollen'

Schreib dir eine Subroutine, die die Länge der Phase in einem Register 
erhält, das für die Lampen auszugebende Bitmuster in einem anderen 
Register.

Die Subroutine gibt das Muster aus und wartet die angegebene Zeit

Dein Hauptprogramm macht dann nichts anderes als nacheinander für jede 
Phase die beiden Werte in die entsprechenden Register zu laden und die 
Subroutine aufzurufen.

Also im Grunde wie die Tabellenlösung, nur ohne Tabelle :-)
1
mainloop:
2
  mov  r5, #2
3
  mov  r6, 10001100b      ; Hauptstraße (HS) rot, Fußgänger (Fuß) rot, Nebenstraße (NS) grün
4
  call phase
5
6
  mov  r5, 10
7
  mov  r6, 11001010b      ; HS rot/geld, Fuß rot, NS gelb
8
  call phase
9
10
  mov  r5, 2
11
  mov  r6, 00101001b      ; HS grün, Fuß rot, NS rot
12
  call phase
13
14
  mov  r5, 10
15
  mov  r6, 01001011b      ; HS gelb, Fuß rot, NS rot/gelb
16
  call phase
17
18
  mov  r5, 2
19
  mov  r6, 10001100b      ; HS rot, Fuß rot, NS grün
20
  call phase
21
22
  mov  r5, 10
23
  mov  r6, 10001010b      ; HS rot, Fuß rot, NS gelb
24
  call phase
25
26
  mov  r5, 2
27
  mov  r6, 10010001b      ; HS rot, Fuß grün, NS rot
28
  call phase
29
30
  mov  r5, 10
31
  mov  r6, 10001011b      ; HS rot, fuß rot, NS rot/gelb
32
33
  jmp  mainloop

Wenn du schon weißt, wie man Makros anlegt, kann man sich auch ein Makro 
machen und so den Schreibaufwand reduzieren

von Peter D. (peda)


Lesenswert?

Kay --- schrieb:
> Ich denke das man die Fußgängeranforderung speichern muss, und dann an
> geeigneter Stelle das Unterprogramm "Fußgänger" starten sollte.

Da Du einen Interrupteingang nimmst, macht der das Speichern schon 
selber, Du mußt ihn nur auf Flanke setzen.
Und dann im Code:
1
; ... code
2
        jnb     IE0, kein_fussgaenger
3
        clr     IE0                    ; Interrupt Bit loeschen
4
; ... code fuer Fussgaenger Gruen
5
kein_fussgaenger:
6
; ... code

Ein Interrupthandler ist dafür nicht nötig.


Peter

von oldmax (Gast)


Lesenswert?

Hi
1
  mov  r5, 10
2
  mov  r6, 01001011b      ; HS gelb, Fuß rot, NS rot/gelb
3
  call phase
@K.H.
ich hoffe, du sitzt nicht irgendwo an Ampelsteuerungen.. wenn ich HS als 
Hauptstrasse und NS als Nebenstrasse definiere sehe ich auf beiden 
Seiten Gelb... und da darf man schon oder noch fahren.
( kann man hier irgendwo Smilies zum Einfügen finden ? )
Ich weiß nicht, warum ihr soviel Codeschnipsel vorlegt, ich denke ich 
habe die Vorgehensweise allgemein gültig beschrieben. Oder hab ich's 
unverständlich rübergebracht ? Wär ja möglich, denn meine Frau versteht 
mich auch nicht immer....
In euren Vorschlägen sehe ich eigentlich immer, das der Controller in 
der "Wartezeit" der einzelnen Phasen nix anderes machen kann. Der Taster 
braucht keinen Interrupt, es genügt, wenn das Tasterereignis einfach nur 
erfasst wird. Dazu braucht es noch nicht einmal eine Entprellung, da das 
Tastersignal erst nach Ablauf der Ampelphase zurückgesetzt wird. Dagegen 
ist ein Timerinterrupt sinnvoll, da er für viele Zwecke genutzt werden 
kann. Ob im ms-Bereich oder s- Bereich. Er ruft ein kleines UP auf, 
welches dem Ereignis entsprechend reagiert. Wie bereits beschrieben:
z.B.
dec. eines Wertes in einer Variablen / Register
kontrollieren ob null und dementsprechend entweder nix tun oder Ereignis 
bearbeiten.
Das kann so schwer doch nicht sein.
Gruß oldmax

von Peter D. (peda)


Lesenswert?

oldmax schrieb:
> ich hoffe, du sitzt nicht irgendwo an Ampelsteuerungen.. wenn ich HS als
> Hauptstrasse und NS als Nebenstrasse definiere sehe ich auf beiden
> Seiten Gelb... und da darf man schon oder noch fahren.

Ob die Phasen und Kommentare stimmen, muß der OP schon selber prüfen. 
Die Beispiele sollen nur das Prinzip zeigen.


> In euren Vorschlägen sehe ich eigentlich immer, das der Controller in
> der "Wartezeit" der einzelnen Phasen nix anderes machen kann.

Könnte man leicht ändern. Aber man muß ja auf den Pauker Rücksicht 
nehmen.


> Dazu braucht es noch nicht einmal eine Entprellung

Stimmt.
Für mich selber würde ich trotzdem die Entprellung aus der Schublade 
ziehen. Die dient dann eben nur der Störunterdrückung.


Peter

von Kay -. (kingganja)


Lesenswert?

Hey,
ich habe mit einem Schulkollegen gestern noch etwas gebastelt.

Danke für die vielen Ideen, aber die meisten waren/sind etwas zu hoch 
gegriffen, so lange machen wir das noch nicht mit dem Assembler.

Ich habe nochmal eine andere Version von der 'Steuerung angehangen, 
vieleicht kann da ja mal jemand rüber schauen.

Muss mir unbedingt mal einen USB/serial-Adapter zulegen.


Beim Build mekert Keil bei drei zeilen,

Text1.a52(68): error A38: NUMBER OF OPERANDS DOES NOT MATCH INSTRUCTION
Text1.a52(101): error A38: NUMBER OF OPERANDS DOES NOT MATCH INSTRUCTION
Text1.a52(144): error A38: NUMBER OF OPERANDS DOES NOT MATCH INSTRUCTION


aber wieso??

Gruß

von Kay -. (kingganja)


Angehängte Dateien:

Lesenswert?

hier noch die Datei

von Karl H. (kbuchegg)


Lesenswert?

Wenn du noch nett bist, dann suchst du die Zeile 68 raus und postest die 
extra

> so lange machen wir das noch nicht mit dem Assembler

Dann solltet ihr gleich mal eine der wichtigsten Lektionen lernen:
Komentiere nicht das, was sowieso schon im Code steht!

Solche Kommentare
1
    mov R0, #200        ; lade das Register 0 mit 200 vor
2
    call Timer          ; Aufruf des Unterprogramm "Timer"
sind für die Tonne! Die kannst du dir sparen. Der Kommentar sagt nichts 
aus, was nicht auch schon im Quelltext steht. Im Quelltext steht, dass 
R0 mit 200 geladen wird, im Kommentar steht, dass R0 mit 200 geladen 
wird. Wozu dann der Kommentar, wenn dort sowieso nichts steht, was mich 
weiterbringt. Im besten Fall bringt mich so ein Kommentar nicht weiter. 
Im schlimmsten Fall ist er einfach nur falsch, weil zb der Code geändert 
wird und auf den Kommentar vergessen wurde.

Was mich interessieren würde: Was hat es mit diesen 200 auf sich? Warum 
200? Warum nicht 199 oder 201? Haben die 200 eine Bedeutung? Welche?

genauso bei der anderen Zeile
Das hier das UP Timer aufgerufen wird, sehe ich auch im Code, das 
brauchst du im Kommentar nicht hinschreiben.

Generell: Kommentiere das Warum? und nicht das Wie?
Bei einem Kommentar will ich wissen: Warum wird etwas gemacht? Was ist 
die Idee an dieser Stelle im Code? Welche Algorithmen werden unter 
Umstaänden verwendet? Wie funktionieren die? Welche Bewandtnis hat es 
mit irgendwwelchen Zahlenwerten?

Das gehört in den Kommentar

Wie etwas dann implementiert wird, sehe ich im Code.

von Karl H. (kbuchegg)


Lesenswert?

jnz 20h.0, Fussgaenger    ; springe wenn 20h.0 nicht =0 ist zu 
"Fußgänger"


Äh nein.
So funktioniert das nicht.
Was soll das sein "wenn 20h.0 nicht 0 ist"
Ich kenn zwar deinen µC überhaupt nicht, aber das würde mich schwer 
wundern, wenn man sich bei einem jnz auf ein Bit eines Ports beziehen 
könnte
Ein jnz wertet üblicherweise die Statusflags aus, die ein vorhergehender 
Befehl wie gewünscht eingestellt hat

von Kay -. (kingganja)


Lesenswert?

@K.H.

Ich schreibe mir immer die Finger wund weil mein Pauker das so 
möchte...... leider.
Aber durch dein "Einwand" gegenüber den Komentaren, ist meine Meinung 
gegenüber dem Pauker gefestigt, er hat keine Ahnung von dem was er 
erzählt. Das merkt man im Unterricht sehr. Der kann keinem einen roten 
Fahden in die Hand geben und mal ein Programm durcharbeiten.

Aber das mit dem roten Fahden habe ich auch in noch keinem Buch 
gefunden.
Ich suche wirklich ein Buch, das wie bei allen anderen Themen mit dem 
"berühmten" "Hello World" anfängt.

Ich denke ich gebe das Programm so ab und gut ist.

von Peter D. (peda)


Lesenswert?

Kay --- schrieb:
> Ich denke ich gebe das Programm so ab und gut ist.

Als Pauker würde ich darauf bestehen, auch das Hex-File zu kriegen. Dann 
kannnst Du schonmal nichts abgeben, was einen Fehler ausgibt.

Und simulieren ist wirklich nicht schwer.


Peter

von Route_66 (Gast)


Lesenswert?

Hallo!
Beim 8051 ist 20h im internen RAM bit-adressierbar. Es dient also als 
Flag.
Je nach Assembler kann man das aber nicht so schreiben. Es ist nämlich 
an der Bitadresse Null. Der Assemblerbefehl lautet also

JNB 0h, marke

(JumpNoBit adresse, label)

von Kay -. (kingganja)


Lesenswert?

Peter Dannegger schrieb:
> Kay --- schrieb:
>> Ich denke ich gebe das Programm so ab und gut ist.
>
> Als Pauker würde ich darauf bestehen, auch das Hex-File zu kriegen. Dann
> kannnst Du schonmal nichts abgeben, was einen Fehler ausgibt.
>
> Und simulieren ist wirklich nicht schwer.
>
>
> Peter

Du ich wolte den Fehler nicht verheimlichen, das Programm wird eh auf 
Funktion geprüft.
Wenn du die Hex-Datei meinst an die ich gerade denke, ist diese eh auf 
einem Eprom.


Gruß

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.