Forum: Mikrocontroller und Digitale Elektronik Taster-Entprellung am Tiny13 in Assembler


von Rolf H. (flash01)


Lesenswert?

wie o.g. suche ich eine Lösung für dieses Projekt.
Schlau gemacht habe ich mich im Tutorial "Entprellen"
und den vielen Threads.
Es läuft immer wieder auf internes Interrupt hinaus.
Da ich das realisieren könnte, habe ich den vielen Profis
im Forum zu verdanken. Das ist nicht das Problem!
Aber...ich wollte eigentlich eine ISR für eine andere Anwendung
im gleichen Quellcode verwenden.
Ist es denn möglich, 2 Interrupts zu realisieren?

Oder gäbe es evtl. ein Befehl wie "Wait" in Bascom
um eine Zeitverzögerung einzubauen?

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>Es läuft immer wieder auf internes Interrupt hinaus.

Nein.

http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:

> Aber...ich wollte eigentlich eine ISR für eine andere Anwendung
> im gleichen Quellcode verwenden.
> Ist es denn möglich, 2 Interrupts zu realisieren?

Niemand schreibt vor, dass eine Timer-Interrupt Routine nur eine Aufgabe 
erfüllen darf.
Entprellen ist insofern anspruchslos, als es nicht besonders 
zeitkritisch ist. Ob der Entprellcode alle 4 oder alle 40ms ausgeführt 
wird, spielt keine wirklich große Rolle. D.h. beim Timing, wie oft bzw. 
in welchen Zeitabständen die ISR aufgerufen wird, orientierst du dich 
erst mal an deiner anderen Anwendung. Und dann rechnest du dir aus, nach 
dem wievielten Mal des ISR-Aufrufs dann auch wieder die Portpins zum 
Entprellen abgefragt werden, damit du so ungefähr in das benötigte 
Zeitfenster zum Entprellen kommst. Dieses Zeitfenster ist groß genug, 
dass man es praktisch immer ohne Probleme schafft, da rein zukommen.

von Hannes L. (hannes)


Lesenswert?

Taster-Entprellung am Tiny13 in Assembler

Hier müsstest Du ein Beispiel in verschiedenen Varianten finden, das auf 
PeDas Entprellalgorithmus basiert. Es ist zwar für den Tiny15 
geschrieben, da es den Tiny13 damals noch nicht gab. Die 
Tastenentprellung läuft auch auf em Tiny13, das ganze Programm 
allerdings nicht.

http://www.hanneslux.de/avr/divers/melody/melody01.html

...

von Rolf H. (flash01)


Lesenswert?

oh..weia!
hatte nicht mit so viel Aufwand gerechnet.
zu spess: wieso schreibst Du "nein" ist ja doch mit Interrupt.
Aber es schadet nicht neue Befehle wie mov und andere kennen zulernen.
Ich weiß nur aus der Franziszeit mit Bascom, daß die Leute eine
simple einfache Routiene mit ihren Wait20 Befehl realisierten.

Erst mal ein Dankeschön

Rolf

von Rolf H. (flash01)


Lesenswert?

he Leute,
bin mit meiner Entprellerei schon weiter gekommen.
Stehe aber jetzt vor einer Routiene, bei der mir das
Tutorial oder Datenblatt nicht weiter helfen kann.

Also, wenn ich vorgebe:

pause:   ldi   r17,0b11110000
warte:   dec   r17
         sbrc  r17
         rjmp  warte
         ret
kommt error: Wrong number of operands (falsche Anzahl v. Operanden).
Der Operand ist doch 3xr17 und warte.
gebe ich vor
pause:   ldi   r17,0b11110000
warte:   dec   r17
         tst   r17
         brne  warte
         ret
ist die Welt in Ordnung
mh... wo steckt bei mir der Überlegungsfehler.
Dachte schon dieses 3xr17 hintereinander verträgt sich nicht,
aber kann doch nicht sein!
Dieses Wrong xxxx habe ich nun schon oft erlebt!

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>Der Operand ist doch 3xr17 und warte.

SBRC (Skip if Bit in Register is Cleared) braucht 2 Argumente: Das 
Register und die Bitnummer -> sbrc r17,3 .

>warte:   dec   r17
>         tst   r17
>         brne  warte

Das tst r17 kannst du dir sparen da das Z-Flag schon durch dec r17 
gesetzt wird.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

> Ich weiß nur aus der Franziszeit mit Bascom, daß die Leute
> eine simple einfache Routiene mit ihren Wait20 Befehl realisierten.

Und genau so hat die dann auch funktioniert: simpel und einfach und im 
Extremfall gar nicht.

> hatte nicht mit so viel Aufwand gerechnet.

Alles eine Frage der Sichtweise. Für das Gebotene finde ich den Aufwand 
sogar erstaunlich gering.

von Rolf H. (flash01)


Lesenswert?

danke für Eure Antworten!
Spess...werde ich heute noch austesten!

Hast Recht..Karl Heinz, deshalb habe ich das Bascom schnellstens
wieder verlassen.
Jahre liegt es zurück, als ich mit den Pics rumgefummelt
habe und einen guten Lehrherr zur Seite hatte...ich meine,
er hieß Thomas Ehlers und da lief auch alles in Assembler.

Grüße

Rolf

von Hannes L. (hannes)


Lesenswert?

Rolf Hegewald schrieb:
> Aber...ich wollte eigentlich eine ISR für eine andere Anwendung
> im gleichen Quellcode verwenden.

Was soll es den überhaupt mal werden?

Ich frage deshalb, weil man viele Dinge, die eine Zeitbasis benötigen, 
ganz gut mit der PeDa-Tastenentprellung kombinieren kann.

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
da ich viele Ideen mit einer Modelleisenbahn (H0) habe,
interessiert mich das Thema.
Hier werden oft Taster-Umschaltungen, z.B. für Weichen, Signale usw.
benötigt.
Aber was ist denn "PeDa-Tastenentprellung" noch nie gehört.

Aber bitte keine Basic-Befehle...nur in Assembler!

Grüße

Rolf

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:
> Hallo Hannes,
> da ich viele Ideen mit einer Modelleisenbahn (H0) habe,
> interessiert mich das Thema.
> Hier werden oft Taster-Umschaltungen, z.B. für Weichen, Signale usw.
> benötigt.
> Aber was ist denn "PeDa-Tastenentprellung" noch nie gehört.

PeDa steh für "Peter Dannegger". Er hat diese Entprellung vor Jahren 
hier gepostet und sie hat sich zur Standardlösung entwickelt, wenn man 
Tasten behandeln möchte.
Sie braucht fast keine Resourcen und funktioniert erstklassig. Wenn nur 
mehr Profis in den Firmen da draussen diese Entprellung benutzen würden, 
die Welt wäre eine bessere und man müsste sich nicht mehr mit nicht oder 
nur halb funktionierenden Tasten auf diversen Fernsteuerungen, 
Liftaufzügen, Automaten etc. rumärgern.

> Aber bitte keine Basic-Befehle...nur in Assembler!

Du hast sie beim Hannes im Code gesehen?


Im Kern besteht sie aus einem Teil, der in eine Timer-Interrupt Routine 
hineinkommt. In welchen Zeitabständen diese Interrupt Routine aufgerufen 
wird ist ziemlich unkritisch. Von ca 4ms bis ca 40ms ist alles möglich.

Hier ist dieser Kern in dem von Hannes verlinkten Code
1
...
2
TIM0_OVF:
3
...
4
Tastenabfrage:  ;Entprellroutine (geklaut bei Peter Dannegger...)
5
 in xl,tap      ;Tastenport einlesen (gedrückt=L)
6
 ...
7
 eor xl,tas     ;nur Änderungen werden H
8
 and tz0,xl     ;Prellzähler unveränderter Tasten löschen (Bit0)
9
 and tz1,xl     ;Prellzähler unveränderter Tasten löschen (Bit1)
10
 com tz0        ;L-Bit zählen 0,2,->1, 1,3,->0
11
 eor tz1,tz0    ;H-Bit zählen 0,2,->tz1 toggeln
12
 and xl,tz0     ;Änderungen nur dann erhalten, wenn im Prellzähler
13
 and xl,tz1     ;beide Bits gesetzt sind (Zählerstand 3)
14
 eor tas,xl     ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
15
 and xl,tas     ;nur (neu) gedrückte Tastenbits bleiben erhalten
16
 or tfl,xl      ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
17
 ;in "tas" steht jetzt der gültige Tastenzustand,
18
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten...

dieser Code wird vom Timer Interrupt alle paar Millisekunden 
abgearbeitet und erledigt das Einlesen und Entprellen von bis zu 8 
Tastern an einem Eingangsport.

Der Timer muss dazu natürlich initialisiert werden, so dass er die 
entsprechenden Interrupts auslöst.

Dazu muss man noch wissen, wie man dann die Tasten tatsächlich abfrägt, 
die dieser Code als gedrückt identifiziert. Und das geht so (wieder aus 
dem Code von Hannes)
1
mainloop:           ;Hauptschleife
2
 ....
3
 sbrc tfl,t1                ;Taste 1 betätigt gewesen? - nein...
4
 rjmp taste1                ;ja, abarbeiten...
5
 ...
6
7
8
taste1:             ;Taste 1 wurde betätigt
9
 cbr tfl,1<<t1              ;Tastenflag löschen
10
 ...  ; mach was immer es bei Betätigen der Taste 1 zu tun gibt

also alles in allem eine sehr einfache Sache.

Mach dir um den Code in der ISR keine Gedanken. Der ist trickreich und 
nicht leicht zu verstehen. Macht aber nichts, den kannst du einfach so 
übernehmen. Du musst nur die Konstanten entsprechend anpassen.
1
;Definition des Ports, an dem die Tasten angeschlossen sind. Im Falle des
2
;Tiny15 nicht unbedingt erforderlich, bei anderen AVRs aber sehr nützlich.
3
.equ tap=pinb           ;Eingang Tastenport
4
5
6
;Definition der Bits, die die 4 Taster repräsentieren:
7
.equ t1=pb0             ;Taster 1
8
.equ t2=pb2             ;Taster 1
9
.equ t3=pb3             ;Taster 1
10
.equ t4=pb4             ;Taster 1

und für die benutzten Register Assignments machen
1
.def tz0=r4         ;Tasten-Prellzähler Bit 0
2
.def tz1=r5         ;Tasten-Prellzähler Bit 1
3
.def tas=r6         ;Tasten-Status (entprellt)
4
.def tfl=r23        ;Tastenflags (neu gedrückte Tasten)

(und das was jetzt noch alles fehlt, findet sich ebenfalls in Hannes 
Code)


Und das Original findet sich hier
Entprellung

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Karl Heinz Buchegger schrieb:
> ;Definition der Bits, die die 4 Taster repräsentieren:
> .equ t1=pb0             ;Taster 1
> .equ t2=pb2             ;Taster 1
> .equ t3=pb3             ;Taster 1
> .equ t4=pb4             ;Taster 1

Aua, muss man das erst zitiert bekommen, damit man sowas merkt???
Das sollte natürlich nicht alles Taster 1 sein...

Karl Heinz Buchegger schrieb:
> Und das Original findet sich hier
> Entprellung

Zu der Zeit, als ich das Bimmel-Programm schrieb, allerdings noch nicht. 
Ich hatte die Infos von hier:
Beitrag "Tasten entprellen - Bulletproof"

Rolf Hegewald schrieb:
> Hier werden oft Taster-Umschaltungen, z.B. für Weichen, Signale usw.
> benötigt.

Naja, ich frug nach, um Dir evtl. ein Programm für den Tiny13 zu 
schreiben, das Zeitbasis und Tastenentprellung enthält und das Du als 
"Rohling" für eigene Versuche und Implementierungen verwenden kannst.

Rolf Hegewald schrieb:
> da ich viele Ideen mit einer Modelleisenbahn (H0) habe,
> interessiert mich das Thema.

Da hänge ich Dir doch gleich mal eine drei Jahre alte Spielerei mit dem 
Mega8 an. Es realisiert eine Schranke der DR aus Erichs Zeiten. Auch da 
ist Peters Tastenentprellung drin, allerdings etwas modifiziert. Der 
DCC-Decoder war mal geplant wurde aber bisher noch nicht implementiert.

...

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Hier mal noch ein kleines Progrämmchen für den Tiny13. Es hat allerdings 
keine Tastenentprellung, sondern steuert anhand eines Logikpegels ein 
Modellbauservo. Je nachdem ob H oder L anliegt, wird das Servo in eine 
der beiden per Poti einstellbaren Positionen gefahren. Es dient zum 
Steuern des Entkupplerservos von akkubetriebenen 
Gartenbahn-Lokomoteufelchen.

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
zu meiner Schande muß ich gestehen, daß ich nach PeDa gefragt habe,
und seit dem 6. Januar daran arbeite bzw. es zu verstehen versuche.
Ich hatte an dem Tag die Qelldatei gleich runter gezogen.
Die vielen .def, .equ, und die Schaufelei in der ISR haben mich
erst mal umgehauen.
Versuchte auch zu ergründen, warum in der ISR
get8key:
steht. Nehme es erst mal so hin.
Hab das ganze auf den Tiny13 gebusselt und hänge noch an
error: Wrong number of operands.
Aber Spess hatte mir schon Tips gegeben, das kriege ich raus.
Ein Mega8 bedient hier einen Schwenkkran der Fa. Märklin
der ca. 20 Jahre alt ist.
Das ganze noch mit Bascom geschrieben.
Ein Atari MEGA1 aus dem Jahre 1983 bedient seit 1993
die Eisenbahnanlage. Linksseitig verfügt er über eine Schnittstelle,
auf dessen Leitungen im Multiplexverfahren die Adresse bzw.
die Daten liegen. Die Auswertung über von mir erstelltes Interface
war die Wahnsinnsarbeit.
Alle Signale, Weichen, Schranke, Drehscheibe Fahrstufen werden
mit Hilfe von erstellten Proceduren über das alte GFA-Basic erzeugt.
Über manche "IF/ELSE/ENDIF" Schleife habe ich graue Haare bekommen.

So, das wars erst mal, der Tag könnte 48 Stunden haben.

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>Aber Spess hatte mir schon Tips gegeben, das kriege ich raus.

Der Tip war aber nur auf den Syntax des Befehls bezogen. Für eine 
Warteschleife ist SBRC ungeeignet bzw. nur sehr bedingt geeignet.

MfG Spess

von Hannes L. (hannes)


Lesenswert?

Rolf Hegewald schrieb:
> Hallo Hannes,
> zu meiner Schande muß ich gestehen, daß ich nach PeDa gefragt habe,
> und seit dem 6. Januar daran arbeite bzw. es zu verstehen versuche.
> Ich hatte an dem Tag die Qelldatei gleich runter gezogen.
> Die vielen .def, .equ,

Das sind doch nur Alias-Namen, die den Code übersichtlicher und 
verständlicher machen (sollen). Mit .def tauft man egister, mit .equ 
kreiert man Konstanten.

> und die Schaufelei in der ISR haben mich

Welche Schaufelei?? Da werden einfach nur 8 Zweibit-Zähler (als 
Entprell-Zähler für jede der 8 Tasten separat) gleichzeitig und 
unabhängig voneinander behandelt. Dies dient dazu, einen Pegelwechsel 
erst dann "durchzulassen", wenn er 4 mal hintereinander erkannt wurde. 
Damit wird sowohl das Betätigen, als auch das Loslassen der Tasten nur 
dann akzeptiert, wenn der neue Pegel (Zustand) 4 Runden hintereinander 
identisch war.

> erst mal umgehauen.
> Versuchte auch zu ergründen, warum in der ISR
> get8key:
> steht. Nehme es erst mal so hin.

Das ist ein Label. Also ein Name für die Adresse, an der der Code steht. 
Das ermöglicht dem Programmierer, die Routine mit ihrem Namen 
aufzurufen, ohne die genaue Adresse zu kennen. Der Befehl "RJMP get8key" 
setzt den Programmcounter dann auf die Adresse, an der das Label steht. 
Oftmals vergibt man auch Labels, die gar nicht als Sprungziel gebraucht 
werden, Hintergrund ist die Erhöhung der Lesbarkeit.

> Hab das ganze auf den Tiny13 gebusselt und hänge noch an
> error: Wrong number of operands.

Nunja, Du wolltest einen Skip-Befehl zum Rechnen missbrauchen. Und der 
wehrt sich nun. Der Skip-Befehl überspringt den nächsten Befehl, wenn 
die Bedingung wahr ist, also im Byte xy das Bit z den richtigen Wert 
hat. Dies braucht 2 Parameter. Du wolltest aber damit subtrahieren oder 
vermindern und hast nur einen Parameter (r17) angegeben. Das wird also 
nix.

Ich empfehle Dir, Dir aus dem Datenblatt die Tabelle "Instruction Set 
Summary" auszudrucken und diese als Arbeitsblatt zu benutzen. Gleiches 
gilt für die Tabelle mit den I/O-Adressen und deren Namen für die Bytes 
und Bits. Das ist für den Einstieg eine sehr gute Hilfe und schützt vor 
Schreibfehlern.

> Aber Spess hatte mir schon Tips gegeben, das kriege ich raus.

Ja klar... Noch'n Tip: In AVR-Studio mal die F1-Taste drücken, wenn der 
Cursor auf einem ASM-Befehl steht.

> Ein Mega8 bedient hier einen Schwenkkran der Fa. Märklin
> der ca. 20 Jahre alt ist.
> Das ganze noch mit Bascom geschrieben.

Ja warum nicht?

> Ein Atari MEGA1 aus dem Jahre 1983 bedient seit 1993
> die Eisenbahnanlage.

Einem Commodore Plus/4 aus derselben Epoche habe ich 1991 mal 
beigebracht, die Fräsmaschine eines Feinmechanikers und Großuhrmachers 
(Turmuhren) zu steuern.

> Linksseitig verfügt er über eine Schnittstelle,
> auf dessen Leitungen im Multiplexverfahren die Adresse bzw.
> die Daten liegen. Die Auswertung über von mir erstelltes Interface
> war die Wahnsinnsarbeit.

Ich habe sowas ähnliches mal für Pauls PC gemacht:
http://www.hanneslux.de/mobast/index.html
Ist aber schon etwas neuer...

> Alle Signale, Weichen, Schranke, Drehscheibe Fahrstufen werden
> mit Hilfe von erstellten Proceduren über das alte GFA-Basic erzeugt.
> Über manche "IF/ELSE/ENDIF" Schleife habe ich graue Haare bekommen.

Ich bekomme graue Haare, wenn man "If/Else/Elseif/Endif" als Schleife 
bezeichnet, das ist nämlich keine Schleife, sondern eine Verzweigung.
:-))

>
> So, das wars erst mal, der Tag könnte 48 Stunden haben.

Und dann müsste man auch noch Frühstück und Mittag durchmachen...

>
> Grüße
>
> Rolf

...

von Karl H. (kbuchegg)


Lesenswert?

> Hab das ganze auf den Tiny13 gebusselt und hänge noch
> an error: Wrong number of operands.

Was. Etwa immer noch?

Dazu brauchst du in erster Linie keinen Tip, sondern einen Druck auf F1, 
wenn der Cursor im AVR-Studio auf dem angemeckerten Befehl steht.

Dann kommt die Hilfe hoch, praktischerweise genau zu dem Befehl über dem 
der Cursor steht (und der die Fehlermeldung ausgelöst hat), und dann 
sieht man in der Hilfe: sbrc benötigt 2 Argumente. Ist ja auch irgendwie 
logisch. Der Befehl heißt in Longform "Skip if Bit in Register is 
Cleared", also in etwa "Überspringe (den nächsten Befehl) wenn ein 
bestimmtes Bit in einem bestimmten Register den Wert 0 hat".
Was also wird der Befehl für Angaben benötigen?
Einfach: welches Bit in welchem Register.

Bei dir steht

   sbrc  r17

die Frage "welches Register" ist damit beantwortet: r17.
Aber welches Bit soll denn getestet werden?
Diese Angabe fehlt. Und genau darüber meckert die Fehlermeldung "Wrong 
number of Arguments" - "Falsche Anzahl an Argumenten". Für SBRC sind 2 
Angaben notwendig, du hast nur 1.

Jetzt gibt es 2 Möglichkeiten
* entweder der SBRC ist überhaupt schon mal der falsche Befehl
  dazu muss man sich die Logik an der Stelle ansehen und entscheiden
  was an dieser Stelle eigentlich höchst wahrscheinlich passieren
  sollte. Dann kann man entscheiden ob der SBRC zb einfach nur ein
  Tippfehler war und eigentlich ein ganz anderer Befehl hätte sein
  sollen
* oder der SBRC ist grundsätzlich schon richtig, nur ist eben
  eine der notwendigen Angaben nicht gemacht worden.

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Rolf Hegewald schrieb:
> da ich viele Ideen mit einer Modelleisenbahn (H0) habe,
> interessiert mich das Thema.
> Hier werden oft Taster-Umschaltungen, z.B. für Weichen, Signale usw.
> benötigt.

> ...

> Aber bitte keine Basic-Befehle...nur in Assembler!

Ja, nur Assembler, ist mir auch lieber als Basic, zumindest auf dem AVR 
und besonders dann, wenn es zeitkritisch wird und Interrupts im Spiel 
sind.

Ich habe Dir mal PeDas Tastenentprellung in ein Tiny13-Programm 
eingebaut. Da Du keine konkrete Anwendung genannt hast, habe ich die 
Servosteuerung drin gelassen. Das Servo wird jetzt durch zwei Taster hin 
und her bewegt, wobei beide Positionen per Poti einstellbar sind.

Das Programm soll Dir erstmal zur Analyse dienen und bei der 
Einarbeitung in AVR-ASM helfen.

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
ich habe mich erst mal auf eine Taste am Tiny13 konzentriert.
D.h. erst mal einfach anfangen.
Das ganze sieht so aus:

; Projekttiny13

; Datei: debbug02.asm    ;Datum: 12.01.2012

; PORTB,Pb0: Ausgang PINB,PB0
; PORTB,Pb3: Eingang


;AVR: Tiny 13

        .INCLUDE  "tn13def.inc"  ; Deklarationen für Tiny13
        .EQU    takt = 1000000  ; Systemtakt 1 MHz
        .DEF    akku = r16      ; r16 in akku benannt


  rjmp    start           ; Reset-Einsprung
  .ORG  OVF0addr    ;Interrupt-Vektor
  rjmp  TIM0_OVF    ;Sprung zur ISR



start:  ldi     akku,LOW(RAMEND); Stapel anlegen
        out     SPL,akku        ;
        ldi     akku,0b00000001 ; Bitmuster 0000 0001
         out     DDRB,akku       ; PortB,PB0 ist Ausgang
  ldi    akku,0b11111110  ; PB1-PB7 = PULLUP
  out    PORTB,akku

;Timer0 initialis.
      ldi      akku,1<<CS01|1<<CS00   ;Timer mit Vorteiler 64
      out      TCCR0B,akku
      ldi      akku,1<<TOIE0          ;Timer Overflow Interrupt
      out      TIMSK0,akku
   sei                             ; und los gehts: Timer frei

loop:  nop
ein:  sbic  PINB,3      ;sbic, springe wenn PINB,B3=LOW
  rjmp  ein
  ldi     akku,1       ; Bitmuster 0000 0001
         out     PORTB,akku      ; PB0 = HIGH
  rcall  pause

aus:  sbic  PINB,3
  rjmp  aus
  ldi  akku,0
  out  PORTB,akku   ; PB0 = LOW
  rcall  pause
  rjmp  loop

pause:  ldi      r17,0b00111111
warte:  tst  r17
    brne  warte  ;springe zu warte,wenn r17 kein Null
    ret

;Interrupt_ISR
TIM0_OVF:      push   r16       ; Timer0 Overflow
              in   r16,SREG
              push  r16
       dec  r17
              pop   r16
              out   SREG,r16
              pop  r16
       reti
Nun kommt etwas eigenartiges.
Nach anstecken der Vcc wartet der Tiny
in der Abfrage
sbic   PINB,3   die LED an PB0 bleibt dunkel.
Drücke ich die Taste, leuchtet die LED und fängt an zu blinken.

Vcc wieder entfernt und PB3 extern mit 10K gegen Vcc belegt,
mh.. dann ist die Welt in Ordnung.
D.h. die LED läßt sich über die Taste sauber EIN/AUSschalten.
Ich habe das Tutorial über Pullup-Widerstand studiert.
Da ging man natürlich von PORTB und PORTD aus, was der Tiny13 nun
mal nicht hat.
Ich kann mir das mit meiner Pullup Konfiguration nicht erklären.
Wo liegt bei mir der Denkfehler?

Grüße

Rolf

von Hannes L. (hannes)


Lesenswert?

Rolf Hegewald schrieb:
> Hallo Hannes,

Hallo Rolf...

> ich habe mich erst mal auf eine Taste am Tiny13 konzentriert.

Ob eine Taste oder bis zu 8 Tasten, das ist beim Entprellen kein 
Unterschied. Ein Byte hat 8 Bit, somit können 8 Tasten gleichzeitig 
behandelt werden wenn man es richtig macht.

> D.h. erst mal einfach anfangen.
> Das ganze sieht so aus:
>
> ; Projekttiny13
>
> ; Datei: debbug02.asm    ;Datum: 12.01.2012
>
> ; PORTB,Pb0: Ausgang PINB,PB0
> ; PORTB,Pb3: Eingang
>
>
> ;AVR: Tiny 13
>
>         .INCLUDE  "tn13def.inc"  ; Deklarationen für Tiny13
>         .EQU    takt = 1000000  ; Systemtakt 1 MHz

Der Tiny13 kann nicht mit 1 MHz laufen, schau mal ins Datenblatt unter 
Clock Sources. Ab Werk läuft sein Oszillator mit 9,6 MHz, der 
Systemtakt-Vorteiler (CLKPR) ist durch die aktive CLKDIV8-Fuse auf 1 zu 
8 voreingestellt, wodurch ein neuer Tiny13 mit 1,2 MHz arbeitet.

>         .DEF    akku = r16      ; r16 in akku benannt

Ich bin eigentlich froh, dass die AVRs 32 Register haben und möchte 
daher ungern an Akku, X und Y erinnert werden... ;-))

>
>
>   rjmp    start           ; Reset-Einsprung
>   .ORG  OVF0addr    ;Interrupt-Vektor
>   rjmp  TIM0_OVF    ;Sprung zur ISR

Ich mag diese Darstellung nicht, ich halte das Kopieren der kompletten 
Interrupt-Sprungtabelle für übersichtlicher. Das ist aber 
Geschmacksache.

>
>
>
> start:  ldi     akku,LOW(RAMEND); Stapel anlegen
>         out     SPL,akku        ;

Das macht der Tiny13 beim Reset selbst, siehe Datenblatt oder meinen 
Beispiel.

>         ldi     akku,0b00000001 ; Bitmuster 0000 0001
>          out     DDRB,akku       ; PortB,PB0 ist Ausgang
>   ldi    akku,0b11111110  ; PB1-PB7 = PULLUP
>   out    PORTB,akku
>
> ;Timer0 initialis.
>       ldi      akku,1<<CS01|1<<CS00   ;Timer mit Vorteiler 64
>       out      TCCR0B,akku
>       ldi      akku,1<<TOIE0          ;Timer Overflow Interrupt
>       out      TIMSK0,akku
>    sei                             ; und los gehts: Timer frei

Jou stimmt...

>
> loop:  nop

Warum NOP??

> ein:  sbic  PINB,3      ;sbic, springe wenn PINB,B3=LOW
>   rjmp  ein
>   ldi     akku,1       ; Bitmuster 0000 0001
>          out     PORTB,akku      ; PB0 = HIGH

Und schon sind alle PullUp-Widerstände aus, weil Du Nullen 
reingeschrieben hast...

>   rcall  pause
>
> aus:  sbic  PINB,3
>   rjmp  aus
>   ldi  akku,0
>   out  PORTB,akku   ; PB0 = LOW

Und hier werden die PullUps auch deaktiviert.

>   rcall  pause
>   rjmp  loop
>
> pause:  ldi      r17,0b00111111

Worin liegt der Sinn, den Vorladewert eines Timeout-Zählers binär 
anzugeben? Mit dezimaler Eingabe sähe es zwar nicht so wissenschaftlich 
aus, wäre aber viel besser lesbar und viel verständlicher.

> warte:  tst  r17
>     brne  warte  ;springe zu warte,wenn r17 kein Null
>     ret

Kann man machen, ist aber nicht der Sinn des Timers beim Entprellen von 
Tasten.

>
> ;Interrupt_ISR
> TIM0_OVF:      push   r16       ; Timer0 Overflow
>               in   r16,SREG
>               push  r16
>        dec  r17
>               pop   r16
>               out   SREG,r16
>               pop  r16
>        reti

Warum machst Du das so? Der AVR hat 32 Register, da kann man es sich 
leisten, die Register, die in der Mainloop verwenet werden, in Ruhe zu 
lassen. Selbst zum Sichern des SREG kann man ein Exklusivregister 
vereinbaren. Die ganze Push-Pop-Orgie ist hier (in diesem Zusammenhang) 
Unsinn.

> Nun kommt etwas eigenartiges.
> Nach anstecken der Vcc wartet der Tiny
> in der Abfrage
> sbic   PINB,3   die LED an PB0 bleibt dunkel.
> Drücke ich die Taste, leuchtet die LED und fängt an zu blinken.
>
> Vcc wieder entfernt und PB3 extern mit 10K gegen Vcc belegt,
> mh.. dann ist die Welt in Ordnung.

Ha sicher, das hast Du doch auch so programmiert. Wenn Du den internen 
PullUp abschaltest, dann kann er halt nur ein mal wirken...

Der AVR hat wunderbare Befehle zum Setzen und Löschen einzelner Bits in 
einem Teil des I/O-Bereiches (in dem auch die Ports liegen). SBI und CBI 
sind Deine Freunde.

Das bringt Dir aber nicht viel, denn Dein Konzept ist sehr 
gewöhnungsbedürftig und alles Andere als optimal.

Ich gehe mal davon aus, dass Du mein Beispiel der Tastenentprellung mit 
Tiny13 nicht gelesen hast. Naja, schade um die Arbeit bzw. Zeit.

> D.h. die LED läßt sich über die Taste sauber EIN/AUSschalten.
> Ich habe das Tutorial über Pullup-Widerstand studiert.
> Da ging man natürlich von PORTB und PORTD aus, was der Tiny13 nun
> mal nicht hat.
> Ich kann mir das mit meiner Pullup Konfiguration nicht erklären.
> Wo liegt bei mir der Denkfehler?

Vermutlich im Übersehen der Doppelfunktion des Port-Registers. Ist der 
Portpin Eingang, dann wirkt PORTB auf die PullUp-Widerstände (1 
eingeschaltet, 0 ausgeschaltet). Ist der Port Ausgang (mit DDRB 
eingestellt), dann wirkt PORTB auf den Ausgangspegel.

Und willst Du den von außen angelegten Zustand der Portpins erfassen, 
dann musst Du PINB einlesen. Jeder Port hat also 3 Register, DDRx, PORTx 
und PINx...

>
> Grüße
>
> Rolf

...

von Peter D. (peda)


Lesenswert?

Hannes Lux schrieb:
> Ich mag diese Darstellung nicht, ich halte das Kopieren der kompletten
> Interrupt-Sprungtabelle für übersichtlicher. Das ist aber
> Geschmacksache.

Dann sollte man aber auch einen bad-isr-handler aufsetzen, der zeigt, 
daß ein Interrupt ohne Handler aufgerufen wird.
Und dann sollte man trotzdem jedesmal ein .org machen, damit die Adresse 
stimmt.


Peter

von Hannes L. (hannes)


Lesenswert?

Peter Dannegger schrieb:
> Dann sollte man aber auch einen bad-isr-handler aufsetzen, der zeigt,
> daß ein Interrupt ohne Handler aufgerufen wird.

Mach' ich doch auch schon geraume Zeit, zumindest als definierten Punkt 
wo sich das Programm aufhängen soll. Während des Debuggens wird da bei 
Problemen auch mal eine LED eingeschaltet. Die Zeit der RETIs in der 
Sprungtabelle ist schon lange vorbei.

> Und dann sollte man trotzdem jedesmal ein .org machen, damit die Adresse
> stimmt.

Da bin ich kein Freund von, denn die Gefahr, die reservierten Namen 
falsch zu schreiben ist (bei mir!!) größer als die komplette Tabelle 
fehlerhaft zu kopieren. Ich kopiere die Tabelle für jeden benutzten Typ 
einmalig aus dem Datenblatt, passe sie an und speichere sie zur 
Wiederverwendung in einer Textdatei.

Aber ich muss ja nicht für einen Arbeitgeber programmieren, sondern nur 
für eigene kleine Basteleien. Daher muss ich nicht unbedingt alles 
nachmachen, was Profis für sinnvoll halten oder was ihnen vorgeschrieben 
wird. Profis programmieren auch in C, und da komme ich nun gar nicht 
ran...

Gruß nach Bln.
...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
für Deine vielen Hinweise erst mal meinen Dank.
Es fiel mir wie Schuppen von den Augen, als ich erkannte,
was ich mit den PortB anstelle.
Stimmt nicht, wenn Du schreibst, ich hätte das mit den 8 Tasten
nicht durchgelesen "Schade um die Zeit"!
Habe es ausgedruckt, es liegt hier vor mir und versuche es Stück
für Stück zu verarbeiten, bzw. es an einen Tiny2313 anzupassen.
Aber da sind ja auch jede Menge Registerbezeichnungen drinnen
(für r1-r5, r16,r17) ...ich denke, Du magst das nicht, oder habe
ich was falsch gelesen?
Ich bin immer noch Anfänger, bedenke das, mein Alter möchte ich
garnicht erwähnen.
So, ich komme drauf zurück, in einer Stunde will ich zum
Hubschrauberfliegen in einer Turnhalle.

Grüße

Rolf
Vor mir liegen BL-Regler (wenn Dir das was sagt) mit zwei AVR
drauf, von den ich 15 Stück mit SMD1206 bzw. 0805 erstellt habe.
Die Atmel waren natürlich von einen Österreicher programmiert
worden. Will mich nicht mit fremden Federn schmücken.

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Rolf Hegewald schrieb:
> Hallo Hannes,
> für Deine vielen Hinweise erst mal meinen Dank.
> Es fiel mir wie Schuppen von den Augen, als ich erkannte,
> was ich mit den PortB anstelle.

Gut.

> Stimmt nicht, wenn Du schreibst, ich hätte das mit den 8 Tasten
> nicht durchgelesen "Schade um die Zeit"!
> Habe es ausgedruckt, es liegt hier vor mir und versuche es Stück
> für Stück zu verarbeiten, bzw. es an einen Tiny2313 anzupassen.

Tastenentprellung frei nach PeDa auf dem Tiny2313 ist im Anhang. 
Natürlich ist die Entprellung kein Selbstzweck, das Programm macht 
natürlich noch ein paar andere Dinge. Die Entprellung wird also nur so 
nebenbei mit erledigt.

> Aber da sind ja auch jede Menge Registerbezeichnungen drinnen
> (für r1-r5, r16,r17) ...ich denke, Du magst das nicht, oder habe
> ich was falsch gelesen?

Da verwechselst Du was. Spess mag die Registertaufe nicht. ;-))
Bei kleineren überschaubaren Programmen, wo ich einen großen Teil der 
Variablen in (den 32 verfügbaren) Registern halten kann, benutze ich 
schon recht gern Exklusivregister. Und diese bekommen dann auch ihre 
Namen. Das Umbenennen der Register in "Akku", "Temp1" bis "Temp4" mach' 
ich persönlich aber nicht. Da hat eben jeder seine Vorlieben. Das 
Umbenennen in "Akku" suggeriert mir, dass Derjenige damit im Stil des 
6502 arbeiten will, der nur Akku und zwei Indexregister (X, Y) hatte, 
und damit auf die Vorzüge der 32 Register der (meisten) AVRs verzichtet.

Was ich persönlich nicht so mag, sind die Bezeichner für die 
Interruptvektoren. Das hat aber andere Ursachen.

> Ich bin immer noch Anfänger, bedenke das, mein Alter möchte ich
> garnicht erwähnen.

Na etwa eine halbe Generation älter als ich...

> So, ich komme drauf zurück, in einer Stunde will ich zum
> Hubschrauberfliegen in einer Turnhalle.

Viel Spaß...

>
> Grüße
>
> Rolf
> Vor mir liegen BL-Regler (wenn Dir das was sagt) mit zwei AVR
> drauf, von den ich 15 Stück mit SMD1206 bzw. 0805 erstellt habe.
> Die Atmel waren natürlich von einen Österreicher programmiert
> worden. Will mich nicht mit fremden Federn schmücken.

Da kann ich nicht mitreden, ich bin nich so der Nachbauer. Ich backe 
zwar kleine Brötchen, aber meine eigenen...

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
die Tastenentprellung (nur erst mal für eine Taste) funzt
einmalig am Tiny13.
Habe heute Morgen Dein Vorschlag mit den Befehl "sbi bzw. cbi"
gescheckt; das ist ja ne tolle Sache, zumal hier die Inhalte der
anderen Pins nicht beinflußt werden und man nicht jedesmal ein
Register für eine Pinänderung benötigt.
Interessant war auch zu testen, ab wann sich die Umschaltung bei
Tastendruck verheddert.

in der Subroutiene pause: steht
pause:  ldi   r17,0b00001111  ;also 15
warte:  tst   r17 ;Spess meinte, das kann weg,aber dann kommen Probleme!
        brne  warte
        ret

in der ISR steht
TIM0_OVF: push  r16 ;habe den Begriff akku entfernt
          in    r16,SREG
          push  r16
          dec   r17
          pop   r16
          out   SREG,r16
          pop   r16
          reti
mh... diesen Ablauf habe ich von Spess übernommen, habe es
aus einem anderen Thread extra ausgedruckt.
Wie K. H. Buchegger immer wieder schreibt "nicht einfach abschreiben
und übernehmen, erst mal damit beschäftigen"!
Nach meiner Hochrechnung sieht das Ganze bei 1,2 MHz und Prescale
von 64 so aus:
0,833yS x 64 = 53,33yS x 256 = 13,65mS =1Tick x 15 Runden = 204,75mS
(aufgerundet)
Wenn ich jetzt pause:  ldi  r17, auf 0b00000111 verkleinere,
habe ich einen sog. Zufallsgenerator, d. h. dann wäre die Zeit
kleiner als 204,75mS..kann man vielleicht auch mal gebrauchen.

Das wars erst mal,

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

Das

>warte:   dec   r17
>         tst   r17
>         brne  warte

und das (Decrement im Interrupt)

>pause:  ldi   r17,0b00001111  ;also 15
>warte:  tst   r17 ;Spess meinte, das kann weg,aber dann kommen Probleme!
>        brne  warte
>        ret

sind aber zwei Paar Schuhe. Bei letzterem wird durch Sichern von SREG im 
Interrupt das Z-Flag im Hauptprogramm nicht beeinflusst. Da musst du 
das durch 'tst r17' setzen. Im ersten Code wird das durch 'dec r17' 
gesetzt.

>in der ISR steht
>TIM0_OVF: push  r16 ;habe den Begriff akku entfernt
>          in    r16,SREG
>          push  r16  <<<<<<<<
>          dec   r17
>          pop   r16  <<<<<<<<
>          out   SREG,r16
>          pop   r16
>          reti
>mh... diesen Ablauf habe ich von Spess übernommen,....

In dem Fall sind die mit '<<<<<<<' gekennzeichneten Zeilen nicht 
notwendig, da r16 nicht weiter verwendet wird.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

doch...doch r16 als akku benannt, wird schon weiter benutzt.
Blättere mal zurück zur Quelldatei, oder evtl. doch nicht?

Ich muß ehrlich zugeben, daß ich mich mit den PUSH und POP
noch nicht intensiv beschäftigt habe..man hat eben zu viel um
die Ohren. Besser aber so als Langeweile!

Grüße

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>doch...doch r16 als akku benannt, wird schon weiter benutzt.
>Blättere mal zurück zur Quelldatei, oder evtl. doch nicht?

Aber doch nicht in der Interruptroutine. Mit dem ersten 'push r16' ist 
das Register gesichert. Mit dem zweiten 'push r16' schiebst du nur das 
zuvor eingelese SREG auf den Stack. Der Inhalt von r16 ist aber weiter 
der Inhalt von SREG.

>Ich muß ehrlich zugeben, daß ich mich mit den PUSH und POP
>noch nicht intensiv beschäftigt habe..

Dann wird es aber Zeit.

MfG Spess

von Rolf H. (flash01)


Lesenswert?

mh...jetzt hab ich das nochmal heraus gesucht.

Wenn du einen Motor eine bestimmte Zeit in eine Richtung fahren lassen
willst geht das einfach so:
      ldi  r16,2
      out  PORTB,r16  ; Motor einschalten
      ldi r17,10      ; Zeit laden
loop: test r17        ; Zeit abgelaufen?
      brne loop
      ldi r16,0
      out  PORTB,r16 ; Motor ausschalten
      ....

TIMER0_OVF:
      push r16       ; Timer0 Overflow
      in r16,SREG
      push r16

      dec r17

      pop r16
      out SREG,r16
      pop r16
      reti



MfG Spess

aber hier wird r16 doch auch nicht anderweitig verwendet,
so richtig blicke ich noch nicht durch.

von spess53 (Gast)


Lesenswert?

Hi

Stimmt. Dort ist es auch überflüssig. Kann sein, das ich den Code vom 
Interrupt damals teilweise automatisch generiert hatte. Das Augenmerk 
lag auch mehr auf der Warteschleife. Im Prinzip stört es auch nicht. Der 
Interrupt ist nur etwas länger.

Werde versuchen in Zukunft besser aufzupassen.

MfG Spess

von Hannes L. (hannes)


Lesenswert?

Rolf Hegewald schrieb:
> aber hier wird r16 doch auch nicht anderweitig verwendet,
> so richtig blicke ich noch nicht durch.

Das scheint mir auch so.

Und dabei kann man es sich doch wirklich einfacher machen.

Der AVR hat 32 Register, da kommt man bei kleinen Programmen sogar ohne 
RAM aus.

Da kann man es sich leisten, ein unteres (dummes) Register exklusiv für 
die SREG-Sicherung im Interrupt zu verwenden. Da dieses Register 
nirgendwoanders benutzt wird (es dient ja exklusiv der SREG-Sicherung) 
braucht man es auch nicht auf Stack sichern.

Man kann sich auch leisten, für Hauptschleife und Interrupt-Handler 
separate Register zu nutzen, denn dann brauchen auch die in der ISR 
benutzten Register nicht auf Stack gesichert werden, sie sind halt 
exklusiv für diesen Zweck reserviert.

Push-Pop-Orgien sind erst nötig, wenn die Register knapp werden. Das ist 
bei Dir aber noch lange nicht der Fall. Du hast es also nicht nötig, r16 
zum Akku zu erklären und nach 6502-Manier alles mit dem Akku und dem 
Stack zu machen. Für unbenutzte Register gibt es kein Geld zurück.

Rolf Hegewald schrieb:
> "sbi bzw. cbi" gescheckt;

Bitte drucke Dir die Liste "Instruction Set Summary" aus dem Datenblatt 
aus und lege sie als eine Art Arbeitsblatt bzw. Referenz neben Deinen 
Computer...

...

von Rolf H. (flash01)


Lesenswert?

Hallo Hannes,
nun habe ich mich einmal intensiv mit den PUSH und POP
beschäftigt, und muß feststellen, daß ich da irgendwelchen
Quatsch fabriziere.

als Beispiel eine Qelldatei von mir, bei der die ISR
so aussieht (Tiny13)

TIM0_OVF: push r16   ;kopiere Inhalt von r16 auf die von Stapelzeiger SP
                     ;adressierte SRAM Speicherstelle, danach SP-1

          in   r16,SREG ;Inhalt vom Statusregister nach r16 laden
          dec  r17      ;Zeitablauf im Hauptprogramm
          pop  r16      ;erst SP+1, danach lade r16 mit dem
                        ;adressierten SRAM-Byte
          out  SREG,r16 ;Inhalt von r16 in SREG laden
          reti
Ich meine, ich hätte irgendwo gelesen, daß nur die I/O Register
in der ISR gerettet werden sollen, die in ihr auch auftauchen, oder?
Ich verwende r16 häufig im Hauptprogramm..r17 auch.
In der ISR wird aber doch nur r17 verwendet, also muß es doch
so ablaufen:

push   r17
in     r17,SREG
dec    r17
pop    r17
out    SREG,r17

Liege ich hier falsch oder richtig!

Danke für Deine Antwort.

Grüße

Rolf

von Krapao (Gast)


Lesenswert?

1
push   r17 ; r17 sichern auf Stack
2
in     r17,SREG ; SREG sichern in r17
3
; irgendwas machen, aber nicht mit r17, weil das das gesicherte SREG enthält
4
out    SREG,r17 ; SREG restaurieren aus r17
5
pop    r17 ; r17 restaurieren vom Stack

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:

> TIM0_OVF: push r16   ;kopiere Inhalt von r16 auf die von Stapelzeiger SP
>                      ;adressierte SRAM Speicherstelle, danach SP-1
>
>           in   r16,SREG ;Inhalt vom Statusregister nach r16 laden
>           dec  r17      ;Zeitablauf im Hauptprogramm
>           pop  r16      ;erst SP+1, danach lade r16 mit dem
>                         ;adressierten SRAM-Byte
>           out  SREG,r16 ;Inhalt von r16 in SREG laden
>           reti

Lass uns das doch mal ganz konkret durchspielen

Das seien deine CPU Register, kurz bevor deine ISR betreten wird
(die aktuellen Zahlen an sich spielen keine Rolle, ich hab sie jetzt 
frei erfunden)

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x12  |    | 0x89   |         |          | <---
   +-------+     +--------+    +--------+         +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....

Ziel der ISR ist es, r17 runterzuzählen. Das und NUR DAS soll die ISR 
machen. Etwas anderes darf nicht verändert werden!
Schaun wir mal

  push r16

Aha. Den Inhalt von r16 auf den Stack schieben.

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x12  |    | 0x89   |         |   0x12   |
   +-------+     +--------+    +--------+         +----------+
                                                  |          | <---
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....

  in   r16,SREG

Den Inhalt von SREG nach r16 kopieren

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x35  |    | 0x89   |         |   0x12   |
   +-------+     +--------+    +--------+         +----------+
                                                  |          | <----
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....

   dec  r17

r17 um 1 erniedrigen. SChau in der Befehlsreferenz nach, das verändert 
auch das Statusregister

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0xAB  |     |  0x35  |    | 0x88   |         |   0x12   |
   +-------+     +--------+    +--------+         +----------+
                                                  |          | <---
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....


  pop  r16

den letzten Wert vom Stack holen und in r16 speichern

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0xAB  |     |  0x12  |    | 0x88   |         |          | <---
   +-------+     +--------+    +--------+         +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....


    out  SREG,r16 ;

den INhalt von r16 nach SREG kopieren

   SREG          r16           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x12  |     |  0x12  |    | 0x88   |         |          | <---
   +-------+     +--------+    +--------+         +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....

   reti

Das wars. Die ISR ist fertig.
Und jetzt vergleich mal, in welchem Zustand die Register waren, als die 
ISR betreteb wurden und wie sie jetzt sind. Welche dürfen verändert 
werden (r17) und welche nicht (alle anderen).
Und wie ist die Situation tatsächlich und wie ist es dazu gekommen?

So war die Situation vor dem Betreten der ISR
1
   SREG          r16           r17                 Stack
2
   +-------+     +--------+    +--------+         +----------+
3
   | 0x35  |     |  0x12  |    | 0x89   |         |          | <---
4
   +-------+     +--------+    +--------+         +----------+
5
                                                  |          |
6
                                                  +----------+
7
                                                  |          |
8
                                                  +----------+
9
                                                  |          |
10
                                                        ....
und so ist sie danach
1
   SREG          r16           r17                 Stack
2
   +-------+     +--------+    +--------+         +----------+
3
   | 0x12  |     |  0x12  |    | 0x88   |         |          | <--- 
4
   +-------+     +--------+    +--------+         +----------+
5
                                                  |          | 
6
                                                  +----------+
7
                                                  |          |
8
                                                  +----------+
9
                                                  |          |
10
                                                        ....
r17 wurde korrekt um 1 erniedrigt, was ja auch der Sinn der ISR war. r16 
hat wieder denselben Wert, der Stackpointer zeigt auch auf das gleiche 
Element wie am Anfang. Aber SREG hat NICHT denselben Wert! -> Fehler

von spess53 (Gast)


Lesenswert?

Hi
>Liege ich hier falsch oder richtig!

Falsch.

>push   r17         -> r17 auf den Stack
>in     r17,SREG    -> r17 = SREG
>dec    r17         -> r17 = SREG-1
>pop    r17         -> r17 = Wert bei Interrupteinsprung
>out    SREG,r17    -> SREG = Wert von r17 bei Interrupteinsprung

Merkst du etwas?

>Ich verwende r16 häufig im Hauptprogramm..r17 auch.
>In der ISR wird aber doch nur r17 verwendet, also muß es doch
>so ablaufen:

Nein. Du willst doch das r17 verändert wird. Es werden dir Register 
gesichert, die in der Interruptroutine benutzt und nicht verändert 
werden dürfen.

>als Beispiel eine Qelldatei von mir, bei der die ISR
>so aussieht (Tiny13)

Dai ist auch ein Fehler drin (<<<<<)

>TIM0_OVF: push r16
>          in   r16,SREG
>          dec  r17
>          pop  r16       <<<<<<< Falsche
>          out  SREG,r16  <<<<<<< Reihenfolge
>          reti

Richtig ist:

>push   r16         -> r16 auf den Stack
>in     r16,SREG    -> r16 = SREG
>dec    r17         -> r17 = r17-1
>out    SREG,r16    -> SREG = r16
>pop    r16         -> r16 = Wert bei Interrupteinsprung

MfG Spess

von Hannes L. (hannes)


Lesenswert?

Rolf Hegewald schrieb:
> Hallo Hannes,

Hallo Rolf,

eigentlich wurde schon alles gesagt (geschrieben), doch da Du mich 
direkt ansprachst, werde ich auch noch antworten...

> nun habe ich mich einmal intensiv mit den PUSH und POP
> beschäftigt, und muß feststellen, daß ich da irgendwelchen
> Quatsch fabriziere.

Jou, ein Stack ist ein Stapel, Du kannst nur das herunternehmen, was Du 
zuletzt draufgelegt hast. Wühlen ist nicht, dann wäre es kein Stack. Zum 
Vergleich bzw. der Abgrenzung:
- Random-Zugriff (völlig wahlfrei adressierbarer Speicher),
- FiFo (First In First Out, Ringspeicher),
- LiFo (Last In First Out, Stack).

>
> als Beispiel eine Qelldatei von mir, bei der die ISR
> so aussieht (Tiny13)
>
> TIM0_OVF: push r16   ;kopiere Inhalt von r16 auf die von Stapelzeiger SP
>                      ;adressierte SRAM Speicherstelle, danach SP-1
>
>           in   r16,SREG ;Inhalt vom Statusregister nach r16 laden
>           dec  r17      ;Zeitablauf im Hauptprogramm
>           pop  r16      ;erst SP+1, danach lade r16 mit dem
>                         ;adressierten SRAM-Byte
>           out  SREG,r16 ;Inhalt von r16 in SREG laden
>           reti

Die Reihenfolge am Ende ist falsch, aber das schrieb man Dir ja bereits.

Dein Konzept zu umständlich, für die Sicherung des SREGs lohnt sich der 
Einsatz eines Exklusivregisters. Da dabei keine Konstantenzugriffe 
erfolgen, reicht ein unteres Register aus. Ich bevorzuge r2 oder r3, da 
ich r0 und r1 aus Gewohnheit frei lasse, da sie gelegentlich auch für 
Sonderfunktionen gebraucht werden. Beispiele findest Du in meinen 
Quelltexten, die Du vielleicht doch mal analysieren solltest.

> Ich meine, ich hätte irgendwo gelesen, daß nur die I/O Register
> in der ISR gerettet werden sollen, die in ihr auch auftauchen, oder?

Gar keine I/O-Register, sondern nur Arbeitsregister (falls man keine 
Exklusivregister in der ISR benutzt) und das Statusregister (siehe sehr 
ausführliche Erklärung von Karl-Heinz).

> Ich verwende r16 häufig im Hauptprogramm..r17 auch.

Ja, der AVR hat aber noch 30 weitere Register. Und für nicht verwendete 
Register gibt es kein Geld zurück. Solange genug Register verfügbar 
sind, darf man sie ruhig (exklusiv) zweckgebunden verwenden. Natürlich 
braucht man ein oder zwei Register für temporäre Zwecke, bei mir sind 
das wl und wh (r24, r25). Die verwende ich aber nur bei der 
Initialisierung, in der Mainloop und in Jobs der Mainloop. Und auch nur 
für Zwecke, wo mich der Inhalt einige Programmzeilen später nicht mehr 
interessiert. In der ISR benutze ich sie aber nicht, um mir das Sichern 
und Wiederherstellen zu sparen. Deshalb benenne (und benutze) ich für 
die ISR eigene Tempregister. Diese werden dann nur in den ISRs temporär 
verwendet. Man muss sie also nicht sichern und nicht wiederherstellen. 
Push und Pop gibt es bei mir in den ISRs nur, wenn ich Register mit 
Spezialfunktionen brauche, die auch woanders gebraucht werden, wie z.B. 
Pointer (X Y, Z).

> In der ISR wird aber doch nur r17 verwendet, also muß es doch
> so ablaufen:
>
> push   r17
> in     r17,SREG
> dec    r17
> pop    r17
> out    SREG,r17
>
> Liege ich hier falsch oder richtig!

Völlig falsch, aber das hat man Dir ja bereits ausführlich erklärt.

>
> Danke für Deine Antwort.

[Dummer Spruch]
Nix zu danken, Kundendienst... ;-)
[/Dummer Spruch]

>
> Grüße
>
> Rolf

Deinen Problemen nach zu urteilen, solltest Du Dein Projekt 
zurückstellen und das Tutorial nochmal von vorn beginnen. Da sind 
etliche Basics, die Dir noch fehlen, weshalb Du Dich an allen Ecken und 
Kanten vera..lbern lässt. Uns dann solltest Du wirklich mal meine 
Quelltexte analysieren, ich habe sie nämlich nicht umsonst reingestellt. 
Sie sind relativ einfach gehalten, übersichtlich strukturiert und üppig 
kommentiert, also ziemlich einsteigerfreundlich. Wenn Du dazu 
Detail-Fragen hast (warum so, und nicht wie im Tutorial), werde ich sie 
Dir gern beantworten.

Nochmal zum Konzept. All diesen Aufwand betreibst Du, um dann in der 
Mailoop (bzw. einem UP davon) zu warten , bis der Timeout abgelaufen 
ist. Diese Vorgehensweise lernt man ausschließlich aus didaktischen 
Gründen, nämlich um bei der ersten ernsthaften Anwendung zu erkennen, 
dass das Quatsch ist, weil nämlich die CPU während des Wartens keine 
andere Arbeit ausführen kann. Richtig ist es, wenn man statt "auf der 
Stelle zu treten" zur Mainloop zurück kehrt und weitere Arbeiten 
erledigt, solange der Timeout noch nicht abgelaufen ist. Mit Deinem 
Konzept benutzt Du zwar einen Timer, wirfst seine Vorzüge aber in den 
Müll. Da kannst Du auch bei Warteschleifen bleiben, da braucht es keinen 
Timer...

...

von Rolf H. (flash01)


Lesenswert?

Hallo Karl Heinz und Spess,
habe alles erstmal ausgedruckt und werde es Morgen früh verarbeiten.
So langsam gehen mir die Augen auf über das Ganze.
Der "Fehler" am Ende von Karl Heinz zeigt doch, daß hier
im unteren Bereich eine Verdrehung vorliegt, wie das Spess
ganz klar erklärte.
Na ich spiele das alles morgen nochmal durch
Das würde doch bedeuten, wenn ich im Hauptprogramm r16, r17, r18
verwende, muß ich auch in der ISR

push  r16
push  r17
push  r18

pop   r18
pop   r17
pop   r16

einrichten, oder?

Grüße

Rolf und ein Dankeschön für die Hilfe

von Karl H. (kbuchegg)


Lesenswert?

Rolf Hegewald schrieb:

> Das würde doch bedeuten, wenn ich im Hauptprogramm r16, r17, r18
> verwende, muß ich auch in der ISR
>
> push  r16
> push  r17
> push  r18
>
> pop   r18
> pop   r17
> pop   r16
>
> einrichten, oder?

Im Prinzip: ja.
Aber was Hannes dir schon die ganze Zeit zu erklären versucht:

verwende halt einfach r16, r17 und r18 nicht in der ISR. Gut r17 musst 
du verwenden, weil es die Aufgabe der ISR ist, r17 zu verändern. Aber 
r16 und r18 nicht. Du hast noch haufenweise andere Register, die du zu 
Arbeitszwecken innerhalb der ISR benutzen kannst. Muss ja nicht 
ausgerechnet r16 und r18 sein.
Veränderst du die Register in der ISR nicht, brauchst du sie auch nicht 
sichern. Manchmal kann die Welt so einfach sein.

Aber: SREG musst du immer und auf jeden Fall in der ISR sichern und 
wiederherstellen.

von spess53 (Gast)


Lesenswert?

Hi

>Das würde doch bedeuten, wenn ich im Hauptprogramm r16, r17, r18
>verwende, muß ich auch in der ISR...

Nur wenn sie

1. durch die Interruptroutine verändert werden.
2. durch die Interruptroutine nicht verändert werden dürfen.

Ich habe jetzt bewusst 'verändert' geschrieben. Es gibt auch Befehle die 
ein Register verwenden, aber es nicht beeinflussen. Z.B.

      add r16,r17

Hier wird nur r16 beeinflusst, r17 bleibt unverändert.

MfG Spess

von Oldmax (Gast)


Lesenswert?

Hi
Rolf Hegewald schrieb:

> Das würde doch bedeuten, wenn ich im Hauptprogramm r16, r17, r18
> verwende, muß ich auch in der ISR
>
> push  r16
> push  r17
> push  r18
>
> pop   r18
> pop   r17
> pop   r16
>
> einrichten, oder?

Egal, ob die Register erforderlich sind oder nicht, so ist es schon 
richtig, aber..... der Fehler steckt auch hier:

> In der ISR wird aber doch nur r17 verwendet, also muß es doch
> so ablaufen:
>
> push   r17
> in     r17,SREG
> dec    r17
> pop    r17
> out    SREG,r17
>
> Liege ich hier falsch oder richtig!

R17 retten ist ok.
SReg nach r17 laden auch ok
Dec kann man machen, aber, dann holst du dir den alten r17 Wert vom 
Stack und packst ihn in das Statusregister. Was bitteschön erwartest du 
nun für Statusbits ? R17 hat vor dem Pushen einen Wert zwischen 0 und 
255. Da es ein Interrupt ist, kannst du nicht erwarten, diesen Wert zu 
kennen. Und genau den haust du in das Statusregister.... Bingo... 
Volltreffer. aus 145 wird 0b10010001, also Statusbits.
Richtig ist:
1
push   r17        ; zuerst r17 sichern
2
in     r17,SREG   ; dann Statusregister holen
3
push   r17        ; und Statusregister sichern
4
....              : deine ISR
5
End_ISR:
6
pop    r17        ; Statusregister vom Stack holen
7
out    SREG,r17   ; und zurückschreiben
8
pop    r17        ; und jetzt erst r17 wieder herstellen
Aber dazu sind auch schon eine Menge Antworten erfolgt. Vielleicht waren 
sie einfach zu ausführlich, das der Kern des Fehlers untergegangen ist.
Gruß oldmax

von Rolf H. (flash01)


Lesenswert?

zu Karl Heinz

nochmal von vorne:

TIM0_OVF: push r2   ;kopiere Inhalt von r2 auf die von Stapelzeiger SP
                     ;adressierte SRAM Speicherstelle, danach SP-1
          in   r2,SREG ;Inhalt vom Statusregister nach r2 laden
          dec  r17      ;Zeitablauf im Hauptprogramm
          out  SREG,r2 ;Inhalt von r2 in SREG laden
          pop  r2      ;erst SP+1, danach lade r2 mit dem
                        ;adressierten SRAM-Byte
          reti

So sieht der Anfang aus:

   SREG             r2           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x12  |    | 0x89   |         |          | <---
   +-------+     +--------+    +--------+         +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....

   ;den Dreher korrigiert:

    out  SREG,r2

den INhalt von r2 nach SREG kopieren

   SREG             r2           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x35  |    | 0x88   |         |          | <---
   +-------+     +--------+    +--------+         +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                  +----------+
                                                  |          |
                                                        ....
   pop  r2

den letzten Wert vom Stack holen und in r2 speichern

   SREG             r2           r17                 Stack
   +-------+     +--------+    +--------+         +----------+
   | 0x35  |     |  0x12  |    | 0x88   |         |          | <---
   +-------+     +--------+    +--------+         +----------+

  ;die Endwerte entsprechen denen vom Anfang
   reti
Lese ich auch das von Hannes, dann dient in meinen Beispiel das
Arbeitsregister r2 nur dazu, den Inhalt vom Statusregister SREG
zu retten. Ich werde mir nochmals die Quelldateien von Hannes
ansehen und besonders mein Augenmerk auf seine ISR legen.

Grüße

Rolf

von Peter D. (peda)


Lesenswert?

Rolf Hegewald schrieb:
> Das würde doch bedeuten, wenn ich im Hauptprogramm r16, r17, r18
> verwende, muß ich auch in der ISR
>
> push  r16
> push  r17
> push  r18
>
> pop   r18
> pop   r17
> pop   r16
>
> einrichten, oder?

In Assembler hat man viele Optimierungsmöglichkeiten:

Wenn Du keine Schweinereien in Interrupts mach willst, also jeder wird 
zuende abgearbeitet, dann kannst Du ein Register fürs SREG Sichern 
reservieren. Auch kannst Du von den 32 Registern einige als 
Interruptarbeitsregister reservieren. Vorzugsweise auch den Y-Pointer, 
im Main kommt man gut mit nur 2 Pointern zurecht.
So kann man dann Interrupts oft ganz ohne PUSH/POP schreiben.

Und auch fürs Main reserviert man einige Register als zerstörbar, dann 
müssen Unterfunktionen keine Arien an PUSH/POP machen.
Oftmals übergibt man ja Werte an Unterfunktionen, die danach garnicht 
mehr gebraucht werden.


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.