PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Drehgeber - Auswertung per polling



henne
02.12.2004, 22:07
Hallo zusammen,
habe einen Drehgeber mit A und B Signal (2 Bit) an PD2 und PD3
angeschlossen und möchte einen counter rauf oder runterzählen.
Dafür wird die erste Stellung einmal eingelesen und der Gray-Code in
Binär-Code umgewandelt, danach wird erneut die Position eingelesen und
dann mit der alten verglichen.
Bei der Gray/Binär-Umwandlung wird das MSB (Bit 1)so wie es ist
übernommen und das LSB(Bit 0) durch XOR-Verknüpfung des LSB mit dem MSB
bestimmt. Der Vergleich soll dann mit CP erfolgen.

Der Code funktioniert aber nicht so, wie ich es möchte.
Muss man einen Interrupt dafür starten ??
Wenn ja, warum ?
Kann man die Sprünge zu den "Unterprogrammen" so durchführen, oder
muss das mir RCALL und RET erfolgen ??
Bin davon ausgegangen, das bei einer Abfrage
CP pos_klein,temp
BRNE weiter
...
bei Gleichheit der Code bei den drei Punkten weitergeht.
Vielleicht kann mir jemand Verbesserungen vorschlagen
oder die Fehler nennen.
Danke schonmal

code:

.include "m8def.inc"

.def temp = r16
.def temp2= r17
.def pos_klein = r18
.def klein_alt = r19
.def klein_neu = r20

;Stackpointer initialisieren

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

;Aus/Eingänge festlegen

ldi temp, 0b11000000 ;PD7,PD6 -Ausgang, Rest als Eingang
out DDRD, temp

ldi pos_klein,0b00000000 ;Positionscounter "leer" laden


;Stellung zum ersten mal einlesen
;und Signale A und B(PD2,PD3)
;ans Ende kopieren

in temp, PIND ;an PIN D anliegende Werte einlesen
BST temp,2 ;Bit 2 von temp in das T-Flag schreiben (Bit Store)
BLD klein_alt,0 ;T-Flag in Bit 0 von klein_alt laden (Bit Load)
BST temp,3
BLD klein_alt,1

ldi temp,0b00000000

;Conversion Gray--->Binary

BST klein_alt,1
BLD temp,1
BST klein_alt,0
BLD temp2,1

EOR temp2,temp
BST temp2,1
BLD temp,0
MOV klein_alt,temp

nochange:

;counter pos_klein überprüfen

ldi temp,0b00000100
CP pos_klein,temp
BRNE weiter
ldi temp,0b10000000
out PORTD,temp ;wenn zähler 4,dann soll LED 7 leuchten

weiter:

in temp, PIND ;an PIN D anliegende Werte einlesen
BST temp,2 ;Bit 2 von temp in das T-Flag schreiben (Bit Store)
BLD klein_neu,0 ;T-Flag in Bit 0 von klein_alt laden (Bit Load)
BST temp,3
BLD klein_neu,1

ldi temp,0b00000000
;Conversion Gray--->Binary
BST klein_neu,1
BLD temp,1
BST klein_neu,0
BLD temp2,1

EOR temp2,temp
BST temp2,1
BLD temp,0

MOV klein_neu,temp

CP klein_alt,klein_neu ;Vergleich von alter und neuer Position
BRNE direction ;wenn ungleich, dann Richtung bestimmen -->direction
RJMP nochange ;ansonsten neu überprüfen --->nochange

direction:

CP klein_alt, klein_neu
BRLO right ;wenn kleiner dann ---->right
RJMP left ;ansonsten ---->left

right:
INC pos_klein
MOV klein_alt, klein_neu
RJMP nochange

left:
DEC pos_klein
MOV klein_alt, klein_neu
RJMP nochange

henne
02.12.2004, 22:48
So, habe mich mal registriert.

Noch zum Verständnis - habe einen Atmega8 bei dem ich die Interrupts INT0 und INT1 nicht unbedingt dafür verwenden wollte, da ich später auch nicht nur einen, sondern zwei Drehgeber auswerten wollte.

Gruß, Hanno

JanB
03.12.2004, 23:05
Hi Hanno,
ich bin nicht sicher, ob ich richtig verstanden habe,
was du eigentlich machen willst, aber ich gebe dir hier
mal ein Beispiel für eine Drehgeber-Abfrage.
Kern dieser Methode ist die Tabelle dgtab.
Jede der vier Zeilen steht für einen "alten" Drehgehber-Zustand.
In jeder Zeile stehen die Additionswerte für den Zähler
für jede der vier möglichen "neuen" Zustände.

Vieleicht hilft dir das weiter.

Gruß Jan



;Codebeispiel zur Auswertung eins Drehgebers
;für Atmel ATTiny15L
;Signale A und B sind an PortB Bit0 und Bit1 angeschlossen
;Es wird 8 Bit weit gezählt mit dem Zähler cnt
;Jan Baare 12/2004

.NOLIST
.INCLUDE "C:\Programme\Atmel\Avr Tools\AvrAssembler\appnotes\tn15def.inc"
.LIST

.DEF temp = r16
.DEF cnt = r17 ;der Zähler
.DEF ast = r18 ;alter Zustand Drehgeber

dgstart: ldi temp,0
out ddrb,temp ;Portb 0-7 auf Input setzen
in ast,portb ;einmal den aktuellen PortZustand holen
andi ast,3 ;Drehgeber-Bits maskieren

dgloop: in temp,portb ;aktuellen PortZustand holen
andi temp,3 ;Drehgeber-Bits maskieren
cp temp,ast ;mit altem Zustand vergleichen
breq dgloop ;Keine Änderung -> nix machen
lsl ast ;*2
lsl ast ;*2
add ast,temp ;alter Zustand mal 4 plus neuer Zustand
ldi ZL,low(dgtab<<1) ;Zeiger Z auf Tabellenanfang setzen
ldi ZH,high(dgtab<<1)
add ZL,ast ;zum Zeiger Addieren
clr ast
adc ZH,ast
lpm ;Tabelleneintrag holen
add cnt,r0 ;auf Zähler draufaddieren
mov ast,temp ;neuer Zustand wird alter Zustand
rjmp dgloop ;fertig


dgtab: .db 0,1,-1,0
.db -1,0,0,1
.db 1,0,0,-1
.db 0,-1,1,0

;Ende

henne
07.12.2004, 20:27
Hallo JanB,
Ich habe den Code für den Atmega8 angepasst,
und als Kontrolle für den Counter eine LED-Anzeige hinzugefügt.
Leider funktioniert das ganze nicht - ich habe an PORTD 6 und 7 jeweils
eine LED und beide leuchten,
mehr passiert nicht.
Das mit der Tabelle verstehe ich auch nicht so ganz--was und warum wird
damit erreicht, den alten Zustand mit 4 zu multiplizieren und zum neuen hinzuzufügen??
Was ist in den Spalten und Zeilen der Tabelle was ??
Hier mal der Code

Gruß, Hanno



;Codebeispiel zur Auswertung eins Drehgebers
;für Atmel AtMega8
;Signale A und B sind an PortD Bit2 und Bit3 angeschlossen
;Es wird 8 Bit weit gezählt mit dem Zähler cnt
;Hanno Monschan 12/2004

.NOLIST
.INCLUDE "m8def.inc"
.LIST

.DEF temp = r16
.DEF cnt = r17 ;der Zähler
.DEF ast = r18 ;alter Zustand Drehgeber
.DEF temp2 = r19



dgstart:
ldi temp,0b11000000
out ddrd,temp ;Portd 6/7 auf Ouput setzen, Rest Input
in ast,portd ;einmal den aktuellen PortZustand holen
andi ast,0b00001100 ;Drehgeber-Bits maskieren
ldi temp2, 4 ;bei Zählerstand 4 soll LED leuchten-->anzeige

dgloop: in temp,portd ;aktuellen PortZustand holen
andi temp, 0b00001100 ;Drehgeber-Bits maskieren
cp temp,ast ;mit altem Zustand vergleichen
breq dgloop ;Keine Änderung -> nix machen


lsl ast ;*2
lsl ast ;*2
add ast,temp ;alter Zustand mal 4 plus neuer Zustand
ldi ZL,low(dgtab<<1) ;Zeiger Z auf Tabellenanfang setzen
ldi ZH,high(dgtab<<1)
add ZL,ast ;zum Zeiger Addieren
clr ast
adc ZH,ast
lpm ;Tabelleneintrag holen
add cnt,r0 ;auf Zähler draufaddieren
mov ast,temp ;neuer Zustand wird alter Zustand

cp temp2, cnt ;wenn Counter==4
breq anzeige ;Ausgabe

rjmp dgloop ;fertig


dgtab: .db 0,1,-1,0
.db -1,0,0,1
.db 1,0,0,-1
.db 0,-1,1,0


anzeige:
ldi temp,0b10000000 ;Ausgabe an LED 7
out portd,temp
rjmp dgloop

;Ende

JanB
08.12.2004, 08:27
Hallo Hanno,

Ich habe den Code für den Atmega8 angepasst,
Das Codefragment war als Beispiel gedacht um es zu analysieren,
die Methode zu verstehen, und dann selbst zu verwenden.
Und nicht, um es 1:1 zu übernehmen. Aber egal.


Leider funktioniert das ganze nicht
Das Beispiel hat den Anschluss des Drehgebers an den Bits 0 und 1 des Ports vorgesehen.
Du hast sie jedoch an den Bits 2 und 3 angeschlossen.
Da müssen die Bits im Register noch um zwei Bits nach rechts verschoben werden, damit es funktioniert.



Das mit der Tabelle verstehe ich auch nicht so ganz
Also diese Methode funktioniert folgendermassen:
Die Signale A und B des Drehgebers können genau vier verschiedene
Zustände annehmen: 00, 01, 10, 11.
Das entspricht den Binärwerten 0 bis 3.
Das Gleiche gilt natürlich auch für die "alten" Drehgeberwerte.
Zur Auswertung müssen die "neuen" und die "alten" DG-Werte miteinander verglichen werden.
Jeder der beiden Werte kann wie gesagt einen von vier verschiedenen Zuständen haben.
Der Vergleich kann also genau 4 mal 4 also 16 Kombinationen ergeben.
Für jede dieser 16 Kombinationen gilt es zu entscheiden,
ob der Zähler stehen bleibt (+0), oder ob er vorwärts (+1), oder rückwärts (-1) zählen muss.
Diese Entscheidungsmatrix ist die Tabelle dgtab.
Sie besteht aus 4 Zeilen a' 4 Spalten, enthält also 16 Werte.
Jede Zeile steht für einen "alten" DG-Zustand.
Jede Spalte steht für einen "neuen".
Der Tabelleneintrag (0 oder 1 oder -1) im Schnittpunkt
von "alter Wert" und "neuer" Wert ist der Zählerschritt,
der einfach auf den Zähler aufaddiert wird.
Wie findet das Progrämmchen den richtigen Tabelleneintrag ?
1. Es wird ein Zeiger (ZH,ZL) auf den Anfang der Tabelle gesetzt.
2. Es wird der "alte" DG-Zustand mal 4 genommen und auf den Zeiger addiert.
Der Zeiger zeigt jetzt auf den Anfang der richtigen Zeile.
3. Es wird der "neue" DG-Wert auch auf den Zeiger addiert.
Jetzt zeigt der Zeiger auch auf die richtige Spalte innerhalb der Zeile.
Der Tabellenwert (0 oder 1 oder -1), auf den der Zeiger jetzt zeigt,
wird ausgelesen (lpm) und zum Zähler addiert.
Jetzt muss nur noch der "neue"DG-Wert zum "alten DG-Wert werden,
und fertig.

Das war jetzt wirklich ausführlich, meine ich.
Diese Methode realisiert eine vollständige Auswertung des DG-Signales.
D.h. Bei einem Drehgeber mit nominal 100 Imp/Umdrehung wird eine
Umdrehung auf 400 Positionen aufgelöst.

Ich nehme an, du bist noch Einsteiger in der Assembler-Programierung.
Dann solltest dir das Beispiel ansehen, und versuchen zu verstehen,
wie es funktioniert, dann kannst du es beliebig selbst anwenden.
Nur Abtippen bringt dich nicht viel weiter.

Ich habe deinen Code etwas angepasst so das er funktionieren müßte.
Was ist das eigentlich für ein Drehgeber, den du da verwendest, und was
willst du eigentlich damit machen ?
Warum soll die LED gerade bei Zählerstand 4 leuchten ? Und nur da ?

Gruß Jan



;für Atmel AtMega8
;Signale A und B sind an PortD Bit2 und Bit3 angeschlossen
;Es wird 8 Bit weit gezählt mit dem Zähler cnt

.NOLIST
.INCLUDE "m8def.inc"
.LIST

.DEF temp = r16
.DEF cnt = r17 ;der Zähler
.DEF ast = r18 ;alter Zustand Drehgeber
.DEF temp2 = r19



dgstart:
ldi temp,0b11000000
out ddrd,temp ;Portd 6/7 auf Ouput setzen, Rest Input
in ast,portd ;einmal den aktuellen PortZustand holen
andi ast,0b00001100 ;Drehgeber-Bits maskieren
lsr ast ;DG-Bits in POsition 0 und 1 schieben
lsr ast

dgloop: in temp,portd ;aktuellen PortZustand holen
andi temp, 0b00001100 ;Drehgeber-Bits maskieren
lsr temp ;DB-Bits in Pos. 0 und 1 schieben
lsr temp
lsl ast ;*2
lsl ast ;*2
add ast,temp ;alter Zustand mal 4 plus neuer Zustand
ldi ZL,low(dgtab<<1) ;Zeiger Z auf Tabellenanfang setzen
ldi ZH,high(dgtab<<1)
add ZL,ast ;zum Zeiger Addieren
clr ast
adc ZH,ast
lpm ;Tabelleneintrag holen
add cnt,r0 ;auf Zähler draufaddieren
mov ast,temp ;neuer Zustand wird alter Zustand

cpi cnt,4 ;Counter==4 ?
brne dgloop ;nee
ldi temp,0b10000000 ;LED schalten
out portc,temp
rjmp dgloop ;fertig


dgtab: .db 0,1,-1,0
.db -1,0,0,1
.db 1,0,0,-1
.db 0,-1,1,0


;Ende

henne
24.02.2005, 16:26
Hallo,
bin erst jetzt dazu gekommen mich wieder näher mit dem Atmega zu beschäftigen.
Den Code habe ich nun bis in letzte Detail verstanden, hoffe ich zumindest.
Die Simulation mit AVR-Studio klappt astrein, wenn ich alte und neue Position manuell angebe, zählt der counter rauf oder runter, so wie`s sein soll.
Nun läufts mit derm realen Drehgeber aber immer noch nicht--habe die Signale gecheckt, die Spg---alles fehlerfrei.
Nun stellt sich mir die Frage, ob der Mikrocontroller zu schnell oder zu langsam für die Erkennung der Signale ist, oder ob es noch woanders dran liegen kann, das der counter immer gleich bleibt.

@Jan: der Counter=4 soll nur ne Überprüfung sein, ob er zählt.

wenn ich die Werte in der Tabelle alle auf 1 setze, dann leuchtet die Lampe sofort---weil ja immer 1 addiert wird und sofort die 4 erreicht wird.

JanB
24.02.2005, 16:55
Hallo,
kann ich mir auch nicht erklären.
Zu schnell ist der MC garantiert nicht.
Zu langsam könnte er sein,
er muss schneller pollen als die Signale wechseln.
Ist ja logisch.

Und wenn es in der Simu klappt, kannes eigentlich nur an der
Hardware liegen.
Hast du alle Ports richtig initialisiert und so ?

Ahhh...Moment....Jaaaa!
Sorry, Sorry
Ich habe in der Routine meinen AVR-Lieblingsfehler eingebaut,
auf den ich immer wieder reinfalle.

Ein Port beim AVR kann man nicht mit IN PORTX lesen !!!
ichidiotwannwerdeichdasendlichlernen
Man muss IN PINX machen.

Also in der dritten Zeile des Codes
muss NICHT "in ast,PORTD" stehen,
SONDERN da muss "in ast,PIND" stehen.
Und in Zeile 7 (dgloop) natürlich genauso.
da muss "in temp,PIND" hin.
Wenn schon falsch, dann wenigstens konsequent.

Das hab ich doch auch im Downloadbereich stehen.
Das das bisher noch keiner gemerkt hat - komisch:
da guckt wohl keiner so genau hin. Egal.

Sagst du mir ob es klappt ?
Oder sprichst du jetzt nicht mehr mit mir ? :-(

Gruß Jan

henne
24.02.2005, 17:19
Die Antwort kam ja mal schneller als mein Atmega schaltet ;-)

JAAAAA--es klappt!
Habe ich gerade geändert, und siehe da----es funktioniert.
Das hätte mir aber auch auffallen müssen, naja, bin ja noch ein Anfänger.

Danke für deine Hilfe!
Da ich mich ja jetzt den anderen Dingen wie z.B. ADC widmen werde, habe ich bestimmt nochmal die ein oder andere Frage an dich ;-)

Gruß, Hanno

henne
24.02.2005, 17:45
Halt,noch was.

sollte die "Tabelle" nicht eigentlich so aussehen:
.DB 0,-1,1,0
.DB 1,0,0,-1
.DB -1,0,0,1
.DB 0,1,-1,0

gehe ich richtig in der Annahme, das man sie auch so schreiben könnte ?
.DB 0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0
dabei sehe ich gerade das sich nur die Zählrichtung im Vergleich zu deiner ändert.(deine Tabelle invertiert).

Woher kann es kommen, das der Drehgeber (100imp/Drehung) den counter schon bei ca.1/4 Umdrehung bei 100 hat ?? müsste doch eigentlich 25 sein!?

JanB
24.02.2005, 18:20
Hallo,
Die Tabelle kann man schreiben wie man will,
rückwärts oder vorwärts, ändert sich nur die Drehrichtung.


Woher kann es kommen, das der Drehgeber (100imp/Drehung) den counter schon bei ca.1/4 Umdrehung bei 100 hat ?? müsste doch eigentlich 25 sein!?
Das ist völlig richtig, und zeigt das es wie gewünscht funktioniert.
Diese Methode ist eine "vollständige" Auswertung.
2 mal 100 um 90 Grad versetze Signale ergibt
genau 400 Zustandswechsel pro Umdrehung.
Viele andere Verfahren verschenken 3/4 oder die Hälfte
der möglichen Auflösung des Drehgebers.
Da kommen dann nur 200 oder 100 Positionen pro Umdrehung raus.

Gruß Jan