Forum: Mikrocontroller und Digitale Elektronik Sprungtabelle per externen Interrupt


von Jens (Gast)


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 ?

von spess53 (Gast)


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

von Mikki M. (mmerten)


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

von spess53 (Gast)


Lesenswert?

Hi

>hier ist der Fehler:

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

MfG Spess

von Mikki M. (mmerten)


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 ;)

von Jens (Gast)


Lesenswert?

hhm ok ich hab grad mal einige sachen ausprobiert aber ohne erfolg

steht das
1
    ldi     ZL,low(Sprungtabelle)     ; Tabellenzeiger laden, 16 Bit
2
    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?

von Jens (Gast)


Lesenswert?

Das wahr gestern abend wohl schon zu spät.
1
.include "tn13def.inc"
2
3
.def  status  =r16
4
.def  temp  =r17
5
6
.org 0x0000
7
         rjmp main          ; Reset Handler
8
.org 0x0001
9
         rjmp int0_handler      ; IRQ0 Handler
10
11
main:
12
        ldi temp, LOW(RAMEND)   ; LOW-Byte der obersten RAM-Adresse
13
        out SPL, temp
14
15
  ldi r16, 0b00011001  ; PB0, PB3 und PB4 sind Ausgänge PB1 ist Eingang
16
  out DDRB, temp
17
18
        ldi temp, (1<<ISC00)    ; INT0 auf Low level konfigurieren
19
        out MCUCR, temp    ; und weg damit... 
20
21
  ldi temp, (1<<INT0)  ; INT0 enablen
22
  out GIMSK, temp    ; und weg damit...
23
24
  ldi r16, 0b00000000  ; Ertsmal alles aus
25
  out PORTB, temp
26
  
27
  ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit
28
      ldi     ZH,high(Sprungtabelle*2)
29
  
30
  ldi status, 0    ; lade 0 ins statusregister
31
  
32
  sei
33
34
loop: rjmp loop ;  Endlosschleife 
35
36
int0_handler:
37
  tst status            ; teste status auf 0
38
  breq Lichtan          ; wenn 0 dann spring zu Lichtan
39
  rjmp Lichtaus         ; wenn nicht 0 dann Lichtaus
40
41
Sprungtabelle:
42
    .dw Lichtan, Lichtaus    ; 1. und 2. Zustand
43
44
45
Lichtan:  sbi PORTB, 0   ; set bit PB0 -> Licht an
46
    ldi status, 1  ; set status = 1
47
    reti
48
49
Lichtaus:  cbi PORTB, 0  ; set bit PB0 -> Licht aus
50
    ldi status, 0  ; set status = 0
51
    reti

von Jens (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


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.

von Jens (Gast)


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.

von Karl H. (kbuchegg)


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.

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.