ASM Timer COUNT verschiedene STEPS

OP #3679068
Lesenswert?

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?
Moderator (Firma: Titel) Persönliche Seite #3679075
Lesenswert?

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?
Gast #3679087
Lesenswert?

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.
#3679094
Lesenswert?

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!
#3679299
Lesenswert?

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.
OP #3679304
Lesenswert?

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...
#3679306
Lesenswert?

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.
#3679310
Lesenswert?

> 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?
#3679321
Lesenswert?

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.
Gast #3679408
Lesenswert?

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
#3679410
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren