Forum: Mikrocontroller und Digitale Elektronik damit ich mit Timer0 nicht aus der Übung komme!


von Rolf H. (flash01)


Lesenswert?

Hallo Spess, Hannes Lux u. Karl Heinz,
ich bin dabei, ein mit Bascom erstelltes Programm im Jahr 2011
auf Assembler umzusetzen.
Eine Art Maske für den Tiny13 hatte ich mir schon vorab
erstellt und das ohne Fehler.

Und jetzt hänge ich an zwei .EQU xxx Befehlen, wo ich nicht weiter
komme
Hier die Quelldatei:

; Projekt-Name: Projekttiny13                      Datum: 29.02.2012

; Datei: Interrupt-Tiny13.asm

; PORTB,PB1 = LEDgn (Output)
; PORTB,PB2 = LEDrt (Output)

; PINB,PB3 = sw1    (Input)
; PINB,PB4 = sw2    (Input)

; AVR: Tiny13

           .INCLUDE   "tn13def.inc"     ; Deklaration für Tiny13
           .EQU       takt = 1200000    ; Systemtakt 1,2 MHz

           .EQU       LEDgn = PORTB,PB1
           .EQU       LEDrt = PORTB,PB2

           .DEF    akku = r16         ; r16 in akku benannt

           #define sw1 PINB,PB3
           #define sw2 PINB,PB4



           rjmp    reset             ; Reseteinsprung
           .ORG    OVF0addr          ; Interrupt-Vektor
           rjmp    TIM0_OVF          ; Sprung zur ISR

reset:     ldi     akku,LOW (RAMEND) ; Stapel anlegen
           out     SPL,akku


           ldi     akku,0x06         ; Bitmuster 0000 0110
           out     DDRB,akku         ; Datenricht. PB1/2=Output
                                     ; PB0 und PB3 bis PB7=Input
           ldi     akku,0b11111001
           out     PORTB,akku        ; PULLUP

; Timer0 initialisieren:
           ldi     akku,1<<CS02|1<<CS00 ; Prescale = 1024 (Beispiel)
           out     TCCR0B,akku
           ldi     akku,1<<TOIE0     ; Timer Overflow Interrupt 
einrichten
           out     TIMSK0,akku
           sei                       ; Timer frei

loop:      nop
           ;noch steckt im .EQU der Wurm drin
           rjmp    loop


;Interrupt-ISR
TIM0_OVF:  push    r2        ;Kopie r2 auf den Stack, danach SP-1
           in      r2,SREG   ;Inhalt vom Statusregister in r2 laden
           dec     r17       ;ISR Abarbeitung (hier dec  r17)
           out     SREG,r2   ;Inhalt von r2 ins SREG laden
           pop     r2        ;SP+1, danach vom Stack in r2 laden
           reti
       .EXIT

hab ich etwa die Assembler-Direktive .EQU falsch interpretiert?

Wenn ich die beiden entferne (.EQU  LEDgn und LEDrt) ist der Fehler
verschwunden.
Bevor ich jetzt weiter mache, muß das erst raus.

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

.equ ist keine Textersetzung sondern eine Wertzuweisung. Also

   .equ Konstante = Wert

Also z.B.

     .equ LEDPort = PortB

oder

     .equ LEDgn = PB1

funktionieren. Deine Kombination nicht. Ersetze einfach das .equ durch 
#define.

MfG Spess

von Bernd S. (Firma: Anscheinend Corner-Cases ;-)) (bernd_stein)


Lesenswert?

Rolf Hegewald schrieb:
> Hallo Spess, Hannes Lux u. Karl Heinz,
> ich bin dabei, ein mit Bascom erstelltes Programm im Jahr 2011
> auf Assembler umzusetzen.
>...
Ich bin zwar selber noch Anfänger und gehöre auch nicht zu Deinen 
Erlauchten, aber ich freue mich das sich jemand beginnt für Assembler zu 
interessieren.

Ich sehe im wesentlichen den Geschwindigkeitsvorteil in dieser Sprache, 
sowie das besesse Verständnis für die Programmierung der 
Mikrocontroller, deshalb möchte ich Dich auf etwas hinweisen, was wir 
als Anfänger irgendwie als Standard hinnehmen, also ein muß.
Dies ist jedoch eine Abwägungssache. Wie halt das Meiste im Leben.

Ich meine jetzt konkret diese PUSH und POP Befehle. Jeder braucht
2 Taktzyklen, die man sich in dem unteren Programmschnipsel sparen kann, 
wenn man für die Sicherung des Statusregisters ( SREG ) ein kostbares 
oberes ( ab r16 aufwärts ) opfert, das nur zur Sicherung des SREG dient, 
was man ja sowieso - ich möchte sagen - fast immer in einer 
Interrupt-Service-Routine ( ISR ) braucht.
1
> 
2
> ;Interrupt-ISR
3
> TIM0_OVF:  push    r2        ;Kopie r2 auf den Stack, danach SP-1
4
>            in      r2,SREG   ;Inhalt vom Statusregister in r2 laden
5
>            dec     r17       ;ISR Abarbeitung (hier dec  r17)
6
>            out     SREG,r2   ;Inhalt von r2 ins SREG laden
7
>            pop     r2        ;SP+1, danach vom Stack in r2 laden
8
>            reti
9
>        .EXIT
10
>

Bernd_Stein

von spess53 (Gast)


Lesenswert?

Hi

>Ich meine jetzt konkret diese PUSH und POP Befehle. Jeder braucht
>2 Taktzyklen, die man sich in dem unteren Programmschnipsel sparen kann,
>wenn man für die Sicherung des Statusregisters ( SREG ) ein kostbares
>oberes ( ab r16 aufwärts ) opfert, das nur zur Sicherung des SREG dient,
>was man ja sowieso - ich möchte sagen - fast immer in einer
>Interrupt-Service-Routine ( ISR ) braucht.

Da verstehe ich den Sinn nicht. Wenn man ein Register zum Sichern von 
SREG reserviert, dann im Bereich r0...r15 und nicht im oberen Bereich. 
Es sei denn, der AVR hat keine r0..r15.

MfG Spess

von Bernd S. (Firma: Anscheinend Corner-Cases ;-)) (bernd_stein)


Lesenswert?

spess53 schrieb:
>
> Da verstehe ich den Sinn nicht. Wenn man ein Register zum Sichern von
> SREG reserviert, dann im Bereich r0...r15 und nicht im oberen Bereich.
> Es sei denn, der AVR hat keine r0..r15.
>
> MfG Spess
>
Ja, ja so ist das, wenn man nicht Sattelfest ist. Der Grund liegt darin, 
das ich einfach gedacht habe das die Befehle IN und OUT nur auf r16 
aufwärts zugreifen können. Was natürlich falsch ist.

Entschuldigung für die Verwirrung die ich evtl. bei dem einen oder 
anderen angestiftet habe.

Bin jetzt wohl damit durcheinander geraten, das  diese Befehle nur die 
SF-Register bis 63 ansprechen können.

Bernd_Stein

von Rolf H. (flash01)


Lesenswert?

sch... jetzt fang ich nochmal an zu schreiben.
war einfach ausgeloggt, und dann ist alles geschriebene hinüber.

Also nochmal:

habe es abgeändert wie folgt

; AVR: Tiny13

           .INCLUDE   "tn13def.inc"     ; Deklaration für Tiny13
           .EQU       takt = 1200000    ; Systemtakt 1,2 MHz

           #define sw1 PINB,PB3
           #define sw2 PINB,PB4

                    #define LEDgn PORTB,PB1
                    #define LEDrt PORTB,PB2

           .DEF    akku = r16         ; r16 in akku benannt

und es funzt.
Ich habe von Hannes Lux die Quelldatei "schranke.asm" studiert
und meine 2 ISR gesehen zu haben.
Das würde mich hier auch interessieren.
a.) Zeiten von 10 Sec. aufzubauen
b.) Abfrage der Tasten sw1 / sw2
Hannes hat mengenweise .EQU aufgebaut, da den Überblick zu
haben ist nicht einfach.
Aber eins nach den anderen.

Grüße

Rolf

von Peter D. (peda)


Lesenswert?

Rolf Hegewald schrieb:
> sch... jetzt fang ich nochmal an zu schreiben.
> war einfach ausgeloggt, und dann ist alles geschriebene hinüber.

Hast Du mal versucht, zurück zu blättern (Firefox)?
Dann sollte das Editfenster wieder Deinen Text zeigen.

Oder vor dem Abschicken eines längeren Textes markieren und Strg-C.


Peter

von Rolf H. (flash01)


Lesenswert?

Hallo Peter,
hab ich mit meinen Internet-Explorer versucht, vergebens.
Merkwürdig, muß mich auch bei anderen Foren sehr oft neu
anmelden. Man sagte mir, das sei eine Einstellungssache am Browser.


ach ja Spess,
über diese Wertzuweisung wie

z.B. .EQU  konstante = 20

habe ich mir nochmal meine Gedanken gemacht.
Hier wird also konstante der Dezimalwert 20 zugewiesen.

Aber was ist denn bei .EQU LEDgn = PORTB ;so von Dir geschrieben
PORTB ist doch ein Register und kein Wert, oder?

Dezimal 20 = hex 0x20  binär 0b00100000 ist das richtig?

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>PORTB ist doch ein Register und kein Wert, oder?

Nein. In deiner Include-Datei tn13def.inc steht:

.equ  PORTB  =$18

Damit ist 'PORTB' ein Wert.

>Dezimal 20 = hex 0x20  binär 0b00100000 ist das richtig?

Nö.   Dezimal 20 = 0x14

MfG Spess

von Rolf H. (flash01)


Lesenswert?

Hallo Leute,
und wieder mal läßt mich der Timer0 keine Ruhe!

Habe mir aus dem Tutorial eine Abhandlung "Interrupt"
ins Studio4 runter gezogen. War auf dem Mega8 gedacht, habe es
auf dem Tiny13 zugeschnitten.

; Projekt-Name: Projekttiny13                      Datum: 08.04.2012
; Datei: Interrupt-Tiny13-01.asm

; PORTB,PB1 = LEDgn (an PB1 LEDgn)

; AVR: Tiny13

           .INCLUDE   "tn13def.inc"  ; Deklaration für Tiny13
           .EQU       takt = 1200000 ; Systemtakt 1,2 MHz

                    #define LEDgn PORTB,PB1

           .DEF    temp = r16        ; r16 in temp benannt
           .DEF    leds = r17        ; r17 in leds benannt

           rjmp    reset             ; Reseteinsprung
           .ORG    OVF0addr          ; Interrupt-Vektor
           rjmp    TIM0_OVF          ; Sprung zur ISR


reset:     ldi     temp,LOW (RAMEND) ; Stapel anlegen
           out     SPL,temp

           ldi     temp,0xFF
           out     DDRB,temp         ; Datenrichtung PortB = Output


; Timer0 initialisieren:
           ldi     temp,1<<CS02|1<<CS00 ; Prescale = 1024 (Beispiel)
           out     TCCR0B,temp       ; Register TCCR0B
           ldi     temp,1<<TOIE0     ; Timer Overflow Interrupt 
einrichten
           out     TIMSK0,temp       ; Register TIMSK0
           sei                       ; Timer frei (im SREG-Register)

           ldi     leds,0xFF         ; lade R17 mit FF, aber wofür?

loop:      rjmp    loop              ; Schleife


;Interrupt-ISR
TIM0_OVF:  push    r2            ; Kopie r2 auf den Stack, danach SP-1
           in      r2,SREG       ; Inhalt vom Statusregister in r2 laden
           out     PORTB,leds    ; lade PORTB mit Inhalt von r17
           com     leds          ; negiere Inhalt von r17 (auf Low 
gesetzt)
           out     SREG,r2       ; Inhalt von r2 ins SREG laden
           pop     r2            ; SP+1, danach vom Stack in r2 laden
           reti
          .EXIT

Und dazu habe ich zwei Fragen:

1. Nach Einschalten blinkt die LED mit ca. 3 Hz
Im Befehl ldi  leds,0xFF habe ich den Wert auf 0x1F verkleinert,
ergab keine Änderung...mh, ich dachte, dann nehme den Wert
mal ganz weg, ergibt keine Änderung!
Er hat doch dann für den Ablauf in der ISR gar keinen Einfluß, oder?
Aber mit com leds (r17) werden die Bits negiert, also zu LOW gesetzt.
Wie kommt dann die LED durch out   PORTB,leds wieder zum leuchten.
Die zweite Frage stelle ich später.

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>1. Nach Einschalten blinkt die LED mit ca. 3 Hz
Im Befehl ldi  leds,0xFF habe ich den Wert auf 0x1F verkleinert,
ergab keine Änderung...mh, ich dachte, dann nehme den Wert
mal ganz weg, ergibt keine Änderung!

Na ja, Bei einer LED bemerkst du nichts. Bei mehreren LEDs am Port wäre 
ein Unterschied sichtbar. Mit der Frequenz hat das allerdings nichts zu 
tun. Die wird von deinem Timer bestimmt.

>Aber mit com leds (r17) werden die Bits negiert, also zu LOW gesetzt.
>Wie kommt dann die LED durch out   PORTB,leds wieder zum leuchten.

Negieren heisst nicht, das sie auf Null gesetzt werden, sondern, das 
aus einer 1 eine 0 und aus einer 0 eine 1 wird.

Und wie ist die zweite Frage?

MfG Spess

von Rolf H. (flash01)


Lesenswert?

Hallo Spess,
danke für Deine Antwort, hatte heute zu Ostern mit
gar keiner Antwort gerechnet.

Hier nochmal die beiden Befehle in der ISR

           out     PORTB,leds    ; lade PORTB mit Inhalt von r17
           com     leds          ; negiere Inhalt von r17 (auf Low

Im ersten Befehl "out  PORTB,leds"   ; =Register r17
steht PORTB/PB1 auf HIGH, sonst würde die LEDgn nicht leuchten.
Die LED liegt an Vcc und wird über einen NPN-Transistor geschaltet.

Im zweiten Befehl "com   leds" wird PORTB/PB1 zu LOW gebracht,
die LED muß dunkel sein.
Tritt nun ein erneuter Overflow ein, leuchtet sie ja wieder...
aber wodurch, wo ist PORTB/PB1 wieder zu HIGH gebracht worden?

Das ist mir alles noch so unlogisch!

ldi     leds,0xFF         ; lade R17 mit FF, aber wofür?
durch ldi....... nicht, denn wenn ich es entferne, kommt ja keine
Änderung.
Ich vermute, daß ich hier einen logischen Denkfehler habe.

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

Du hast nur eine LED an PB1. Also ist nur Bit1 vom Led interessant:

Led = 0Bxxxxxx1x

1.Overflow
out 0bxxxxxx1x ->  Led leuchtet
com 0bxxxxxx1x -> led = 0bxxxxxx0x

2.Overflow
out 0bxxxxxx0x ->  Led aus
com 0bxxxxxx0x -> led = 0bxxxxxx1x

3.Overflow
out 0bxxxxxx1x ->  Led leuchtet
com 0bxxxxxx1x -> led = 0bxxxxxx0x

und so weiter

>ldi     leds,0xFF         ; lade R17 mit FF, aber wofür?
>durch ldi....... nicht, denn wenn ich es entferne, kommt ja keine
>Änderung.

Led ist das Register r17. Und das hat auch wenn du nichts 
hineinschreibst beim Einschalten einen (zufälligen) Wert. Ob das Bit1 
beim Start 0 oder 1 hat auf das nachfolgend Blinken keinen Einfluss.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

ja, nach zweimal Lesen blick ich jetzt durch!
Meine errechnete Zeit von Overflow zum nächsten beträgt
bei f=1,2 MHz und Prescale 1024 etwa 0,218 Sec.

Jetzt die 2. Frage:
Um die Blinkfrequenz zu verlangsamen schlägt das Tutorial
zwei Möglichkeiten vor.
Entweder Timer1 nutzen, oder ein Zählregister aufstellen.
z.B. Register r18 mit 0x07 laden Decrementieren und auf Low abfagen.
Hatte ich mir so gedacht:

         ldi    r18,0x07
gehe:    tst    r18
         breq   ende
         dec    r18
         rjmp   gehe
ende:
Nun bin ich mir unsicher, setze ich den Ablauf mit in der ISR
hinein oder lasse ich es mit
loop:
      Zählablauf

      rjmp loop

Grüße

Rolf

von asm noob (Gast)


Lesenswert?

...

         ldi    r18,0x07
         tst    r18
gehe:    breq   ende
         dec    r18
         rjmp   gehe
ende:

das "tst r18" nur einmal nutzen, da das "dec" das zero flag sowieso 
setzten würde wenn r18 null wird?

von Rolf H. (flash01)


Lesenswert?

Hallo noob,
Danke für Dein Hinweis!

Es hat mir keine Ruhe gelassen, jetzt sieht die ISR so aus

;Interrupt-ISR
TIM0_OVF:  push    r2
           in      r2,SREG
           out     PORTB,leds
           com     leds

           ldi    r18,0xFF
           tst    r18
gehe:      breq   ende
           dec    r18
           rjmp   gehe

ende:      out     SREG,r2
           pop     r2
           reti
           .EXIT

aber es bringt nichts, die LED blinkt weiterhin mit ca. 3Hz
wie vorher ohne Zählschleife.
Wenn ein Durchlauf = 0,218 Sec. dauert, dann müßten es ja
jetzt x 256 = 55,8 Secunden sein...Sch. Technik, bestimmt wieder
ein Denkfehler von mir.
Oder sollte ich die Zählschleife zwischen out / com setzen?
Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>aber es bringt nichts, die LED blinkt weiterhin mit ca. 3Hz
>wie vorher ohne Zählschleife.

Damit veränderst du auch nur die Abarbeitungszeit der Interruptroutine.
1
           ....
2
           ldi    r18,0xFF
3
           ....
4
5
;Interrupt-ISR
6
TIM0_OVF:  push    r2
7
           in      r2,SREG
8
9
           dec r18                  ; wenn <>0
10
           brne ende                ; dann raus  
11
12
           out     PORTB,leds       ; sonst Blinken
13
           com     leds             ; und Delayzähler
14
           ldi    r18,0xFF          ; neu setzen  
15
16
ende:      out     SREG,r2
17
           pop     r2
18
           reti

Allerdings solltest du den Prescaler kleiner machen oder r18 mit einem 
kleineren Wert laden. Sonst wird die Blinkperiode sehhhhhr lang.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

Hallo Spess,
es funzt!
Sieht also nach Deinen Vorschlag so aus:

           ldi     leds,0xFF ;egal wie, die LED bleibt erst dunkel
                             ;aber das sind im Momment Kleinigkeiten.
           ldi     r18,0b01111111 ;Binär behalte ich besser Übersicht.

loop:      rjmp    loop

;Interrupt-ISR
TIM0_OVF:  push    r2
           in      r2,SREG

           dec     r18              ; wenn <>0
           brne    ende             ; dann raus

           out     PORTB,leds       ; sonst Blinken
           com     leds             ; und Delayzähler
           ldi     r18,0b01111111   ; neu setzen

ende:      out     SREG,r2
           pop     r2
           reti
           .EXIT

Nach meinen Messungen sehen die Zeiten in etwa so aus:

Inhalt in r18

0000 0111 (hex=07; dez.=7)  ca.   2 Sec.
0000 1111 (hex=0F; dez.=15) ca. < 4 Sec.
0001 1111 (hex=1F; dez.=31) ca. < 8 Sec.
0011 1111 (hex=3F; dez.=63) ca. <16 Sec.
0111 1111 (hex=7F; dez.=127 ca.  27 Sec.

Für mich war hier als Beispiel im Besonderen die Frage wichtig
"Wie kann man in der ISR zwei Dinge realisieren"!
Einmal eine Zeit ablaufen lassen und zum anderen etwas zu
steuern.
Für viele Frager ein Anstoß fürs Tutorial, denn erst hierdurch
bin ich schlauer geworden. Für den Rest, den man noch nicht
begriffen hat, helfen dann schon die Provis wie Spess,
Hannes Lux oder K. H. Buchegger.

Viele Grüße und ein Dankeschön

Rolf

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:

> TIM0_OVF:  push    r2
>            in      r2,SREG
>
>            dec     r18              ; wenn <>0
>            brne    ende             ; dann raus
>
>            out     PORTB,leds       ; sonst Blinken
>            com     leds             ; und Delayzähler
>            ldi     r18,0b01111111   ; neu setzen
>

r18 wir hier als Zähler benutzt! Bei jedem ISR AUfruf wird er um 1 
verringert, und wenn dann irgendwann 0 erreicht ist, werden die LED 
umgeschaltet und der Zähler kriegt wieder einen Startwert.

Hier ist also eine Binärschreibweise für die Zahl in r18 nicht 
angebracht. Eine Dezimalschreibweise ist hier viel besser. Denn du sagst 
ja auch nicht: Ich lass r18 von 0b01111111 aus herunterzählen. Wieviele 
Wiederholungen sind des das? Da musst du im Kopf erst mal die Binärzahl 
in eine Dezimalzahl umwandeln um zur Erkentniss zu kommen: r18 wird 63 
mal heruntergezählt.

Dann schreibs doch gleich so hin!

            ldi,     r18, 63

fertig. Du brauchst nichts umrechnen und jeder kann sofort sehen, 
wieviele Wiederholungen das sein werden.

Ob man Hexadezimal-, Binär- oder Dezimalschreibweise bevorzugt hängt 
davon ab, was die Zahl bedeutet! In welchem Zusammenhang sie auftaucht 
und welches die natürlichste Schreibweise dafür ist. Bei Zahlen, die im 
Zusammenhang mit 'zählen' im weitesten Sinn auftauchen ist in der 
überwiegenden Mehrzahl der Fälle die dezimale Schreibweise die 
natürlichste. Denn: wir alle sind Dezimalzahlen von klein auf gewohnt 
und denken und rechnen mit ihnen.


> Für mich war hier als Beispiel im Besonderen die Frage wichtig
> "Wie kann man in der ISR zwei Dinge realisieren"!
> Einmal eine Zeit ablaufen lassen und zum anderen etwas zu
> steuern.

Dann mach das doch. Du machst dann eben 2 Dinge hintereinander in der 
ISR. Manchmal hast du eben Zahlen in Registern, die du für beide Dinge 
gebrauchen kannst, aber das muss nicht so sein.

Der Trick hier bei dir, besteht darin, dass du nur bei jeder n-ten 
Wiederholung in der ISR die LED umschaltest.
Deine ISR kannst du dir wie den Sekundenzeiger einer Uhr vorstellen. 
Früher hast du bei jedem Ticken die LED umgeschaltet. Wenn du jetzt 
möchtest, das nur jede 7-te Uhrentick umgeschaltet wird, wie machst du 
das. Du zählst im Kopf die Anzahl der Ticker mit (oder machst dir eine 
Stricherlliste). Bei jedem Tick zählst du 1 runter bis 0, machst deine 
Aktion und fängst wieder von vorne zu zählen an.
6, 5, 4, 3, 2, 1, 0  - umschalten - 6, 5, 4, 3, 2, 1, 0 - umschalten - 
6, ...
Deine Uhr tickt nach wie vor jede Sekunde 1-mal. Aber die LED werden 
alle 7 Sekunden umgeschaltet, wie gewünscht. Und in deinem Programm 
übernimmt eben das Register r18 die Rolle dieser Stricherlliste.

Viele Konzepte aus der realen Welt lassen sich überraschend gut auf die 
Programmierung anwenden. Wenn du mit Timer zu tun hast, dann ist oft die 
Analogie "Eine Uhr tickt alle 1 Sekunde, wie würde ich persönnlich mein 
Problem damit lösen" ein überraschend guter Leitfaden. Denn im Prinzip 
weißt du von vielen Problemen wie man sie löst. Du könntest das aus dem 
Stand heraus. Und in einem Programm ist das auch nicht anders. Die 
Register sind dein 'Notizzettel' auf dem du dir Notizen in Form von 
Zahlen machen kannst und mit denen du operieren kannst. Du musst nur 
immer herausfinden, wie DU dasselbe Problem mit den relevanten Zahlen 
und einem Notizblock lösen würdest.

Vor noch gar nicht allzulanger Zeit war es so, dass es für jedes Problem 
einen eigenen Befehl gab. Aber das ändert sich jetzt langsam. Du kennst 
jetzt die wichtigsten Befehle. Jetzt beginnt die Phase, in der du mit 
Kombinationen von Befehlen Ideen ausdrückst. So wie ein Maler seine 
Pinsel, Farben, Techniken, Leinwände, deren Vor und Nachzüge kennen 
gelernt hat, so beginnst du jetzt immer mehr, dein Basiswissen 
einzusetzen um mit Kombinationen davon bestimmte Dinge zu erreichen.

von Rolf H. (flash01)


Lesenswert?

an Karl Heinz meinen Dank, habe es drei Mal gelesen.
Nun habe ich mich durchgerungen und bei Reichelt den ATtiny 25 bestellt.

Aus dem Datenblatt "Register Summary" habe ich das vom Tiny13 und 25
ausgedruckt.

Identisch sind die Register mit denen ich schon gearbeitet habe:

SREG  SPL  TCCR0B  TCNT0  PORTB  DDRB  PINB

hinzu gekommen ist SPH
jetzt müßte ich das so aufstellen


        .INCLUDE   "tn25def.inc"
        .EQU       takt = 1200000

        rjmp   start
        .ORG   OVF0addr   ;bleibt das so?
        rjmp   TIM0_OVF   ;bleibt das so? (war die vom Tiny13)


start:  ldi    r16,LOW (RAMEND)
        out    SPL,r16
        ldi    r16,HIGH (RAMEND)
        out    SPH,r16

geändert hat sich Register TIMSK0 in TIMSK

der Tiny13 hat einen Speicher von 1KByte, das wären 1024 Byte.
Was sich darin verbirgt ist mir immer noch nicht ganz klar.
Soviel weiß ich:
32 Arbeitsregister r0-r32 von Speicher 00-1F=dez.0-31   (32 Byte)
64 SFR Register           von Speicher 20-5F=dez.32-95  (64 Byte)
64 x 8Bit SRam            von Speicher 60-9F=dez.96-159 (64 Byte)

der Tiny 25 hat 2KByte, RAM=128 Byte, EEPROM=128 Byte
SRAM weiß ich nicht!
Bedeutet das, die Angabe 2K ist sein Gesamtspeicher, in dem jetzt
alle Speicherarten drin stecken, also Ram, SRAM, EEPROM

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>hinzu gekommen ist SPH

ist eigentlich nur für den ATTiny85 interessant, da dort der 
Stackpointer nicht mehr in ein Byte passt. Beim ATTiny25 brauchst du 
übrigens den Stackpointer nicht mehr initialisieren. Der wird nach einem 
Reset automatisch auf RAMEND gesetzt.

->Datenblatt S.11:  4.6.1 SPH and SPL — Stack Pointer Register

>der Tiny 25 hat 2KByte, RAM=128 Byte, EEPROM=128 Byte
>SRAM weiß ich nicht!

Der interne RAM der AVRs ist generell S(tatic)RAM.

>Bedeutet das, die Angabe 2K ist sein Gesamtspeicher, in dem jetzt
>alle Speicherarten drin stecken, also Ram, SRAM, EEPROM

Nein. Das ist nur der Flash.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

Hallo Spess,
dann habe ich in meinen Angaben doch Quatsch geschrieben!

Nämlich:

32 Arbeitsregister r0-r32 von Speicher 00-1F=dez.0-31   (32 Byte)
64 SFR Register           von Speicher 20-5F=dez.32-95  (64 Byte)
was schließt sich denn ab Speicher 60 an

wenn er einen Speicher von 1KByte hat und es das SRAM ist, dann
stimmt doch das nachfolgende nicht, oder
64 x 8Bit SRam  von Speicher 60-9F=dez.96-159 (64 Byte)

in welchen Bereich steckt denn nun das EEPROM?
Im SRAM gehen doch die Daten nicht verloren.


Fragen über Fragen!

von Hannes L. (hannes)


Lesenswert?

Sorry, ich hatte den Anfang des Threads verpasst, habe ihn erst jetzt 
gefunden...

Rolf Hegewald schrieb:
> in welchen Bereich steckt denn nun das EEPROM?

Das EEPROM wird nicht in den Adressbereich des RAMs gemappt, es hat eine 
eigene Adressierung. Die Adresse wird nach eearh:eearl geschrieben, die 
Daten in eedr geschrieben bzw daraus gelesen. Der Schreib- oder 
Lesebefehl muss durch Setzen der entsprechenden Bits in eecr ausgelöst 
werden.

> Im SRAM gehen doch die Daten nicht verloren.

Doch, SRAM ist flüchtig. Die Daten gehen bei Ausschalten der 
Versorgungsspannung verloren. Nur Flash (Programmspeicher) und EEPROM 
(Daten- bzw. Parameterspeicher) sind nichtflüchtig, behalten also auch 
ohne Versorgungsspannung ihren Inhalt.

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
danke für Deine Antwort.
Habe soeben den ersten Tiny 25 zum laufen gebracht.
In der ISR sind einmal ein Zeitablauf und EIN/AUS einer Led
enthalten.
Sieht alles so aus:

; Projekt-Name: Projekttiny25                      Datum: 15.04.2012

; Datei: Interrupt-Tiny25-00.asm

; PORTB,PB1 = LEDgn (an PB1 LEDgn)


; AVR: ATTINY13-20PU (DIP)

           .INCLUDE   "tn25def.inc"  ; Deklaration für Tiny25
           .EQU       takt = 1200000 ; Systemtakt 1,2 MHz

                    #define LEDgn PORTB,PB1

           .DEF    temp = r16        ; r16 in temp benannt
           .DEF    leds = r17        ; r17 in leds benannt

           rjmp    reset             ; Reseteinsprung
           .ORG    OVF0addr          ; Interrupt-Vektor
           rjmp    TIM0_OVF          ; Sprung zur ISR


reset:     ldi     temp,LOW (RAMEND) ; Stapel anlegen
           out     SPL,temp

           ldi     temp,0xFF
           out     DDRB,temp         ; Datenrichtung PortB = Output


; Timer0 initialisieren:
           ldi     temp,1<<CS02|1<<CS00 ; Prescale = 1024 (Beispiel)
           out     TCCR0B,temp       ; Register TCCR0B
           ldi     temp,1<<TOIE0     ; Timer Overflow Interrupt 
einrichten
           out     TIMSK,temp       ; Register TIMSK0
           sei                       ; Timer frei (im SREG-Register)


           ldi     leds,0xFF
           ldi     r18,7

loop:      rjmp    loop

;Interrupt-ISR
TIM0_OVF:  push    r2
           in      r2,SREG
           dec     r18              ; wenn <>0
           brne    ende             ; dann raus

           out     PORTB,leds       ; sonst Blinken
           com     leds             ; und Delayzähler
           ldi     r18,7            ; neu setzen

ende:      out     SREG,r2
           pop     r2
           reti
           .EXIT

Das ist für den Tiny13
32 Arbeitsregister r0-r32 von Speicher 00-1F=dez.0-31   (32 Byte)
64 SFR Register           von Speicher 20-5F=dez.32-95  (64 Byte)
was schließt sich denn ab Speicher 60 an
hoffentlich blicke ich da mal richtig durch, im Tutorial muß
ich nochmal lesen.
Grüße

Rolf

von Rolf H. (flash01)


Lesenswert?

Hallo Leute,
ich habe wieder mal mit einem neuen Tiny25 geübt, hier die .asm
1
; Projekt-Name: Projekttiny25                      Datum: 27.04.2012                  
2
3
; Datei: Interrupt-Tiny25-01.asm
4
5
; PORTB,PB0 = LEDrt / PB1 = LEDgn / PB2 = LEDgl (mit #define)
6
7
; PINB,PB3 = sw1 (mit #define)
8
; PINB,PB4 = sw2  (mit #define)
9
10
; AVR: ATTINY25-20PU (DIP)
11
12
; Ablauf: nach sw1 langsames, sw2 schnelles nacheinander Leuchten der Leds. 
13
14
           .INCLUDE   "tn25def.inc"  ; Deklaration für Tiny25
15
           .EQU       takt = 1200000 ; Systemtakt 1,2 MHz
16
17
                  ;Definition von Arbeitsregistern
18
           .def    akku=r16
19
           .def    temp1=r17
20
           .def    temp2=r18
21
           
22
           ;Definition von PORTB und PINB (SFR-Register)
23
           #define LEDrt PORTB,PB0
24
           #define LEDgn PORTB,PB1
25
    #define LEDgl PORTB,PB2
26
27
           #define sw1   PINB,PB3
28
           #define sw2   PINB,PB4
29
           
30
           
31
           rjmp    reset             ; Reseteinsprung
32
           .ORG    OVF0addr          ; Interrupt-Vektor
33
           rjmp    TIM0_OVF          ; Sprung zur ISR
34
35
36
reset:     ldi     akku,LOW (RAMEND) ; Stapel anlegen
37
           out     SPL,akku
38
39
           ldi     akku,0b00000111   ; PB0-PB2=Outp.     
40
           out     DDRB,akku         ; Datenrichtungsregister
41
           ldi     akku,0b00011000   ; an PB3 und PB4=PULL UP
42
           out     PORTB,akku
43
;Achtung: cbi bzw. sbi zum schalten der Ports verwenden                                    
44
           
45
; Timer0 initialisieren:
46
           ldi     akku,1<<CS02|1<<CS00 ; Prescale = 1024
47
           out     TCCR0B,akku        ; Register TCCR0B
48
           ldi     akku,1<<TOIE0      ; Timer Overflow Interrupt einrichten
49
           out     TIMSK,akku         ; Register TIMSK (Tiny13=TIMSK0)
50
           sei                       ; Timer frei (im SREG-Register)
51
52
; Alle LEDs = AUS
53
           cbi     LEDrt
54
           cbi     LEDgn
55
           cbi     LEDgl
56
57
loop:      sbic    sw1               ; Abfrage PINB,PB3
58
           rjmp    gehesw2           ; Sprung, wenn sw1=1
59
           rjmp    langsam
60
61
gehesw2:   sbic    sw2               ; Abfrage PINB,PB4
62
           rjmp    gehesw1
63
           rjmp     schnell
64
gehesw1:   rjmp    loop
65
66
langsam:   sbi     LEDrt
67
           rcall   zeit1             ; Pausenzeit ca. 1 Sec.
68
           sbi     LEDgn
69
           rcall   zeit1
70
           sbi     LEDgl
71
           
72
           rcall   zeit1
73
           cbi     LEDrt
74
           rcall   zeit1             
75
           cbi     LEDgn
76
           rcall   zeit1
77
           cbi     LEDgl
78
           rcall   zeit1
79
           rjmp    langsam 
80
           
81
schnell:   sbi     LEDrt
82
           rcall   zeit3             ; Pausenzeit ca. 3 Sec.
83
           sbi     LEDgn
84
           rcall   zeit3
85
           sbi     LEDgl
86
           
87
           rcall   zeit3
88
           cbi     LEDrt
89
           rcall   zeit3             
90
           cbi     LEDgn
91
           rcall   zeit3
92
           cbi     LEDgl
93
           rcall   zeit3
94
           rjmp    schnell 
95
96
zeit1:     ldi     temp1,0x01        ; Zeit ca. 1 Sec.
97
pause1:    tst     temp1
98
           brne    pause1            ; springe nach pause1: wenn HIGH
99
           ret 
100
101
zeit3:     ldi     temp2,0x05        ; Zeit ca. 3 Sec.
102
pause3:    tst     temp2
103
           brne    pause3            ; springe nach pause3: wenn HIGH
104
           ret
105
106
;Interrupt-ISR
107
TIM0_OVF:  push    r2
108
           in      r2,SREG
109
           dec     temp1
110
           dec     temp2
111
           out     SREG,r2
112
           pop     r2
113
           reti
114
           .EXIT
Ich wollte aus ; Alle LEDs = AUS
1
           cbi     LEDrt
2
           cbi     LEDgn
3
           cbi     LEDgl
1
           cbi     LEDrt|LEDgn|LEDgl
machen, aber dann sagte der Compiler Error.

Ich habe bewußt zwei Arbeitsregister (r17 und r18) in die
ISR geschoben, um zu sehen ob das funktioniert und das mit dem
Interrupt besser begreifen zu können.
Aber dann muß doch bei jedem Durchlauf erst r17 und danach r18
dekrementiert werden, oder?

Es macht richtig Freude, mit den Befehlen wie "sbic oder brne"
zu arbeiten.
Wenn ich an die Bascom-Zeit zurück denke, was bin ich oft
an dem "IF" verzweifelt.

Grüße

Rolf

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:

> Ich habe bewußt zwei Arbeitsregister (r17 und r18) in die
> ISR geschoben, um zu sehen ob das funktioniert und das mit dem
> Interrupt besser begreifen zu können.
> Aber dann muß doch bei jedem Durchlauf erst r17 und danach r18
> dekrementiert werden, oder?

Ja, klar.
Dein µC arbeitet einen Befehl nach dem anderen ab.

Aber macht ja nichts. Am Ende der ISR ist der gewünschte Effekt 
eingetreten: beide Register, r17 und r18 sind dekrementiert. Und da die 
ISR von nichts anderem unterbrochen wird, sieht es in den Hauptschleifen 
so aus, als ob beide Register gemeinsam ihren Wert ändern. Die 
Hauptschleife kann nicht feststellen, welches der beiden Register zuerst 
dekrementiert wurde. Zu irgendeinem Zeitpunkt wird sie durch den 
Interrupt unterbrochen und wenn dessen Bearbeitung fertig ist, haben 
beide Register ihren Wert geändert.

von spess53 (Gast)


Lesenswert?

Hi

>Ich wollte aus ; Alle LEDs = AUS
....
>           cbi     LEDrt|LEDgn|LEDgl

>machen, aber dann sagte der Compiler Error.

Kein Wunder. Setze mal deine #defines für LEDrt,LEDgn und LEDgl dort 
ein.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

mh...versteh ich noch nicht.
Ich habe diese doch zu Beginn deklariert,
und nun soll ich es nochmal machen?

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>mh...versteh ich noch nicht.

Aus cbi LEDrt|LEDgn|LEDgl

macht der Preprocessor

  cbi PORTB,PB0 | PORTB,PB1 | PORTB,PB2

Und das versteht der Assembler nicht und meckert. Zu Recht.

Mach doch einfach ein Makro draus:

   .macro LED_Off
     cbi     LEDrt
     cbi     LEDgn
     cbi     LEDgl
   .endmacro

Im Programm schreibst du dann einfach

   LED_Off

und fertig.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

Hallo Leute,
einen Ortungspieper habe ich vom Pic auf Atmel umgesetzt.
Er arbeitet je nach Stellung der 3 DIP-Schalter in den Zeiten
20  30  40 Minuten...dann schreit er im Intervall los.
In jedem Kornfeld ist das Flugmodell auszumachen.
Die Quelldatei habe ich mal nur auf 20 Minuten zusammen
gerückt.

; Datei: zeitmess02.asm    ;Datum: 04.05.2012

; PORTB,Pb0: Ausgang (Pieper)
; PINB,Pb3-Pb4: Eingang (test=b3/sw1=b4)

; Register r17 mit veränderbaren Inhalt
; Register r18 mit festen Wert = 255
; r17 = 21 = gemessen 1176 Sec. (ca. 20Min.)
; r17 = 32 = gemessen 1789 Sec. (ca. 30Min.)
; r17 = 43 = gemessen 2420 Sec. (ca. 40Min.)


;AVR: Tiny 13

           .INCLUDE  "tn13def.inc" ;Deklarationen für Tiny13
           .EQU    takt = 1200000  ;Systemtakt 1,2 MHz

           .DEF     akku = r16      ;r16 in akku benannt
           #define  test PINB,PB3
           #define  sw1  PINB,PB4

            rjmp    reset          ;Reset-Einsprung
    .ORG    OVF0addr       ;Interupt-Vektoren
    rjmp     TIM0_OVF       ;Sprung zur ISR

reset:      ldi     akku,LOW(RAMEND)
            out     SPL,akku
            ldi     akku,0b00000001;Bitmuster 0000 0001
            out     DDRB,akku      ;PORTB,PB0 ist Ausgang
            ldi    akku,0b00011110 ;PB1-PB4 = PULLUP
     out    PORTB,akku

; Timer0 initial.
            ldi     akku,1<<CS02|1<<CS00
            out     TCCR0B,akku
     ldi     akku,1<<TOIE0
     out     TIMSK0,akku
     sei
; alle 0,22 Sec. Interrupt Overflow

loop:

; Abfrage von test und sw1 auf LOW

geh:     sbic     test           ;PINB,PB3 = Taste
     rjmp     gehsw1    ;Sprung nach gehsw1
            rjmp     piepen
;******************************

; Zeitvorgabe als Beispiel für 1200 Sec. = 20 Min.

gehsw1:     sbic     sw1           ; PINB,PB4 = DIP-Schalter1
            rjmp     geh            ; Sprung nach geh, wenn sw1=1
     ldi     r18,255       ; dezimal = 255

sprung2:    rcall   variable       ; nach r17= dezimal 22
            dec     r18          ; r18 - 1
     tst     r18
            brne     sprung2       ; Sprung, wenn r18=1
     rjmp     piepen          ; Sprung, wenn r18=0
;*******************************

piepen:     sbi     PORTB,PB0     ; Piepen = EIN
            rcall   zeit2         ; Zeit ca. 2 Sec
     cbi     PORTB,PB0     ; Piepen = AUS
     rcall   zeit3         ; Zeit ca. 3 Sec
            rjmp     piepen
     rjmp    loop          ; Schleife
;*******************************

       ;0,22x9 = 1,98 Sec.
zeit2:      ldi     r17,9        ; dezimal = 9
pause2:     tst     r17         ; teste r17 auf Null
     brne    pause2         ; Sprung, wenn r17=1
     ret

           ;0,22x15 = 3,3 Sec.
zeit3:      ldi     r17,15       ; dezimal = 15
pause3:     tst     r17         ; teste r17 auf Null
     brne    pause3         ; Sprung, wenn r17=1
            ret
;*******************************


variable:   ldi     r17,22       ; dezimal = 22
fest:     tst     r17         ; teste r17 auf Null
     brne    fest         ; Sprung, wenn r17=1
            ret


;Interrupt-ISR
TIM0_OVF:  push    r2
           in      r2,SREG
           dec     r17
           out     SREG,r2
           pop     r2
           reti
       .EXIT

so, ich mußte eine Menge formatieren.
Obwohl ich in Tools  Optionen  Edit auf Tab=1 und den Haken
gesetzt hatte..versteh ich nicht.
An den ganzen Timer0 Ablauf begreif ich eins immer noch nicht.
Ich habe die Register R17 u. R18 am arbeiten.
R18 hat den festen Wert von 255, R17 für 20 Minuten = 22
Dadurch, daß ich Prescale auf 1024 gesetzt habe, komme ich auf
ca. 0,22 Sec. = 1Tick
Mit einem Zeitmesser kam ich auf genau 1176 Sec. Stimmt ja auch
alles, wenn man Toleranzen mit einbezieht.  Aber!
Ich gehe mal von aus und aktiviere vor Ubatt sw1.
Nun stecke ich die Versorgung an...der Timer läuft los und die
Hauptroutiene, die in Geschwindigkeit ankommt wo r18 auf 255
geladen wird...dann weiter
sprung2: wo über rcall variable r17 auf 22 geladen wird.
kommt zurück nach r18-1 und bald ist 255 zu Null angekommen.
Das alles läuft mit einer Windeseile ab, da denkt der Timer doch
noch garnicht daran, das Ganze anhalten zu wollen, und R17 um
-1 zu verringern.
Meiner Meinung steckt doch hier gar kein syncroner Ablauf drin
R17 und R18 laufen wild durcheinander, oder?
Das ich trotzdem auf fast genau 20 Min. komme wundert mich!

Grüße

Rolf

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.