mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Sprungtabelle per externen Interrupt


Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey

Ich versuche gerade die Sprungtabellen aus dem Tutorium zu verstehen. Da 
ich kein AVRStudio habe habe ich versucht mir ein programm zu schreiben 
und das auf meinem Attiny13 laufen zu lassen. An PB1 hängt ein Taster 
und am PB 0 eine LED. Jetzt sollte per Interrupt auf Tastendruck die LED 
an und ausgeschaltet werden.

 .include "tn13def.inc"

.def  status  =r16
.def  temp  =r17

.org 0x0000
         rjmp main          ; Reset Handler
.org 0x0001
         rjmp int0_handler      ; IRQ0 Handler

main:
        ldi temp, LOW(RAMEND)   ; LOW-Byte der obersten RAM-Adresse
        out SPL, temp

  ldi r16, 0b00011001  ; PB0, PB3 und PB4 sind Ausgänge PB1 ist Eingang
  out DDRB, temp

        ldi temp, (1<<ISC00)    ; INT0 auf Low level konfigurieren
        out MCUCR, temp    ; und weg damit...

  ldi temp, (1<<INT0)  ; INT0 enablen
  out GIMSK, temp    ; und weg damit...

  ldi r16, 0b00000000  ; Ertsmal alles aus
  out PORTB, temp

  sei

loop: rjmp loop ;  Endlosschleife

int0_handler:
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit
    ldi     ZH,high(Sprungtabelle)
    add     ZL,status                   ; Index addieren, 16 Bit
    ldi     status,0
    adc     ZH,status
    ijmp                                ; indirekter Sprung in 
Sprungtabelle

Sprungtabelle:
    rjmp  Lichtan    ; 1. Zustand
    rjmp  Lichtaus    ; 2. Zustand


Lichtan:  sbi PORTB, 0   ; set bit PB0 -> Licht an
    reti

Lichtaus:  cbi PORTB, 0  ; set bit PB0 -> Licht aus
    reti


Was mach ich falsch ?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Was mach ich falsch ?

Erstmal, entweder '.def  status  =r16' und dann nur status verwenden und 
kein r16.

Du hast nirgends eine Stelle, in der du 'status' änderst. Was soll dann 
deiner Meinung nach passieren?

MfG Spess

Autor: Mikki Merten (mmerten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hier ist der Fehler:

falsch:
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit
    ldi     ZH,high(Sprungtabelle)

richtig:
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit
    ldi     ZH,high(Sprungtabelle*2)

FLASH ist beim AVR 16 Bit breit

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>hier ist der Fehler:

Nein. Da verwechselst du etwas. Das ist kein LPM.

MfG Spess

Autor: Mikki Merten (mmerten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt ist ja nen jump via ZH:ZL --> PC

Aber das mit dem Status ist schon richtig, wird weder initialisiert noch
bei jedem Interrupt zwischen 0 - 1 getoggelt ;)

Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hhm ok ich hab grad mal einige sachen ausprobiert aber ohne erfolg

steht das
    ldi     ZL,low(Sprungtabelle)     ; Tabellenzeiger laden, 16 Bit
    ldi     ZH,high(Sprungtabelle)

an der richtigen Stelle oder sollte das besser in die main-Routine mit 
rein?

ich kann den den status mit inc status ändern aber wie springt der dann 
wieder zurück?

Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wahr gestern abend wohl schon zu spät.
.include "tn13def.inc"

.def  status  =r16
.def  temp  =r17

.org 0x0000
         rjmp main          ; Reset Handler
.org 0x0001
         rjmp int0_handler      ; IRQ0 Handler

main:
        ldi temp, LOW(RAMEND)   ; LOW-Byte der obersten RAM-Adresse
        out SPL, temp

  ldi r16, 0b00011001  ; PB0, PB3 und PB4 sind Ausgänge PB1 ist Eingang
  out DDRB, temp

        ldi temp, (1<<ISC00)    ; INT0 auf Low level konfigurieren
        out MCUCR, temp    ; und weg damit... 

  ldi temp, (1<<INT0)  ; INT0 enablen
  out GIMSK, temp    ; und weg damit...

  ldi r16, 0b00000000  ; Ertsmal alles aus
  out PORTB, temp
  
  ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit
      ldi     ZH,high(Sprungtabelle*2)
  
  ldi status, 0    ; lade 0 ins statusregister
  
  sei

loop: rjmp loop ;  Endlosschleife 

int0_handler:
  tst status            ; teste status auf 0
  breq Lichtan          ; wenn 0 dann spring zu Lichtan
  rjmp Lichtaus         ; wenn nicht 0 dann Lichtaus

Sprungtabelle:
    .dw Lichtan, Lichtaus    ; 1. und 2. Zustand


Lichtan:  sbi PORTB, 0   ; set bit PB0 -> Licht an
    ldi status, 1  ; set status = 1
    reti

Lichtaus:  cbi PORTB, 0  ; set bit PB0 -> Licht aus
    ldi status, 0  ; set status = 0
    reti  
  

Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Code funktioniert jetzt. Könnt ihr mal drüber schauer und kritik 
äußern?

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

Bewertung
0 lesenswert
nicht lesenswert
Jens schrieb:

>
>         ldi temp, (1<<ISC00)    ; INT0 auf Low level konfigurieren
>         out MCUCR, temp    ; und weg damit...

Low Level?
Dir ist bewusst, dass dein Interrupt damit ausgelöst wird, solange 
deine Taste gedrückt wird. Denn solange die Taste gedrückt ist, hat der 
Pin Low Level und damit wird ständig ein Interrupt ausgelöst.

Wenn du das willst ist es ok.
Wenn du aber einen Tastendruck auswerten willst, dann musst du die 
Flanke detektieren  ...

... wenn da nicht das Tastenprellen wäre. Generell sind Tasten an einem 
Interrupt keine gute Idee. Mit einer Ausnahme: wenn die Taste den µC aus 
dem Tiefschlaf aufwecken muss. Das geht nur über Interrupt.

>
>   ldi temp, (1<<INT0)  ; INT0 enablen
>   out GIMSK, temp    ; und weg damit...
>
>   ldi r16, 0b00000000  ; Ertsmal alles aus
>   out PORTB, temp
>
>   ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit
>       ldi     ZH,high(Sprungtabelle*2)

Wozu brauchst du jetzt noch die Sprungtabelle?
Du verwendest sie ja gar nicht mehr!

>
>   ldi status, 0    ; lade 0 ins statusregister
>
>   sei
>
> loop: rjmp loop ;  Endlosschleife
>
> int0_handler:
>   tst status            ; teste status auf 0
>   breq Lichtan          ; wenn 0 dann spring zu Lichtan
>   rjmp Lichtaus         ; wenn nicht 0 dann Lichtaus
>
> Sprungtabelle:
>     .dw Lichtan, Lichtaus    ; 1. und 2. Zustand
>
>
> Lichtan:  sbi PORTB, 0   ; set bit PB0 -> Licht an
>     ldi status, 1  ; set status = 1
>     reti
>
> Lichtaus:  cbi PORTB, 0  ; set bit PB0 -> Licht aus
>     ldi status, 0  ; set status = 0
>     reti

Ob es schlau ist dem zum Interrupthandler gehörenden reti in 2 Pfaden zu 
verstecken, wäre zumindest debatierbar. Grundsätzlich ist es von der 
Logik her ok, aber so etwas artet ganz schnell zum Wartungs-Albtraum 
aus.

Hinweis: mit einem XOR (im AVR Assembler heißt das dann: EOR) kann man 
ganz einfach ein einzelnes Bit toggeln (also von 0 auf 1 und umgekehrt 
schalten).

>     ldi status, 1  ; set status = 1
Solche Kommentare kannst du dir schenken. Da steht im Kommentar genau 
dasselbe wie im Code. Das hier status auf 1 gesetzt wird, sehe ich auch 
im Code. Die Frage ist: warum wird hier status auf 1 gesetzt?
Kommentiere nicht das wie, sondern das warum ...

> Lichtaus:  cbi PORTB, 0  ; set bit PB0 -> Licht aus
... und wenn du kommentierst (dieser Kommentar ist von der Sache her 
ok), dann achte darauf, dass die Kommentare richtig sind. Hier wird PB0 
nicht gesetzt, sondern gelöscht.  -> hättest du das wie (setzen/löschen) 
aus dem Kommentar heraus gelassen, wäre der Kommentar richtig.

> Lichtaus:  cbi PORTB, 0  ; Licht aus

Hier erzählt mir der Kommentar warum etwas passiert: Die Anweisung ist 
dazu da, um das Licht zu löschen. Die Anweisung selbst erzählt mir, wie 
das gemacht wird: indem am Port B das Bit 0 gelöscht wird.

Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey Karl Heinz

>Dir ist bewusst, dass dein Interrupt damit ausgelöst wird, solange
>deine Taste gedrückt wird. Denn solange die Taste gedrückt ist, hat der
>Pin Low Level und damit wird ständig ein Interrupt ausgelöst.

Ja, ich dachte der Tiny hat ja nur INT0 daher der wahl zwischen 
Low-level und "Jede änderung". (Für was hättest du dich entshieden?)

>Generell sind Tasten an einem
>Interrupt keine gute Idee. Mit einer Ausnahme: wenn die Taste den µC aus
>dem Tiefschlaf aufwecken muss. Das geht nur über Interrupt.

Sondern? was wird sonst allgemein für Tasten verwendet?

>Wozu brauchst du jetzt noch die Sprungtabelle?
>Du verwendest sie ja gar nicht mehr!

Stimmt da hab ich mich selbst ausgetrickst, weil ich unbedingt die 
Sprungtabellen verwenden/verstehen wollte.

Stimmt: Variable XOR mit 1 verknüpft negiert ja das Ergebniss.

Ich werd heut abend nochmal versuchen eine version mit Sprungtabellen zu 
schreiben.

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

Bewertung
0 lesenswert
nicht lesenswert
Jens schrieb:

> Ja, ich dachte der Tiny hat ja nur INT0 daher der wahl zwischen
> Low-level und "Jede änderung". (Für was hättest du dich entshieden?)

Was sagt das Datenblatt.
Hab jetzt nicht nachgesehen, aber die Megas können auch auf 
Flankenwechsel (also Übergang von 0 auf 1, bzw umgekehrt) mit einem 
Interrupt reagieren.
>
>>Generell sind Tasten an einem
>>Interrupt keine gute Idee. Mit einer Ausnahme: wenn die Taste den µC aus
>>dem Tiefschlaf aufwecken muss. Das geht nur über Interrupt.
>
> Sondern? was wird sonst allgemein für Tasten verwendet?

Portpin als ganz normaler Eingang und in regelmässigen Zeitabständen 
nachsehen, ob sich der Pegel geändert hat.

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.