[ATtiny13] Einfachstes Assemblerprogramm mit Timer-Interrupt funktioniert nicht auf

#1215545
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 :/
#1215567
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
  [...]
#1215603
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 :)
Angehängte Dateien:
Gast #1215607
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
#1215611
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?
#1215842
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

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