rjmp main ;Sprung zur marke "ende" -> endlosschleife
20
21
delay5s: ; 5ms Pause
22
; =============================
23
; Warteschleifen-Generator
24
; 5000000 Zyklen:
25
; -----------------------------
26
; warte 4999995 Zyklen:
27
ldi R17, $21
28
WGLOOP0: ldi R18, $D6
29
WGLOOP1: ldi R19, $EB
30
WGLOOP2: dec R19
31
brne WGLOOP2
32
dec R18
33
brne WGLOOP1
34
dec R17
35
brne WGLOOP0
36
; -----------------------------
37
; warte 3 Zyklen:
38
ldi R17, $01
39
WGLOOP3: dec R17
40
brne WGLOOP3
41
; -----------------------------
42
; warte 2 Zyklen:
43
nop
44
nop
45
; =============================
46
ret
47
; wieder zurück
48
49
delay10s:
50
; =============================
51
; Warteschleifen-Generator
52
; 10000000 Zyklen:
53
; -----------------------------
54
; warte 9999990 Zyklen:
55
ldi R17, $42
56
WGLOOP01: ldi R18, $D6
57
WGLOOP11: ldi R19, $EB
58
WGLOOP21: dec R19
59
brne WGLOOP21
60
dec R18
61
brne WGLOOP11
62
dec R17
63
brne WGLOOP01
64
; -----------------------------
65
; warte 9 Zyklen:
66
ldi R17, $03
67
WGLOOP31: dec R17
68
brne WGLOOP31
69
; -----------------------------
70
; warte 1 Zyklus:
71
nop
72
; =============================
73
ret ;wieder zurück
74
75
76
green:
77
rcall delay5s
78
ldi r17,(1<<PB0)|(0<<PB1)|(0<<PB2)
79
out PORTB,r17
80
rcall delay10s
81
ldi r17,(0<<PB0)|(1<<PB1)|(1<<PB2)
82
out PORTB,r17
83
rcall delay5s
84
ret
85
86
red:
87
ldi r17,(0<<PB0)|(1<<PB1)|(1<<PB2)
88
out PORTB,r17
89
ret
90
91
greensof:
92
ldi r17,(1<<PB0)|(0<<PB1)
93
out PORTB,r17
94
ret
so normal soll die ampel immer rot sein wenn kiener der ports gegen gnd
liegt aber die ampel schaltet immer zeischen grün und rot hin und her
und es ist ihr egal ob ich einen der ports an gnd lege oder nicht.
wo habe ich den fehler gemacht?
habe jetzt aus rjamp rcall gemacht aber sie springt noch immer hin und
her und wenn ich Port 4 an gnd halte den leuchtet keine lede mehr danach
wiederum beide dann nur grün und denn wieder hin und her
Hmm
Der Tiny13 ist doch ein älterer Tiny.
Bei denen wurde doch der Stackpointer noch nicht automatisch
initialisiert.
Schaden kann es auf keinen Fall, wenn man dem Stackpointer erst mal
etwas vernünftiges zuweist.
1
.include "tn13def.inc" ;Definitionsdatei für den Prozessor typ
Wenn ich noch einen Vorschlag machen darf:
Sortiere deinen Code um. Die Delay Funktionen müssen da nicht mitten im
Code stehen. Wenn du diese Blöcke ans Programmende verschiebst, dann
hast du Hauptschleife und die einzelnen Unterprogramme physisch näher
beieinander und man muss nicht dauernd rauf/runter scrollen um sich
anzusehen, was wann passieren soll. Zumal deine nicht existente
Codeformatierung die Sache auch nicht gerade leichter zu lesen und zu
verstehen macht.
1
.include "tn13def.inc" ;Definitionsdatei für den Prozessor typ
rjmp main ;Sprung zur marke "ende" -> endlosschleife
25
26
green:
27
rcall delay5s
28
ldi r17,(1<<PB0)|(0<<PB1)|(0<<PB2)
29
out PORTB,r17
30
rcall delay10s
31
ldi r17,(0<<PB0)|(1<<PB1)|(1<<PB2)
32
out PORTB,r17
33
rcall delay5s
34
ret
35
36
red:
37
ldi r17,(0<<PB0)|(1<<PB1)|(1<<PB2)
38
out PORTB,r17
39
ret
40
41
greensof:
42
ldi r17,(1<<PB0)|(0<<PB1)
43
out PORTB,r17
44
ret
45
46
delay5s: ; 5ms Pause
47
; =============================
48
; Warteschleifen-Generator
49
; 5000000 Zyklen:
50
; -----------------------------
51
; warte 4999995 Zyklen:
52
ldi R17, $21
53
WGLOOP0: ldi R18, $D6
54
WGLOOP1: ldi R19, $EB
55
WGLOOP2: dec R19
56
brne WGLOOP2
57
dec R18
58
brne WGLOOP1
59
dec R17
60
brne WGLOOP0
61
; -----------------------------
62
; warte 3 Zyklen:
63
ldi R17, $01
64
WGLOOP3: dec R17
65
brne WGLOOP3
66
; -----------------------------
67
; warte 2 Zyklen:
68
nop
69
nop
70
; =============================
71
ret
72
73
delay10s:
74
; =============================
75
; Warteschleifen-Generator
76
; 10000000 Zyklen:
77
; -----------------------------
78
; warte 9999990 Zyklen:
79
ldi R17, $42
80
WGLOOP01: ldi R18, $D6
81
WGLOOP11: ldi R19, $EB
82
WGLOOP21: dec R19
83
brne WGLOOP21
84
dec R18
85
brne WGLOOP11
86
dec R17
87
brne WGLOOP01
88
; -----------------------------
89
; warte 9 Zyklen:
90
ldi R17, $03
91
WGLOOP31: dec R17
92
brne WGLOOP31
93
; -----------------------------
94
; warte 1 Zyklus:
95
nop
96
; =============================
97
ret
funktional ist das völlig gleichwertig, aber arbeitstechnisch ist es
viel einfacher damit zu arbeiten. Und warum die delay10s nicht einfach
aus 2 Aufrufen von delay5s besteht, weiß auch nur der Programmierer :-)
Karl heinz Buchegger schrieb:> Und warum die delay10s nicht einfach> aus 2 Aufrufen von delay5s besteht, weiß auch nur der Programmierer :-)
Es ist eben einfacher, blind und gedankenlos sowas
http://www.home.unix-ag.org/tjabo/avr/AVRdelayloop.html zu bedienen, als
auch nur kurzzeitig sein Kopf einzusetzen (siehe auch oben: mehr als
einmal rjump vs. ret).
worüber ihr euch aufregt... ist es nicht egal womit man 5 bzw. 10
sekunden rechenzeit vernichtet, wenn der flash groß genug ist? sorry
aber ich hasse es wenn man immer über den programmierstil anderer
herziehen muß. solange wie's funktioniert ist doch gut. als tip kann man
sowas ja anmerken, aber doch nicht bitte gleich als grundfalsch oder
bescheuert hinstellen.
Ben _ schrieb:> worüber ihr euch aufregt... ist es nicht egal womit man 5 bzw. 10> sekunden rechenzeit vernichtet, wenn der flash groß genug ist?
Wenn der Programmierer den Überblick verliert, ist es nicht mehr egal
> sorry> aber ich hasse es wenn man immer über den programmierstil anderer> herziehen muß. solange wie's funktioniert ist doch gut.
"Funktioniert" ist nicht alles
> als tip kann man> sowas ja anmerken
genau so hab ich es auch gemeint.
Oder was ist an der Einleitung des Abschnitts nicht zu verstehen:
<Zitat>
Wenn ich noch einen Vorschlag machen darf:
</Zitat>
> , aber doch nicht bitte gleich als grundfalsch oder> bescheuert hinstellen.
Hat niemand gemacht.
also die antwort von unserem lieben gast otto fand ich schon ziemlich
grenzwertig. "auch nur kurzzeitig seinen kopf einzusetzen" hat für mich
nichts mit dem "problem" zu tun wie man am besten die funktion
aufsplittet oder auch nicht.
und wenn der programmierer in seinem eigenen programm den überblick
verliert macht er mehr als das verkehrt und merkts hoffentlich. meiner
meinung nach eignet sich jeder einen programmierstil an, mit dem er
selber halt am besten klarkommt. die einhaltung bestimmter formen halte
ich nur dann für wichtig wenn andere mit dem quellcode klarkommen
müssen, etwa wenn ein programm von mehrere programmierern geschrieben
wird.
ich z.b. würde meine funktion für sowas variabel gestalten wenn ich
mehrere zeitintervalle brauche, etwa
LDI r16,10
RCALL wait
für eine pause von 10 sekunden.
das finde ich aber nicht schlechter oder besser als andere lösungen
solange sie im gleichen rahmen funktionieren. es finden sich bestimmt
sogar leute die mich zusammenhauen weil ich "unnötigerweise" ein
register verwende.
ok wollte auch keine diskussion vom zaun brechen, sondern nur darauf
aufmerksam machen, daß man sich auf die wirklichen probleme
konzentrieren sollte als auf kleinigkeiten im programmierstil. also
**prost!**
Hi
Auch wenn der Hinweis auf die Zuweisung der Stackadresse schon genannt
ist, möchte ich sie trotzdem wiederholen. Außerdem schreibst du von
Portpins, die gegen GND gschaltet werden und dann die Ampel auslösen.
Ich sehe aber keinen In-Befehl. Irgendwie sollte aber der Controller
schon mitbekommen, wenn sich in seiner Peripherie etwas ändert, und so
solltest du schon sagen, das er sich den Pin mal ansehen soll. Ich kenne
zwar den Attiny13 nicht, denke aber, das es da auch einen In-Befehl
gibt.
Ansonsten hilft bei der Analyse eines Problemes nur logisch vorzugehen
und das Verhalten zu durchleuchten. Sicher ist, das er genau das macht,
was du ihm befohlen hast..... auch wenn du etwas ganz anderes
wolltest.
Gruß oldmax
oldmax schrieb:> Ich sehe aber keinen In-Befehl. Irgendwie sollte aber der Controller> schon mitbekommen, wenn sich in seiner Peripherie etwas ändert, und so> solltest du schon sagen, das er sich den Pin mal ansehen soll. Ich kenne> zwar den Attiny13 nicht, denke aber, das es da auch einen In-Befehl> gibt.
SBIS bzw. SBIC machen das.
Skip If Bit (in I/O Register) ist Set/Clear
So gesehen ist das schon in Ordnung.
Ohne das jetzt simuliert oder getestet zu haben: Ich denke es ist der
fehlende Stackpointer wodurch alle RET ins Leere gehen.
Sven M. schrieb:> hallo ich bis nochmal undzwar sehe ich es so das doch zu jedem rcall> oder rjmp ein ret am ende der aufgerufenen funktion kommt oder ist das> falsch?
Nur bei call.
Bei jmp muss auch mit jmp weitergemacht werden.
Sven M. schrieb:> sprich wenn ich in eine funktion springe muss ich auch wieder am ende> mit z.B. rjmp main aus ihr raus springen?
Genau.
RJMP springt einfach irgendwo hin. Punkt. Mehr gibt es dazu nicht zu
sagen.
Aber RCALL und RET gehören zusammen.
RCALL springt irgendwo hin UND speichert sich die Adresse von wo
weggesprungen wurde auf dem Stack ab. Wird dann ein RET ausgeführt, so
holt sich der RET diese Adresse wieder vom Stack und führt einen Sprung
dorthin aus. Der RET springt also wieder dorthin zurück, von wo der
RCALL hergekommen ist.
Allerdings: Der RET weiß nichts von einem RCALL. Er holt sich einfach
Bytes vom Stack, interpretiert die als Adresse und springt dorthin. Es
gibt nämlich auch noch andere Möglichkeiten wie man auf dem Stack etwas
ablegen kann, nicht nur RCALL. Das heißt, man muss dann aufpassen, dass
dieser Mechanismus nicht durcheinander kommt. Der RET passt da nicht
darauf auf.
Zeig dochmal den Schaltplan.
Oder erzähl mal, wo was angeschlossen ist und wo gegen.
Hast Du Widerstände in Reihe zu den LEDs?
Hast Du Pullups an den Tasten?
Der ATtiny13 läuft übrigens mit 1,2MHz und der Stack ist nach dem Reset
schon richtig gesetzt.
Peter
also PB4 Taster gegen GND PB3 andere µC entweder gegen GND oder VCC
pullups sind doch aktiviert...
LED's:
VCC->R1,5K->LED Rot 2mA->PB0
VCC->R1,5K->LED Grün 2mA->PB1
PB2 Ausgang zu anderem µC
Ach.
An den Kopf klatsch.
Du brauchst natürlich die Pullup an den Eingängen.
Nur:
zb Hier
red:
ldi r18,(0<<PB0)|(1<<PB1)|(1<<PB2)
out PORTB,r17
ret
schaltest du dir irrtümlich die Pullups wieder weg.
Damit sind dann die Eingänge offen und alles mögliche kann passieren.
Ein klassischer Fehler, wenn man nicht genau Buch darüber führt, welche
Portbits welchen Zustand haben MÜSSEN und sich nicht ändern dürfen. Bei
dir ist es eben PORTB, PB4 und PB3 die 1 sein müssen.
also habe das ganze mal aufgespielt wenn ich die schaltung unter strom
setzte leuchtet erst rot dann dauer grün egal ob ich die pins gegen VCC
oder GND lege-.- ich finde einfach keinen fehler
Hi
>Man könnte sich hier viel Arbeit und Diskussion ersparen durch>Verwendung eines C-Compilers.
Stimmt. C korrigiert die Fehler automatisch.
MfG Spess
Erich schrieb:> Man könnte sich hier viel Arbeit und Diskussion ersparen durch> Verwendung eines C-Compilers.
Würde auch nicht viel ändern.
Sorgfältig arbeiten muss man da wie dort.
Die Art der Fehler ist in C dann eben eine andere.
1.)
>Aber RCALL und RET gehören zusammen.
2.)
>>habe jetzt aus rjamp rcall gemacht aber sie springt noch immer hin und>>her>Ja, du hast noch mehr rjmps mit einem ret beendet;) Der arme Stack.
3.)
>herlich manchmal steht man cht vor ner wand -.- natürlich lese ich das>falsche register aus
Das sind z.B. 3 Situationen, die mit einem C Compiler in dieser Art und
Weise überhaupt nicht passieren können.
Klar, mit den Ports, der Initialisierung, den Masken und Operationen
muss man sich auch in C auseinandersetzen, sonst wird das nix.
>Sorgfältig arbeiten muss man da wie dort.
Das stimmt.
Hi
>Das sind z.B. 3 Situationen, die mit einem C Compiler in dieser Art und>Weise überhaupt nicht passieren können.
Und wenn man Assembler kann, passieren die auch nicht. Du wirst doch
nicht die Schwierigkeiten eines Anfängers als Arument benutzen. Was
meinst du, was
ich hier schon für Anfänger-C-Code gesehen habe, der als Gegenargument
getaugt hätte. Also lass deine Missionierungsversuche.
MfG Spess
Natürlich muss ich dazusagen C ist was feines da es eine hochsprache
ist... ich habe mal mit C für programme angefangen und durfte
ähnlichkeit zu PHP faststellen daher sind mir manche sachen in C recht
leicht gefallen... da die sprachen eine gewisse ähnlichkeit haben
Heyy ich bins nach langem nochmal :)
undzwar folgendes das mit den rot und grün phasen über PB3 geht aber das
mit sofort green geht nicht die rote led wird nur etwas schwächer wenn
ich PB4 an masse anlege
OK.
Den 2.ten Registerfehler hast du gesehen.
Aber: Was denkst du?
Wenn du PB4 drückst, dann geht der rcall nach greensof. Dort wird rot
ausgschaltet und eine andere ein. Gleich darauf geht es mit einem ret
wieder zurück zur Aufrufstelle wo gleich danach ein rcall nach rot
stattfindet wo rot wieder eingeschaltet wird.
Was denkst du, wie lange das in etwa dauert, dass rot abgeschaltet ist?
10µs, 15µs?
Auf jeden Fall viel zu kurz als dass du als Mensch das wahrnehmen
könntest, dass rot tatsächlich aus ist. Du siehst das als: rot wird im
Mittel etwas schwächer. Und wenn du dir die andere LED ganz genau
ansiehst, dann wirst du sehen, dass sie ganz leicht glimmt.
Vergleich doch einfach mal, wodurch sich der Code beim Drücken von PB3
und beim Drücken von PB4 unterscheidet. In der Subroutine, die nach PB3
angesprungen wird, sind Wartezeiten drinnen, die dir erlauben, das
Aufleuchten der entsprechenden LED (und das Abschalten von Rot) auch
tatsächlich zu sehen. In der Subroutine von PB4 sind diese Wartezeiten
aber nicht drinnen.
Nachdem das gesagt ist: Du solltest tatsächlich dein Zeitkonzept nochmal
überdenken. So wie es jetzt ist, ist es nicht sehr flexibel.
Auf der anderen Seite macht man so eine Ampelsteuerung allerdings in der
Realität ganz anders (mit einer State-Maschine) und ich verstehe schon,
dass das für dich jetzt erst mal einfach nur eine Übung ist, damit sich
da etwas an den LEDs tut. Nichts desto trotz musst du dir mehr Sorgfalt
und mehr Beobachtung auf die Fahnen schreiben. Einfach irgendwas
hinzuschreiben, ist auf Dauer zu wenig. Wenn deine Programme nicht
funktionieren, dann musst du sie analysieren. Auch, oder auch ganz
speziell mit dem Hintergedanken: Moment, wie lange dauert das denn
eigentlich? Hier und hier schalte ich zwar die LED ein, aber wie gehts
dann im Programm weiter? Was passiert danach? Welche LED wird denn dann
ein/aus geschaltet? Wieviel Zeit (Systemtakte) ist denn da ungefähr
vergangen? Ist das viel Zeit oder ist das wenig Zeit? Kann ich als
Mensch so kurze Zeitspannen überhaupt sehen?
Hi
Vielleicht noch ein kleiner Tip von mir....
Also, du hast 3 LED Rot, Grün und Gelb. Nun nimmst du eine Variable und
spendierst der diese 3 Bits.
1
Ampel_LED: .Byte 1 ; Bit 0 = Rot
2
; Bit 1 = Gelb
3
; Bit 2 = Grün
4
; Bit 3-7 nicht benutzt
Nun überlegst du, wie viele Ampelphasen es gibt:
1=nur Grün
2=nur Gelb
3=nur Rot
4=Rot und gelb
danach wieder von vorn. Dies kann man nutzen, indem man dieses Muster in
ein Variablenfeld packt.
1
Phase1: .Byte 1 ; nur Bit 2 setzen, alle anderen auf 0
2
Phase2: .Byte 1 ; nur Bit 1 setzen, alle anderen auf 0
3
Phase3: .Byte 1 ; nur Bit 0 setzen, alle anderen auf 0
4
Phase4: .Byte 1 ; nur Bit 0 und 1 setzen, alle anderen auf 0
Diese Variablen initialisierst du vor deiner Programmschleife mit den
Bits der LED
1
Init_Ampel_LED:
2
LDI Reg_a, 0b00000100 ; Grün
3
STS Phase1, Reg_a
4
LDI Reg_a, 0b00000010 ; Gelb
5
STS Phase2, Reg_a
6
LDI Reg_a, 0b00000001 ; Rot
7
STS Phase3, Reg_a
8
LDI Reg_a, 0b00000011 ; Gelb und Rot
9
STS Phase4, Reg_a
10
CLR Reg_a
11
STS Phase_Cnt, Reg_a ; Phasenzähler auf 0 setzen
12
RCALL Set_Ampel_Status (0) ; Ampel in Grundstellung anzeigen
13
RET
Im Programm rufst du nun deine Ampel auf und zählst einfach eine
Variable von 0 bis 3 in deinen Zeiten. Nennen wir sie mal Phase_Cnt.
Also mal ganz grob:
Progamm:
Stack setzen
LED-Feld Initialisieren (RCall INIT_Ampel_LED)
Programm Schleife
Eingänge lesen (RCall Read_IO)
Bei Ereignis die Ereignisroutine aufrufen (RCall Ampel_Zyklus)
Schleife Ende
[avrasm]
Ampel_Zyklus:
Delay...
LDS Reg_a, Phase_Cnt
INC Reg_a ; nächste Ampelphase
STS Phase_Cnt, Reg_a
RCALL Set_Ampel_Status (1)
Delay...
LDS Reg_a, Phase_Cnt
INC Reg_a ; nächste Ampelphase
STS Phase_Cnt, Reg_a
RCALL Set_Ampel_Status (2)
Delay...
LDS Reg_a, Phase_Cnt
INC Reg_a ; nächste Ampelphase
STS Phase_Cnt, Reg_a
RCALL Set_Ampel_Status (3)
Delay...
CLR Reg_a
RCALL Set_Ampel_Status (0)
RET
Set_Ampel_Status:
LDI XL,Low(Phase1) ; 1. Adresse vom Variablenfeld holen
LDI XH,High(Phase1)
LDS Reg_a, Phase_Cnt
ADD XL, Reg_a
CLR Reg_a
ADC XH, Reg_a ; addressieren
LD Reg_a, X ; adressiertes Byte holen
RCall Set_Ausgabe ; Aufruf einer Routine für die Ausgabe
; das Bitmuster steht in den Bits 0-2 von Reg_a
RET
[/avsasm]
Ich habe dir mal so in etwa die Routinen aufgeschrieben. Ob du in deinem
µC die Adressierung über das X-Register durchführen kannst, weiß ich
nicht, aber ein Atmega 8 arbeitet damit. In der Routine Set_Ausgabe
holst du dir die Bits und setzt die entsprechenden Portbits. Das ist nun
nicht mehr so schwer. Der Vorteil dieser Vorgehensweise liegt auf der
Hand. Die Routinen sind klein und leicht nachvollziehbar. Außerdem
kannst du nun ohne große Anstrengung deine Ampelsteuerung erweitern.
Überleg einmal, wie viele Schritte eine Kreuzung bräuchte. Bei der
Variablen Ampel_LED setzt du einfach weitere Bits für die andere
Fahrtrichtung, erweiterst das Variablenfeld Phase und initialisierst
entsprechend die erweiterten Bits zu den Phasen. Nun endet Phase_Cnt
nicht mehr bei 4, sondern halt bei 7 oder so. Am Programm brauchst du
fast nichts mehr ändern. Gerade bei der Anwendung von Assembler ist es
wichtig, ein Programm in kleine Aufgaben zu packen. Auch solltest du dir
angewöhnen, von Anfang an auf Delays in Form von Schleifen zu
verzichten. Lerne mit dem Timer umzugehen und leite Zeitereignisse aus
dem Timer –Interrupt ab. Im Moment sind die Delays noch nicht kritisch,
aber wenn du erst mal eine Taste 5 Sek. Festhalten musst, um sicher zu
sein, das dein Programm auch in dieser Zeit den Eingang gelesen hat,
wirst du dich fragen, warum. Und es ist nun mal so, das ein Programm in
einer Schleife zur Zeitenbildung nichts anderes machen kann, es sei
denn, du arbeitest mit Interrupts auf den Eingängen. Aber auch das ist
nict unbedingt das gelbe vom Ei…
Nimm meinen Beitrag nicht als Kritik, sondern als einen weiteren Weg,
Programme zu gestalten. Die Kunst eines Programmierers liegt nun mal im
Erkennen der Aufgabe und der Lösung, die manchmal gar nicht so
offensichtlich ist. Es scheint auch etwas aufgebläht, durch die
Verwendung von Variablen, aber sehr schnell lernt man diese Eigenschaft
eines Controllers zu schätzen. Angenommen, du möchtest eine
7-Segmentanzeige mit den Zahlen 0 bis 9 ansteuern, nimm ein
Variablenfeld Segment_a usw.
Nun ist deine Zahl in irgendeinem Register, also X-Register auf die
Adresse der ersten Variable setzen, deine Zahl dazuaddiert und die
dadurch adressierte Variable laden. Da sollte dann das passende
Binärmuster für die 7-Segmentanzeige für die Ausgabe drin stehen. Einmal
getestet und es funktioniert immer. Solche Bausteine kannst du dir dann
in irgendeiner Datei ablegen und bei Bedarf wieder für weitere Projekte
holen.
Gruß oldmax