mikrocontroller.net

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


Autor: Sören P. (webrequest)
Datum:

Bewertung
0 lesenswert
nicht 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.

; Compile:    avra first.asm
; Load into ATtiny:   avrdude -p ATtiny13 -c usbasp -U flash:w:first.hex

.include "tn13def.inc"

.def temp = r16
.def leds = r17
.def i = r18

.org 0x0000
        rjmp    main

.org OVF0addr
        rjmp    overflow
 
main:
        ldi     temp, RAMEND;    ; Stackpointer setzen.
        out     SPL, temp
  
        ldi     temp, 0xFF    ; Kompletter Port B ist Ausgang.
        out     DDRB, temp
 
        ldi     leds, 0xFF    ; Alle LEDs an
        out     PORTB, leds
 
        ldi     temp, 0x01        ; Teiler 1 (0,000064 Sek. zwischen Overflows)
        out     TCCR0B, temp
 
        ldi     temp, 1 << TOIE0        ; Interrupt bei Timer-Overflow
        out     TIMSK0, temp
 
        sei        ; Interrupts aktivieren
 
programmschleife:   
  rjmp    programmschleife  ; Endlosschleife

 
overflow:                   ; Overflow-Handler
  cpi  i, 5      
  brne  zaehler_nicht_erreicht
  breq   zaehler_erreicht

zaehler_nicht_erreicht:
  inc i
  reti

zaehler_erreicht:
  ldi  i, 0
  cpi   leds, 0xFF
  brne   leds_sind_an
  breq  leds_sind_aus

leds_sind_an:
  ldi  temp, 0x00    ; Ausschalten.
  out  PORTB, temp
  reti

leds_sind_aus:        ; Anschalten.
  ldi  temp, 0xFF
  out  PORTB, temp
  reti

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

Autor: Stefan N. (laser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach mal aus den beiden "breq" jeweils ein "rjmp".

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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
  [...]
  ldi  i, 0
  cpi   leds, 0xFF
  brne   leds_sind_an
  breq  leds_sind_aus

leds_sind_an:
  ldi  temp, 0x00    ; Ausschalten.
  out  PORTB, temp
  reti

leds_sind_aus:        ; Anschalten.
  ldi  temp, 0xFF
  out  PORTB, temp
  reti
  [...]
einfach
  [...]
  com   leds        ;leds invertieren
  out  PORTB, leds  ;Zustände ausgeben
  reti
  [...]
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
  ldi  temp, 0x0F
  [...]
  eor leds, temp    ;letzte 4 Bit toggeln
  out  PORTB, leds
  reti
  [...]

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sören P. wrote:
>         ldi     leds, 0xFF    ; Alle LEDs an
...
>   cpi   leds, 0xFF

Dieser Vergleich dürfte also immer wahr ergeben.


Peter

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

Gruß aus Berlin
Michael

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sören P. (webrequest)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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 :)

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi


overflow:    push r16
             in r16,SREG
             push r16

             dec i
             brne overflow10 

             ldi r16,$FF
             eor leds,r16
             out  PORTB,leds
             ldi i,5

overflow10:  pop r16
             out SREG,r16
             pop r6
             reti

So in etwa könnte das aussehen.

MfG Spess

Autor: Sören P. (webrequest)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.