Assembler für Assembler - Anfänger (bin selber noch einer)

Das mit dem Reload hatte ich schon die ganze Zeit angemerkt... das gehört mit in die ISR. Direkt hinter das Decrement. Wenn der Zähler auf null dekrementiert wird, wird dabei automatisch das Z-Flag gesetzt. Abhängig von diesem kannst Du dann den Reloadwert in das Zählerregister laden, klar?
(Wenn man sowas ähnliches beim Überlauf benötigt, kann man dec nicht zum dekrementieren verwenden, da dieses das C-Flag nicht beeinflußt. Aber man kann stattdessen eins subtrahieren (SUBI Register, 1))

Zur "Zeitkonstante": In Deinem Programm lädst Du desöfteren vielfache von 23 in Rechenregister (LDI), bzw vergleichst mit diesen Konstanten (CPI). Dabei wird der Wert der verwendeten Konstante direkt mit in den Opcode verschlüsselt, soweit klar?
Wenn dieses Timing zu verändern sein sollte, mußt Du also überall im Quellcode die 23 durch den neuen Wert ersetzen, und die vielfachen davon dementsprechend auch. Du kannst aber für den Compiler eine Konstante mit dem Wert 23 festlegen (mit .equ), und im weiteren Code dann diese Konstante statt der direkten Werte verwenden. Und für vielfache davon verwendest Du eben (Faktor*Konstantenname). Der Compiler erzeugt daraus genau denselben Maschinencode - die Konstante existiert ja nur für den Compiler als Name, beim Kompilieren wird die Zahl draus.
Der Vorteil ist jetzt aber (neben der bessere Übersicht des Quellcodes, wenn man einen sinnigen Namen nimmt), daß man das ganze Timing an einer einzigen Stelle (im Quellcode) anpassen kann: direkt in der Zeile mit dem .equ .
 
mh...so ein Sch.
Mir raucht der Kopf und komme nicht weiter. Hier nochmal den geänderten Code. Folgendes spielt sich nach Reset ab:
Die LED an PB2 erlischt nur kurz und dann leuchten alle 3 an PB0 / PB1 / PB2 dauerhaft. Wo ist nur mein Denkfehler!

Ich weiß nicht, vermute das "cpi" nicht der richtige Befehl ist. Denn es heißt, daß C (im SREG) nur dann 1 ist, wenn
der Inhalt vom Zielregister (hier temp1) mit der 8Bit-Konstanten identisch ist.

Code:
           ldi     temp1,115     ;lade r17 mit 115 (dez.)
           clr     akku
           out     PORTB,akku     
          
loop:      cpi     temp1,92      ;wenn temp1 größer 92..C=0
           brcs    gehe01        ;dann keine Verzweigung "gehe01"
           
           rjmp    Ende   
           
gehe01:    cpi     temp1,69      ;wenn temp1 größer 69..C=0
           brcs    gehe02        ;dann keine Verzweigung gehe02
           cbi     PORTB,PB2     ;PB1-PB0 = 1
           rjmp    Ende   

gehe02:    cpi     temp1,46      ;wenn temp1 größer 46..C=0
           brcs    gehe03        ;dann keine Verzweigung gehe03
           cbi     PORTB,PB1     ;PB0 = 1
           rjmp    Ende         

gehe03:    cpi     temp1,23      ;wenn temp1 größer 23..C=0
           brcs    Ende         ;dann keine Verzweigung Ende
           cbi     PORTB,PB0     ;PB2-PB0 = 0
                   
Ende:      rjmp    loop
                   

;Interrupt-ISR

TIMER0_OVF:push    akku      ;Kopie r16 auf den Stack, danach SP-1
           in      akku,SREG ;Inhalt vom Statusregister in r16 laden
           
           dec     temp1     ;(r17-1) wenn r17=0 >>Z=1
           breq    reinit    ;Verzweigung wenn Z=1
reinit:    ldi     temp1,115          
           ldi     akku,0x07
           out     PORTB,akku ;PB0-PB2=1
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti
          .EXIT
 
Zu CPI und dem Carry: CPI "rechnet" Ergebnis=Registerinhalt-Konstante (Immediate). Es ist dasselbe, wie SUBI, nur daß das Ergebnis nicht ins Register geschrieben wird. Eine Subtraktion. Das Carry ist das Überlaufbit, es wird nur gesetzt, wenn bei der Subtraktion ein Überlauf (oder "Unterlauf") erfolgte. Wann ist das der Fall? wenn die Konstante größer war, als der Registerinhalt.
Instruction Set zu CPI schrieb:
C:Rd7 •K7 +K7 •R7+ R7 •Rd7
Set if the absolute value of K is larger than the absolute value of Rd; cleared otherwise
Zum Denkfehler:
Schau Dir mal nochmal genau Deine Timer-Überlauf-ISR an. Wenn Temp1 nach dem dekrement null ist (also wenn das Z-Flag dabei gesetzt wurde), springst Du zum Label reinit, wo Du das reinit erledigst. Scheint ja auch zu klappen.
Aber was geschieht eigentlich, wenn Temp1 nach dem Decrement nicht null war, also Z=0?;)
 
Eine Subtraktion. Das Carry ist das Überlaufbit, es wird nur gesetzt, wenn bei der Subtraktion ein Überlauf (oder "Unterlauf") erfolgte. Wann ist das der Fall? wenn die Konstante größer war, als der Registerinhalt.

Zum Denkfehler:
Schau Dir mal nochmal genau Deine Timer-Überlauf-ISR an. Wenn Temp1 nach dem dekrement null ist (also wenn das Z-Flag dabei gesetzt wurde), springst Du zum Label reinit, wo Du das reinit erledigst. Scheint ja auch zu klappen.
Aber was geschieht eigentlich, wenn Temp1 nach dem Decrement nicht null war, also Z=0?;)

dann stimmt das doch garnicht, wenn ich im Code vorgebe:
loop: cpi temp1,92 ;wenn temp1 größer 92...C=0 (ich lade zu Beginn temp1 mit 115)
D.h. der Registerinh. ist größer als die Konstante....dann müßte C=1 sein und nicht 0
Hier muß ich erst mal Klarheit haben, bevor ich mich mit der ISR beschäftige

Danke erst mal für Deine Hilfe.
 
...
D.h. der Registerinh. ist größer als die Konstante....dann müßte C=1 sein und nicht 0
Hier muß ich erst mal Klarheit haben, bevor ich mich mit der ISR beschäftige
...
Ja, genau so ist es, da oben stimmts ja auch. Der Fehler liegt ja auch in der ISR...
Bei der Initialisierung schaltest Du die 3 LEDs explitzit nochmal in der 3ten geposteten Zeile ab (sollte aber da noch das initial value von PORTB sein).
Die 3 LEDs schaltest Du hier an:
...
Code:
...
;Interrupt-ISR

TIMER0_OVF:push    akku      ;Kopie r16 auf den Stack, danach SP-1
           in      akku,SREG ;Inhalt vom Statusregister in r16 laden
           
           dec     temp1     ;(r17-1) wenn r17=0 >>Z=1
           breq    reinit    ;Verzweigung wenn Z=1
reinit:    ldi     temp1,115          
           [COLOR="#FF0000"]ldi     akku,0x07
           out     PORTB,akku [/COLOR];PB0-PB2=1
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti
          .EXIT
Das willst Du dort machen, wenn bei dem Vergleich das Z gesetzt wurde (also temp1=0). Nochmal: Was geschieht dort, wenn Temp1 ungleich 0?
(Damit solltest Du drauf kommen - ansonsten sag, wenn ich's auflösen soll...)
 
Kommando zurück! Das stimmt schon, wenn ich schreibe "wenn temp1 größer 92..C=0"
habe die Sache mal über den Debugger laufen lassen und eindeutig war an 3 Beispielen zu sehen:

1. ldi temp1,93
cpi temp1,92 ;C bleibt 0 (Registerinh. größer als Konstante)
brcs gehe01 ;keine Verzweigung

2. ldi temp1,92
cpi temp1,92 ;C bleibt 0 (Registerinh. = Konstante)
brcs gehe01 ;keine Verzweigung

1. ldi temp1,91
cpi temp1,92 ;C wird gesetzt (Registerinh. kleiner als Konstante)
brcs gehe01 ; Verzweigung
 
Bei der Initialisierung schaltest Du die 3 LEDs explitzit nochmal in der 3ten geposteten Zeile ab (sollte aber da noch das initial value von PORTB sein).
Die 3 LEDs schaltest Du hier an:

Das willst Du dort machen, wenn bei dem Vergleich das Z gesetzt wurde (also temp1=0). Nochmal: Was geschieht dort, wenn Temp1 ungleich 0?
(Damit solltest Du drauf kommen - ansonsten sag, wenn ich's auflösen soll...)

habe den Code ausgedruckt vor mir liegen und versuche die erste Satzreihe zu begreifen.
was mach ich in der Intialisierungsphase?
reset: ldi akku,(1<<PB2)|(1<<PB1)|(1<<PB0)
out DDRB,akku
mehr mache ich nicht!
Code:
TIMER0_OVF: push   akku
            in     akku,SREG
           
           dec     temp1     ;(r17-1) wenn r17=0 >>Z=1
           breq    reinit    ;Verzweigung wenn Z=1
reinit:    ldi     temp1,115          
           ldi     akku,0x07
           out     PORTB,akku ;PB0-PB2=1
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti
ich habe schon versucht das reinit: nach dem out zu setzen..hat nichts gebracht.
Gieb Dir einmal nen Ruck und hilf mir
 
Hallo LotadaC,
nicht das Du denkst...ich hab das Problem erst mal am Nagel gehangen!
Schon heute Morgen 7 Uhr habe ich Studio4 angeschmissen und erkenne durch Debugger, daß ich in der ISR
den größten Sch. baue.
Ist schon nicht gerade gut, daß ich zu Beginn "clr akku" in der Hauptroutiene vorgebe, denn dadurch wurde
Z schon gesetzt. Aber das ist nicht der Grund, warum alle 3 LEDs leuchten. Wie Du schon schreibst liegt der Grund
in der ISR.

Code:
TIMER0_OVF: push   akku
            in     akku,SREG
            dec     temp1       ;(r17-1) wenn r17=0>>Z=1
            breq    reinit      ;Verzweigung, wenn Z=1
            ldi     akku,0x07
            out     PORTB,akku
reinit:     ldi     temp1,115
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti

reinit: wird ja jedesmal ausgeführt das hat mir der Debugger gezeigt. Vielleicht kannst Du mir nen Tip geben,
wie ich das realisieren könnte....arbeite aber weiter selbst daran.
 
CLR manipuliert zwar das Z-Flag (und auch einige andere Flags im SREG), aber auf Deine Verzweigungen (branches) hat das keinen direkten Einfluß, da vorher jeweils ein CPI durchgeführt wird, welches seinerseits das SREG (Z) manipuliert.
Schau mal genau, was Du da tust:
Du vergleichst, und springst ein paar Zeilen weiter. Wenn die Bedingung falsch ist, springst du nicht dorthin, sonder machst eventuell nochwas anderes (letzte Version), kommst aber trotzdem irgendwann in den Zeilen an, wo das reinit stattfindet. Deswegen wird der Zähler jedesmal mit 115 beladen, deswegen also bei jedem Durchlauf der Hauptschleife nie der LED-Status geändert. Du mußt im nicht-Zweig Deiner Abfrage das reinit überspringen
Nochmal sortieren:
In der ISR soll der Zähler dekrementiert werde. Wurde er dabei 0, soller irgendwie zurückgesetzt werden (und auch gleich noch der LED-Status umgesetzt). ansonsten braucht nix weiter geschehen (Register retten/wiederherstellen mal außen vor).
Das könntest Du also so umsetzen:
Code:
...
01-dekrementiere Zähler
02-wenn Zähler dabei 0 geworden, springe reinit
03-sonst mache irgendwas
04-springe hinterReinit
05-reinit:
06-reinit-code
07-Ledstatus ändern
08-hinterinit:
09-ISR beenden
...
damit hast Du 2 getrennte Zweige, die nur hintereinander geschrieben sind. Einer von 03 bis 04, einer von 05 bis 07. Bei 02 trennen sich die Wege, bei 08 (strenggenommen ist da gar keine Instruktion, eigentlich 09) laufen sie wieder zusammen, klar?

Wenn man jetzt mal genauer drüber nachdenkt, wird aber in 03 gar nichts gemacht - der Zweig ist leer. also dreht man die Sprungbedingung um, und läßt unnötiges weg:
Code:
...
01-dekrementiere Zähler
02-wenn Zähler dabei nicht 0 geworden, springe hinterReinit
06-reinit-code
07-Ledstatus ändern
08-hinterinit:
09-ISR beenden
...
 
Hi
Vielleicht solltest du hier mal ein Auge drauf werfen:
Code:
TIMER0_OVF: push   akku
            [COLOR="#FF0000"]in     akku,SREG[/COLOR]            
dec     temp1       ;(r17-1) wenn r17=0>>Z=1
            breq    reinit      ;Verzweigung, wenn Z=1
            [COLOR="#FF0000"]ldi     akku,0x07
            out     PORTB,akku[/COLOR]
reinit:     ldi     temp1,115
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti
Irre ich mich, oder überschreibst du das SReg mit Absicht ?
Gruß oldmax
 
hi, ich kämpfe und bin "NAHE" dran, aber es giebt noch nen Schönheitsfehler!

Code:
TIMER0_OVF: push   akku
            in     akku,SREG
            dec    temp1       ;(r17-1) wenn r17=0>>Z=1
            brne   hinterinit  ;Verzweigung, wenn Z=0
            ldi    temp1,115
            ldi    akku,0x07
            out    PORTB,akku
hinterinit:
           
           out     SREG,akku ;Inhalt von r16 ins SREG laden
           pop     akku      ;SP+1, danach vom Stack in r16 laden
           reti
          .EXIT
 
also Leute, es läuft...aber noch mit einem Schönheitsfehler!
Zu Oldmax.. habe erst mal die PUSH und POP rausgeschmissen, weil mich das sehr unsicher machte.
Aber das war nicht die Ursache. Hier nochmal den geänderten Code:

Code:
          ldi     temp1,115     ;lade r17 mit 115 (dez.)
            
          
loop:      cpi     temp1,92      ;wenn temp1 kleiner 92..C=1
           brcs    gehe01        ;dann  Verzweigung "gehe01"
           ldi     akku,0x07
           out     PORTB,akku
           rjmp    Ende   
           
gehe01:    cpi     temp1,69      ;wenn temp1 kleiner 69..C=1
           brcs    gehe02        ;dann Verzweigung "gehe02"
           cbi     PORTB,PB2     ;PB1-PB0 = 1 (PB2=0)
           rjmp    Ende   

gehe02:    cpi     temp1,46      ;wenn temp1 kleiner 46..C=1
           brcs    gehe03        ;dann Verzweigung "gehe03"
           cbi     PORTB,PB1     ;PB0 = 1 (PB2 und PB1=0)
           rjmp    Ende         

gehe03:    cpi     temp1,23      ;wenn temp1 kleiner 23..C=1
           brcs    Ende          ;dann Verzweigung "Ende"
           cbi     PORTB,PB0     ;PB2-PB0 = 0
Ende:      rjmp    loop
                   
;Interrupt-ISR

TIMER0_OVF:
            dec    temp1       ;(r17-1) wenn r17=1>>Z=0
            brne   hinterinit  ;Verzweigung, wenn Z=0
            ldi    temp1,115  
            ldi    akku,0x07
            out    PORTB,akku
hinterinit:
           
            reti
          .EXIT

Von Beginn an erlöschen die 3 LEDS an PB2 bis PB0 im ca. 5 Sek. Rythmus....alles ok.
Das Umschalten (wenn alle LEDs=0) zum Leuchten dauert das doppelte.
Vom logischen Ablauf in "gehe03" muß es auch dazu kommen, denn ab Zählerstand 45 bis runter 23 wird
PB0 ständig auf LOW gesetzt. Bis aber der Zähler temp1 in der ISR zu Null kommt dauert es nochmal 23.
Hier muß ich mir noch was einfallen lassen.
Mein Dank nochmal an LotadaC ich hab mich in der ISR immer wieder fobben lassen.
 
Ich habe den Thread jetzt nicht komplett verfolgt,

aber in der Timer-ISR musst du in jedem Fall das Statusregister sichern! Zum Beispiel indem du es auf dem Stack ablegst.

Das selbe gilt für das Register "akku"!

Dirk :ciao:
 
Hi Rolf
habe erst mal die PUSH und POP rausgeschmissen, weil mich das sehr unsicher machte.
Das ist mit Sicherheit ein weiterer Weg ins Nirwana für dein SREG :rolleyes:
Push und POP dienen dazu, die Registerinhalte zu erhalten, da du nicht weißt, an welcher Stelle dein Interrupt ausgelöst wird. Stell dir vor, du liest die Zeitung. Da klingelt jemand an der Tür. Du kannst nicht sagen, das passiert immer, wenn ich eine neue Seite aufschlage oder einen neuen Artikel beginne, sondern das passiert an jeder beliebigen Stelle in der Zeitung. Willst du dort weitermachen, wo du aufgehört hast, mußt du dir die Zusammenhänge merken. Und genauso ist das mit den Push und POP.
Richtig, zuerst das Register AKKU ablegen, damit dieser Inhalt erhalten bleibt. Dann das SREG in den AKKU übertragen. Damit ist das SREG gesichert, aber nur so lange, wie du den AKKU nicht benutzt. Das tust du aber, du beschreibst ihn mit
Code:
LDI  AKKU, 0x07
. Das gibst du an den Port aus und danach kommt ein
Code:
Out SREG, Akku
Was glaubst du, steht jetzt im SREG ? Mit Sicherheit nicht die Ergebnisbits aus Anweisungen, die von diesen Bits leben....
Richtig wär gewesen, du hättest den Akku in Ruhe gelassen, oder ihn nach dem Befehl
Code:
In AKKU, SREG
nochmal mit Push abgelegt.
Nun hat deine ISR immer noch keine Sicherstellung der Statusflags, denn mit
Code:
Dec Temp1
zerstörst du den Inhalt von SREG. Das kann gut gehen, wenn der Interrupt an einer Stelle auftrat, wo das Statusregister unbedeutend war, aber irgendwann raufst du dir die Haare, weil du dir ein Verhalten deines Programmes absolut nicht erklären kannst....
Daher....
Code:
TIMER0_OVF: 
            push   akku               ; Akkuinhalt sichern
            in     akku,SREG         ; Statusbits in Akku kopieren
            [COLOR="#00FF00"]push   akku              ; Statusbits sichern[/COLOR]       
            dec     temp1       ;(r17-1) wenn r17=0>>Z=1
            breq    reinit        ;Verzweigung, wenn Z=1
            ldi     akku,0x07   ; kann getrost verwendet werden
            out     PORTB,akku
reinit:     ldi     temp1,115
           [COLOR="#00FF00"]POP    Akku         ; SREG muss jetzt natürlich wieder zurückgeholt werden[/COLOR]           out     SREG,akku ; und kann nun auch richtig ins SREG zurückgeschrieben werden
           pop     akku        ;jetzt noch den alten Inhalt vom Akku laden
           reti

Die Richtigkeit des restlichen Programmes hab ich nicht beachtet. Daherist es durchaus noch möglich, das da noch Fehler drin sind. Mir geht's hier erst einmal darum, das du erkennst, warum in den Interruptroutinen die Push und POP Befehle so wichtig sind. Sicher, du kannst für das SREG ein festes Register reservieren, aber egal, wie du dich drehst, es ist wichtig, alles was n einer ISR beeinflußt wird, zu sichern und anschließend wieder herzustellen. Immer dran denken, die ISR wird an JEDEM beliebigen Programmschritt zuschlagen. Du hast da keinen Einfluss drauf.
Also, Push und POP sind keine Zauberfloskeln. Den Stackpointer brauchst du da auch nicht besonders in der Doku erwähnen. Wichtig ist: zu jedem Push gehört auch ein POP.
Gruß oldmax
 
endlich...es läuft jetzt ohne den Minifehler!

Hier nochmal das Ende vom Code:

Code:
gehe03:    
           cbi     PORTB,PB0     ;PB2-PB0 = 0
Ende:      rjmp    loop
                   
;Interrupt-ISR

TIMER0_OVF:
            push   akku
            in     akku,SREG
            dec    temp1       
            cpi    temp1,23
            brcs   nachladen
            rjmp   weiter
nachladen:  ldi    temp1,115  
            
weiter:
            out    SREG,akku
            pop    akku   
            reti
           .EXIT

gehe03: habe ich verjüngt und die ISR abgeändert. Auffallend war, als ich noch kein "rjmp weiter" hatte, lief garnichts
mehr...alle LEDs leuchten.
Die verflixten brxx Pseudobefehle sind mir immer noch ein rotes Tuch.
Werde mir das von oldmax noch durchschnüffeln.
Durch sowas steigt man so richtig in die Tiefe ein.
 
Hallo Rolf
Die verflixten brxx Pseudobefehle sind mir immer noch ein rotes Tuch.
Na ja, es sind wenn du so willst, alles Pseudo-Befehle und darüber kannst du froh sein. Wenn du Maschinencode schreiben müßtest..... :yes4: dann wärest du :dirol:
Also, bleiben wir bei den Assemblerbefehlen, denn die BRXX-Befehle sind stinknormale Befehle, die auf Bits im Statusregisterr reagieren. So löst du den Sprung aus, wenn du bei der vorhergehenden Operation
Code:
Dec Temp1
das Carry-Bit setzt. Es sind halt bedingte Sprünge. Ist das Bit gesetzt, dann wird der BRCS ausgeführt, wenn nicht, dann mach mit dem nächsten Befehl weiter. Ich hab das Gefühl, du machst es dir selber schwer. Think simple. Assembler ist grad eine Sprache, die wirklich nicht einfacher aufgebaut sein kann.
Aber grad das Einfache ist für uns schwer:banghead:
Das beginnt schon bei der Mathematik. Wir rechnen mit Zahlen von 0 bis 9 und wissen, zahlen > 9 haben einen Übertrag in die nächst höhere Stelle. Das können wir perfekt. Sollen wir aber im Dualbereich rechnen tun wir uns gewaltig schwer, obwohl die Ziffern nur 0 und 1 sind. alles was >1 ist, bekommt einen Übertrag in die nächsthöhere Stelle. Die Ziffern 2 bis 9 können wir da glatt vergessen und doch qualmt der Kopf, wenn so eine popelige Division oder Multiplikation durchgeführt werden soll. Aber ich hör jetzt auf, sonst muß ich mir wieder anhören, das dies nicht hilfreich sei:flute:
Gruß oldmax
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)