Forum: Mikrocontroller und Digitale Elektronik ASM Timer COUNT verschiedene STEPS


von Martin F. (martin_f70)


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?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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?

von Martin F. (martin_f70)


Lesenswert?

Mit ISR klappt alles, wenn ich statt inc r19 zb. eine LED_X Funktion 
aufrufe klappt das.

von c-hater (Gast)


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.

von Karl H. (kbuchegg)


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!

von Martin F. (martin_f70)


Lesenswert?

Ich bin auf Linux, da gibt es weder Simulator noch definierte Adressen.

WIe kann ich denn nun mein Problem lösen, ich bin Anfänger.

von Peter D. (peda)


Lesenswert?

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"

von Martin F. (martin_f70)


Lesenswert?

Nein! Sie Blinken ja normal, aber halt mit dieem erst die dann die nicht

von Peter D. (peda)


Lesenswert?

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.

von Martin F. (martin_f70)


Lesenswert?

.h ??? ich bin in assembler

von Peter D. (peda)


Lesenswert?

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.

von Martin F. (martin_f70)


Lesenswert?

Ja aber bei meinem nicht... man das funktioniert alles nur das zählen im 
loop net...

von Karl H. (kbuchegg)


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.

von Martin F. (martin_f70)


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...

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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?

von Martin F. (martin_f70)


Lesenswert?

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!

von Karl H. (kbuchegg)


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.

von Martin F. (martin_f70)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Martin F. (martin_f70)


Lesenswert?

Ja :D ALso welchen Simulator gibt es für Linux ohne Wine?

von Karl H. (kbuchegg)


Lesenswert?

Kann ich dir nicht sagen. Ich abreite auf WIndows, benutze das 
AVR-Studio und hab damit keine Probleme.

http://www.mikrocontroller.net/articles/AVR_und_Linux
http://www.mikrocontroller.net/articles/AVR-Simulation

von oldmax (Gast)


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

von Peter D. (peda)


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.

von Peter D. (peda)


Lesenswert?

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.
1
  .def savesreg = r2
2
;...
3
  in savesreg, sreg
4
;...
5
  out sreg, savesreg
6
  reti

von Thomas K. (thomas_k39)


Lesenswert?

Ein AVR-Simulator für Linux?

Bitteschön: 
https://www.google.com/search?btnG=1&pws=0&q=linux+avr+simulator&gws_rd=ssl

Schon der erste Treffer ist sehr hilfreich :)

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.