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
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.
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
...
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
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
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
> 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.
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
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.
...
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
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
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.
...
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.
...
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
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
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
...
> 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.
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.
...
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
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
...
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
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.
...
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.
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...
...
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
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
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
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
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.
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
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...
...
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
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
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
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...
...
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
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.
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
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
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
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