Hallo liebe TEchnikgemeinde.
ICh wollte in Assembler auf meinem ATmega8 einen COUNTER mittels Timer
bauen. Ich habe es geschafft, dass der Timer immer nur im 1MHz Modus
einen weiterlässt:
1
timer0_overflow:
2
push r18
3
lds r18, __sram_counter__
4
subi r18, 0xf0
5
sts __sram_counter__, r18
6
brlo timer0_fin
7
;HIER PASSIERT WAS
8
timer0_fin:
9
pop r18
10
reti
Ich möchte ja aber dass er verschiedene Funktionen bei bestimmter ANzahl
der "Timer-DUrchlässe" ausführt.
Das habe ich wie folgt gelöst:
1
led_1:
2
ser r16
3
in r17, 0x18
4
eor r17, r16
5
out 0x18, r17
6
ret ; PORTB EINSCHALTEN
7
8
led_2:
9
ser r16
10
in r17, 0x12
11
eor r17, r16
12
out 0x12, r17
13
ret ; PORTD EINSCHALTEN
14
15
timer0_overflow:
16
push r18
17
lds r18, __sram_counter__
18
subi r18, 0xf0
19
sts __sram_counter__, r18
20
brlo timer0_fin
21
inc r19
22
timer0_fin:
23
pop r18
24
reti
25
26
loop:
27
cpi r19, 0x01
28
brne loop
29
rcall led_2
30
cpi r19, 0x02
31
brne loop
32
rcall led_1
33
rjmp loop
Nur leider gehen nicht nach 1*1MHz die LED_2 Reihe n und erstrecht nicht
dann nach einem weiteren MHz LED_1 Reihe. Wieso?
Martin Fischer schrieb:> Ich habe es geschafft, dass der Timer immer nur im 1MHz Modus einen> weiterlässt
Ähm, wie bitte?
> erstrecht nicht dann nach einem weiteren MHz LED_1 Reihe. Wieso?
Was sagt der Simulator?
> Wieso?
Kommst du überhaupt in den Interrupt?
Hast du ein Oszi, dann kannst du auf der Hardware in der ISR einen Pin
togglen und messen, ob bis dahin alles passt...
BTW: ist dir klar, dass in der ISR auch das Statusregister gesichert
werden muss, weil es durch die Rechenoperation verändert wird?
Martin Fischer schrieb:> timer0_overflow:> push r18> lds r18, __sram_counter__> subi r18, 0xf0> sts _sram_counter_, r18> brlo timer0_fin> ;HIER PASSIERT WAS> timer0_fin:> pop r18> reti
Das ist Mist. Du rettest die Flags nicht. Das kann unter bestimmten
Umständen OK sein, hier aber nicht, denn:
> loop:> cpi r19, 0x01> brne loop> rcall led_2> cpi r19, 0x02> brne loop> rcall led_1> rjmp loop
Hier benutzt du Vergleiche und Verzweigungen, die nicht atomar sind.
Zwischen Vergleich und Verzweigung kann dir die Interruptroutine die
Flags ändern.
Und die Logik bei loop musst du dir auch noch mal ansehen. Am Besten im
Simulator durchspielen!
Solange r19 nicht 0x01 ist geht es immer wieder zurueck zu loop. Zur
Prüfong ob r19 vielleicht 0x02 wäre kommt es erst gar nicht. Das
passiert erst wenn der erste Rücksprung zu loop nicht gemacht wird, weil
r19 den Wert 0x01 hat. Nur ist dann r19 ziemlich sicher nicht 0x02.
Wie gesagt: spiel es im Simulator durch! Und bring deine Toolchain in
Ordnung! Es ist unsinnig bei den in und out die Portadressen direkt als
Zahl hinzuschreiben, wenn es dafuer vordefinierte Namen gibt!
Meine Augen sind leider träger als Deine, ich kann LEDs nicht mehr im
1MHz-Takt blinken sehen.
Für Blink-LEDs mache ich mir einen 10ms oder 100ms Takt und der taktet
dann einen Scheduler, eine Statemaschine oder gibt ein Array aus dem
Flash aus.
Z.B.:
Beitrag "AVR Sleep Mode / Knight Rider"
Martin Fischer schrieb:> Ich bin auf Linux, da gibt es weder Simulator noch definierte Adressen.
Dann nenn dochmal diese äußerst merkwürdige Toolchain, die keine
standard *.h Files enthält.
Martin Fischer schrieb:> .h ??? ich bin in assembler
Ja und.
Auch in Assembler gibt es und braucht man Includes. Schau mal in das
Manual Deines Assemblers.
Martin Fischer schrieb:> Ja aber bei meinem nicht... man das funktioniert alles nur das zählen im> loop net...
Dann installier dir endlich mal was Ordentliches!
Das ist doch sinnlos! Du kommst mit 4 Anweisungen nicht klar (denn das
du die ISR selbst geschrieben hast, glaub ich dir nicht) und wirfst dir
absichtlich Prügel zwischen die Beine? Mann, du musst noch viel lernen!
Du willst jede Hilfe haben, die du kriegen kannst. 'Ich hab keinen
Simulator weil ich auf Linux bin' ist doch kein Argument!
Programmieren ist schon schwierig genug. Da braucht man nicht auch noch
zusätzliche Schwierigkeiten, die absolut vermeidbar sind.
Man! Es klappt alles, die ISR, die LED Funktionen. Wenn ich in der ISR,
an der Stelle, wo inc r19 steht, die LED Funktion Aufrufe dann blinkt
dieser Port auch. Meine Frage ist nur wie ich das im LOOP mit dem zählen
machen kann dass beim ersten Timerdurchlauf Port B eingeschalten wird
und dann beim 2. Durchlauf Port D.
Alles andere funktioniert, also hört auf schonwieder irgendwelchen
Blödsinn rumzuheulen ... das ist so nervig...
Martin Fischer schrieb:> Man! Es klappt alles, die ISR, die LED Funktionen. Wenn ich in der ISR,> an der Stelle, wo inc r19 steht, die LED Funktion Aufrufe dann blinkt> dieser Port auch. Meine Frage ist nur wie ich das im LOOP mit dem zählen> machen kann dass beim ersten Timerdurchlauf Port B eingeschalten wird> und dann beim 2. Durchlauf Port D.>> Alles andere funktioniert, also hört auf schonwieder irgendwelchen> Blödsinn rumzuheulen ... das ist so nervig...
Ich hab dir in der Früh schon gesagt, wo das Logik Problem in deiner
loop liegt. Also hör auf hier rumzuheulen.
> mman das funktioniert alles nur das zählen im> loop net...
Das funktioniert eben nicht alles.
Das SREG in der ISR nicht zu sichern ist ein schwerer Fehler!
Und das zählen in der Loop
1
loop:
2
cpi r19, 0x01
3
brne loop
4
rcall led_2
5
cpi r19, 0x02
6
brne loop
7
rcall led_1
8
rjmp loop
wie soll dennn r19 jemals den Vergleich mit 0x02 schaffen? Wenn r19 den
Wert 0x02 hat, dann kommt es nie zu dieser Abfrage, weil hier
1
loop:
2
cpi r19, 0x01
3
brne loop
es sofort wieder zurück zu loop geht.
Sieh zu, dass du einen Simulator am laufen hast. Das sind 7 Anweisungen,
die du nicht im Kopf überblicken kannst, von denen du aber im Simulator
SOFORT gesehen hättest, wo hier das Problem liegt. Und du lehnst jetzt
ernsthat ab, dir eine ordentliche Toolchain zu installieren oder dir die
Voraussetzungen dafür zu schaffen? Das ist doch lächerlich. Wie stellst
du dir denn vor, dass du Programme entwickeln willst, die das Micky
Mouse Stadium verlassen?
Martin Fischer schrieb:> Ja. Und ich sagte ich bin Anfänger ich weiß nicht wie ich das lösen soll> da ich auch nicht alle Assemblerbefehle aus dem FF kenne!
Das hat nichts mit 'Assembelrbefehlen' zu tun, sondern mit dem Teil in
der Programmierung, an dem du noch monatelang kniefeln wirst und er das
Hauptproblem in der Programmierung darstellt: die Programmlogik.
Die Zugmöglichkeiten von SChachfiguren lernt man an einem Nachmittag.
Aber man verbringt sein halbes Leben damit, Schachstrategie zu lernen.
Mit genau den Zügen, die man am ersten Nachmittag gelernt hat.
Genauso auch hier: die Befehle sind Pipifax. Es ist die Kombination und
was diese Kombinationen bewirken, eben die Logik die durch die
Kombination der Befehle entsteht, die das eigentliche Problem in der
Programmierung dartstellen.
1
loop:
2
cpi r19, 0x01
3
brne loop2
4
rcall led_2
5
loop2:
6
cpi r19, 0x02
7
brne loop3
8
rcall led_1
9
loop3:
10
rjmp loop
wenn r19 nicht den Wert 0x01 hat, dann darf der Sprung eben nicht wieder
zurück nach loop gehen, sondern er muss zu der Stelle führen, an der
geprüft wird, ob r19 den Wert 0x02 hat.
Wenn Led1 und Led2 mit unterschiedlichen Frequenzen blinken sollen, dann
wirst du eben nicht damit auskommen, alles mit lediglich r19 abzudecken,
sondern dann musst du eben 2 Register haben, die unterschiedlich schnell
hochzählen.
Programmlogik!
Und um die zu kontrollieren, gibt es nichts besseres als den µC selber.
Nur hat der einen Nachteil: er ist sauschnell, so dass du nicht
mitkommst zu verfolgen, was da eigentlich passiert. Genau an dieser
Stelle kommt der SImulator ins Spiel. Der arbeitet genau gleich wie der
eigentliche µC, allerdings mit einer Geschwindigkeit, die du
kontrollieren kannst und die dir Zeit gibt, genau und exakt zu
verfolgen, was da eigentlich wann und warum passiert, so dass du
nachvollziehen kannst, an welcher Stelle du einen Denkfehler in deiner
Programmlogik hast.
DAnke. Unterschiedlich schnell sollen die erstmal nicht, halt nur zu ner
anderen Zeit. Werde deine Lösung gleich testen. Welchen Simulator gibt
es denn für Linux ohne Wine nutzen zu müssen?
EDIT!
Loop 3 ist unnötig, da kann ich gleich brne zu loop
Martin Fischer schrieb:> EDIT!> Loop 3 ist unnötig, da kann ich gleich brne zu loop
Ja - jetzt.
Genauso wie auch loop2 einst einmal unnötig war und du gleich zu loop
zurückspringen konntest. Nur leider hast du genau diesen Rücksprung dann
bei der Programmerweiterung übersehen bzw. nicht bedacht.
Hi
Beginn erst mal mit dem Hinweis, das Statusregister zu sichern.
1
Push R16
2
In R16, SREG
3
Push R16
4
5
....
6
7
POP r16
8
Out SREG, r16
9
Pop R16
10
RETI
Da SREG nicht direkt auf den Stack geschrieben werden kann, musst du den
Umweg über ein Universalregister gehen.
Dann sorg dafür, das der Interrupt in jeder mSek. ausgelöst wird. Dazu
hast du die Möglichkeit, den Vorteiler entsprechend zu setzen und den
Interrupt im CTC Mode laufen zu lassen. Näheres steht hier im Tutorial.
Nun brauchst du nut noch Erbsen zählen. Also, zähl ein register immer
bis 9. Bei Zehn setzt du zurück und zählst die nächste Dekade. Dafür
eignen sich die Register r0 bis r14.Such dir ein geeignetes aus. Diese
darfst du natürlich nicht auf den Stack schreiben. Ach ja, eine Variable
richtest du dir ein. Ich nenn sowas immer Time_Flag. In deiner ISR setzt
du bei bestimmten Zzählerwerten einfach ein Bit. z. B. beim Übergang von
9 mSek auf die nächste Dekade und schon hast du ein Zeitereignnis von 10
mSek. oder nach 9 hundertstel ein 2. Bit. Wär dann eine zehntel Sekunde.
[AVR ASM]
Fime_Flag: .Byte 1
; Bit 0 = 10 mSek.
; Bit 1 = 100 mSek.
; Bit 3 = 1 Sek
; usw.
[/]
Auch krumme Werte lassen sich so in einem Bit ablegen. Nun rufst du in
deiner Programmschleife einfach die Zeiterreignisse ab.
1
MAIN_Loop:
2
......
3
RCALL Chk_10_mSek
4
RCALL Chk_100_mSek
5
RCALL Chk_Sek ; etc
6
.....
7
RJMP Main_Loop
8
9
Chk_10_mSek:
10
LDS r16, Time_Flag
11
ANDI r16, 0b00000001 ; Zeitbit gesetzt ?
12
BREQ End_Chk_10mSek ; wenn nicht, dann ende
13
LDS r16, Time_Flag
14
ANDI r16, 0b11111110 ; Zeitbit löschen
15
STS Time_Flag, r16
16
;***********************************
17
;* alles, was in diesem Zeitraster *
18
;* bearbeitet werden soll, wird *
19
;* programmiert. *
20
;***********************************
21
End_Chk_10mSek:
22
RET
23
24
Chk_100_mSek:
25
........
26
RET
27
28
Chk_1_Sek:
29
.....
30
RET
So hast du einen überschaubaren Code. In der ISR werden nur die Bits
gesetzt und die Bearbeitung wird ohne Push und POP Zwang in der normalen
Programmschleife ausgeführt. Eine weitere Zeit einsetzen? Kein Problem.
Und kene Angst, dein Code überschreitet die Zeit, die du zwischen den
Interrupts verfügbar hast. Bei einer Taktfrequenz von 1 MHz hast du
eine Taktzeit von 1 µSek. Bei durchschnittlich 2 Taktzyklen entspricht
das rd. 5000 Befehle in 10 mSek. Denk mal drüber nach. Und du kannst
dann immer noch das 16 Fache mit einem externen Quarz erreichen.
Viel Spaß bei deiner weiteren Arbeit und Assembler.
Gruß oldmax
Martin Fischer schrieb:> Ja :D ALso welchen Simulator gibt es für Linux ohne Wine?
Als Linuxer sollte man eigentlich wissen, daß man sich deutlich mehr
anstrengen muß, als der faule und bequeme Windowser (mehr googlen, mehr
konfigurieren, mehr Manuals lesen, mehr Fehlermeldungen verstehen usw.).
Wenn Du nichtmal weißt, wie Du die bestimmt vorhandenen Includes findest
und in den Suchpfad einträgst, solltest Du besser auf Windows wechseln.
oldmax schrieb:> Push R16> In R16, SREG> Push R16
Wobei man in Assembler auf die Doppelsicherung verzichten kann, der AVR
hat ja nur einen Interruptlevel.
Man reserviert sich z.B. R2 zur SREG-Sicherung.