Forum: Mikrocontroller und Digitale Elektronik [ATtiny13] Einfachstes Assemblerprogramm mit Timer-Interrupt funktioniert nicht auf


von Sören P. (webrequest)


Lesenswert?

Hallo,

bin vor kurzem auch in den Genuss bekommen, mit Mikrocontrollern 
rumbasteln zu dürfen. Habe mir ein paar ATtiny13's gegönnt, da diese 
relativ günstig sind, aber für den Anfang meines Erachtens völlig 
ausreichen.

Zuerst habe ich ein paar einfache Programme in C - welches ich schon 
teilweise behersche - geschrieben. LEDs blinken und so, nichts 
komplexes.

Aber ich habe mir gedacht, dass ich mir Assembler wenigstens mal 
angucken kann. Einfache Dinge, wie ein paar LEDs zum Blinken bringen, 
gingen hier auch - bis ich zu den Interrupts kam.

Der Aufbau des ATtinys ist richtig, aber irgendwie blinken die LEDs 
nicht, wie sie sollten, sondern leuchten.

Da ich unter Linux arbeite, kann ich leider nicht in den Genuss eines 
simulierenden AVR-Studios kommen. Ich hoffe, ihr könnt mir trotzdem 
helfen.

An den Fuses habe ich nichts verstellt, der Tiny läuft also mit internem 
Takt und so.

1
; Compile:    avra first.asm
2
; Load into ATtiny:   avrdude -p ATtiny13 -c usbasp -U flash:w:first.hex
3
4
.include "tn13def.inc"
5
6
.def temp = r16
7
.def leds = r17
8
.def i = r18
9
10
.org 0x0000
11
        rjmp    main
12
13
.org OVF0addr
14
        rjmp    overflow
15
 
16
main:
17
        ldi     temp, RAMEND;    ; Stackpointer setzen.
18
        out     SPL, temp
19
  
20
        ldi     temp, 0xFF    ; Kompletter Port B ist Ausgang.
21
        out     DDRB, temp
22
 
23
        ldi     leds, 0xFF    ; Alle LEDs an
24
        out     PORTB, leds
25
 
26
        ldi     temp, 0x01        ; Teiler 1 (0,000064 Sek. zwischen Overflows)
27
        out     TCCR0B, temp
28
 
29
        ldi     temp, 1 << TOIE0        ; Interrupt bei Timer-Overflow
30
        out     TIMSK0, temp
31
 
32
        sei        ; Interrupts aktivieren
33
 
34
programmschleife:   
35
  rjmp    programmschleife  ; Endlosschleife
36
37
 
38
overflow:                   ; Overflow-Handler
39
  cpi  i, 5      
40
  brne  zaehler_nicht_erreicht
41
  breq   zaehler_erreicht
42
43
zaehler_nicht_erreicht:
44
  inc i
45
  reti
46
47
zaehler_erreicht:
48
  ldi  i, 0
49
  cpi   leds, 0xFF
50
  brne   leds_sind_an
51
  breq  leds_sind_aus
52
53
leds_sind_an:
54
  ldi  temp, 0x00    ; Ausschalten.
55
  out  PORTB, temp
56
  reti
57
58
leds_sind_aus:        ; Anschalten.
59
  ldi  temp, 0xFF
60
  out  PORTB, temp
61
  reti

Ich hoffe, ihr könnt mir helfen. Ich habe echt absolut keine Ahnung, was 
ich falsch gemacht haben könnte :/

von Stefan N. (laser)


Lesenswert?

Mach mal aus den beiden "breq" jeweils ein "rjmp".

von Karl H. (kbuchegg)


Lesenswert?

Du musst schnelle Augen haben :-)

Rechne doch mal nach mit welcher Frequenz deine LED 'blinken' :-)

(Beim Drüberschauen ist mir nichts aufgefallen, Pgm sieht auf die 
Schnelle gut aus. Nur das Timing eben)

von Michael U. (amiga)


Lesenswert?

Hallo,

habe nur schnell rübergeschaut:

Prescaler auf 1, also läuft der Timer mit dem AVR-Takt.
Overflow-IRQ, also eine volle Runde -> 256 macht rund 4700Hz.
In der Interruptroutine zählst Du 5 Durchläufe, also schalten die LEDs 
mit knapp 1000Hz.
Hast Du so schnelle Augen???

ÜS: grrr... wieder zu langsam... ;)

Gruß aus Berlin
Michael

von Johannes M. (johnny-m)


Lesenswert?

Du vergleichst leds, gibst aber temp aus. leds wird aber nirgends 
geändert. Deshalb kann auch nur einmal was passieren. Wozu zwei 
Variablen? temp wird überhaupt nicht gebraucht.

Das Toggeln der LEDs geht übrigens wesentlich kürzer und einfacher mit 
einem Einerkomplement (com) der LED-Zustände.

Also anstatt
1
  [...]
2
  ldi  i, 0
3
  cpi   leds, 0xFF
4
  brne   leds_sind_an
5
  breq  leds_sind_aus
6
7
leds_sind_an:
8
  ldi  temp, 0x00    ; Ausschalten.
9
  out  PORTB, temp
10
  reti
11
12
leds_sind_aus:        ; Anschalten.
13
  ldi  temp, 0xFF
14
  out  PORTB, temp
15
  reti
16
  [...]
einfach
1
  [...]
2
  com   leds        ;leds invertieren
3
  out  PORTB, leds  ;Zustände ausgeben
4
  reti
5
  [...]
In leds steht immer der aktuelle Zustand der LEDs. Der wird bei 
Erreichen des Zählerstandes invertiert (dazu muss nur am Anfang einmal 0 
oder 0xFF drin stehen). Wenn nicht alle Pins geändert werden sollen, 
geht es mit einem eor (Exklusiv-ODER) mit einer entsprechenden 
Bitmaske. Wenn nur die 4 LSB gesetzt werden sollen, dann
1
  ldi  temp, 0x0F
2
  [...]
3
  eor leds, temp    ;letzte 4 Bit toggeln
4
  out  PORTB, leds
5
  reti
6
  [...]

von Peter D. (peda)


Lesenswert?

Sören P. wrote:
1
>         ldi     leds, 0xFF    ; Alle LEDs an
2
...
3
>   cpi   leds, 0xFF

Dieser Vergleich dürfte also immer wahr ergeben.


Peter

von Michael U. (amiga)


Lesenswert?

Hallo,

igitt... na ein Glück, daß nicht nur ich das übersehen habe... ;-)))

Gruß aus Berlin
Michael

von Karl H. (kbuchegg)


Lesenswert?

Michael U. wrote:
> Hallo,
>
> igitt... na ein Glück, daß nicht nur ich das übersehen habe... ;-)))
>

:-)
Yep. Mit dieser ganzen unnützen Branch-Orgie in dieser ISR wird man ganz 
schwumrig.

von Sören P. (webrequest)


Angehängte Dateien:

Lesenswert?

peda, deinen Einwand verstehe ich nicht ganz.
led wird ja nur einmal, und zwar in main, auf 0xFF gesetzt, danach ist 
der Status ja abhängig vom Blinkstand der LEDs.


Habe das ganze nach euren Vorschlägen nochmal überarbeitet (war mir da 
mit der Blinkfrequenz passiert ist, weiß ich selber nicht :-)) und 
angehängt.
Eigentlich müsste das funktionieren, tut es aber nicht :/


"Mach mal aus den beiden "breq" jeweils ein "rjmp"."
Habe ich einfach mal gemacht, aber noch nichts so ganz verstanden, 
wieso? Wäre nett, wenn du das nochmal eräutern könntest :)

Dass ich leds verändert, aber temp ausgegeben habe, war ebenso ein 
Fehler, aber auch nach Korrektur mag es noch nicht so, wie ich es will 
;D

Wie gesagt, ich bin blutiger Assembleranfänger. Über weitere 
Lösungsansätze würde ich mich selbstverständlich sehr freuen :)

von spess53 (Gast)


Lesenswert?

Hi

1. Eine Interruptroutine sollte nur ein 'reti' haben.

2. Schleifenzähler besser nach unten zählen. dann reicht:

     dec rXY
     breq abcd  -> wenn Null
     ->wenn nicht Null

3. '  cpi   leds, 0xFF' kannst du dir sparen, wenn du ein anderes 
Register mit $FF lädst und  'eor leds,Register' machst.

4. In Interruptroutinen immer SREG sichern. Nichtbeachtung bringt 
lustige Effekte.

5......

Keine Lust mehr

MfG Spess

von Johannes M. (johnny-m)


Lesenswert?

1
ldi     temp, 0b00000101        ; Teiler 1024 (1000000 / 1024 = 976 Overflows/s)
Nö.
Ein Überlauf des Timers tritt erst dann auf, wenn der Timer einmal 
durchgezählt hat, und das ist bei einem 8-Bit-Timer nach 256 Timertakten 
der Fall. Das gibt bei den Einstellungen etwa 3,8 Überläufe pro Sekunde, 
was mit dem Zählen bis 100 ungefähr 26 Sekunden dauert, bis sich was 
tut.

Und bitte benutze auch hier die Namen der Steuerbits, wie es in 
Bitmanipulation beschrieben ist!

Und warum hast Du meinen Vorschlag oben ignoriert und die ganze 
unsinnige Abfragerei stehen gelassen?

von spess53 (Gast)


Lesenswert?

Hi

1
overflow:    push r16
2
             in r16,SREG
3
             push r16
4
5
             dec i
6
             brne overflow10 
7
8
             ldi r16,$FF
9
             eor leds,r16
10
             out  PORTB,leds
11
             ldi i,5
12
13
overflow10:  pop r16
14
             out SREG,r16
15
             pop r6
16
             reti

So in etwa könnte das aussehen.

MfG Spess

von Sören P. (webrequest)


Lesenswert?

Aha, jetzt läuft es auf einmal.
In Danke euch, jetzt hab' ich 'ne Basis zum drin-rum-wuseln. ;D

von Peter D. (peda)


Lesenswert?

spess53 wrote:
> So in etwa könnte das aussehen.

Man muß ja nicht so umständlich programmieren, wie ein dummer 
C-Compiler.

Reserviere ein low-Register fürs SREG sichern. Da es keine 
Interruptprioritäten gibt, reicht ein Register für alle Interrupts.
Reserviere etwa 4 high Register und den Y-Pointer als Scratchpad für 
Interrupts.
Dann ist in der Regel in Interrupthandlern kein PUSH/POP nötig.


Peter

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.