PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Erkennen der Drehrichtung eines Drehgebers (Inkrementalwertgeber) mit ATtiny13



Schmidtbot
13.07.2011, 22:51
Hallo,
ich brauche Hilfe. Ich baue ein Sportboot(4x1m). Das Ruder wird über einen Schrittmotor(SM) betätigt. Dies Funktioniert auch schon. Jetzt kommt aber mein Problem. Am Lenkrad des Bootes befindet sich eine Drehgeber. Dieser Drehgeber soll den SM Steuern. Dafür habe ich mit einen Attiny13 und einen NE555 für die Trimmung ein Board erstellt. Dieses funktioniert auch. Nur der µC nicht.
Ich gebe auf den µC am PB.3 und am PB.4 die beiden Signale des Gebers. Der µC soll dann am PortB.2 den Takt des Gebers Ausgeben wenn ich das Lenkrad nach rechts drehen. Wenn ich das Lenkrad nach links drehe soll er den Takt am PB.1 ausgeben. Dafür hab ich diesen Code erstelle. Leider Funktioniert dieser nicht. Am Anfang soll er beim Starten die beiden Ausgänge auf High setzen nach dieser Pause soll er das Arbeiten Anfangen.

Besten Danke für euere Hilfe!
Gruß
Rolf


begin:
rjmp main ; RESET External Reset, Power-on Reset and Watchdog Reset
reti; INT0 External Interrupt 0
reti; PCINT0 External Interrupt Request 0
reti; TIM0_OVF Timer/Counter0 Overflow
reti; EE_RDY EEPROM Ready
reti; ANA_COMP Analog Comparator
reti; TIM0_COMPA Timer/Counter Compare Match A
reti; TIM0_COMPB Timer/Counter Compare Match B
reti; WDT Watchdog Time-out
reti; ADC ADC Conversion Complete
;------------------------------------------------------------------------------
main:
ldir16, lo8(RAMEND) ; Main program start
outSPL, r16; Set Stack Pointer to top of RAM
cbiDDRB, 3 ; PORTB.3 als Eingang gesetzt
cbiDDRB, 4 ; PORTB.4 als Eingang gesetzt
sbiDDRB, 1 ; PortB.1 als Ausgang gesetzt
sbiDDRB, 2 ; PortB.2 Als Ausgang gesetzt
;... ; Hier Init-Code eintragen.
;------------------------------------------------------------------------------
mainloop:
ldir18, 0b00000000
ldir17, 0b00000000
ldir16, 0b00000001
ldir19, 0b00000110
outPORTB, r19
incr17
brne mainloop
incr18
brne mainloop
rolr16
sbrsr16, 7
rjmp mainloop
ldir19, 0b00000000
outPORTB, r19
Start:
inr16, PINB; alle Eingänge in R16 einlessen
sbrcr16, 3
sbrR21, 0b00000001 ; setze BIT 0 in R19 auf high
cbrr21, 0b00000001
sbrcr16, 4
sbrR22, 0b00000001 ; setze BIT 0 in R19 auf high
cbrr22, 0b00000001
Flanke:
comr17; alle Werte im Register 17 negieren
movr18, r16; kopiere das Register 16 in das Register 17
andr18, r17; Und verknüfen der beiden Register- Ergebniss der UND-Verknüpfung in r18
movr17, r16; kopieren den Inhalte des R16 in R17
sbrcr18, 3 ; überprüfe die Und-Verknüpfung des ersten Port, true = nicht fals = springe
sbrR19, 0b00000001 ; setze BIT 2 in R19 auf high
cbrr19, 0b00000001
sbrcr18, 4 ; überprüfe die Und-Verknüpfung des ersten Port, true = nicht fals = springe
sbrR20, 0b00000001 ; setze BIT 2 in R19 auf high
cbrr20, 0b00000001
Und1:
andR22, R19
movr23, R22
Und2:
andR21, R20
movr24, R21
negieren:
movr25, r23
movr26, r24
incr25
incr26
und3:
andr23, r26
sbrcr23, 0
sbiPORTB, 2
cbiPORTB, 2
und4:
andr24, r25
sbrcr24, 0
sbiPORTB, 1
cbiPORTB, 1
rjmp Start ; Springe zum Mainloop Punkt

PicNick
14.07.2011, 08:45
Wenn man nur die steigenden (oder fallenden) Flanken berücksichtigt, wird man bei jeder Dreh-Richtungsänderung immer einen Schritt verlieren.
Weiss nicht, wie heikel das in deinem Fall ist.
1) wenn nicht, reicht ein D-Flip-Flop, Channel A als "Clock", Channel B als "Data". Das ergibt mit 0/1 die Richtung, und clock ist auch der Takt für den SM

2) vollständige decodierung:
Da Du offenbar gevifter Assembler-Fuzzy bist, hilft Dir vielleicht das Assembler-Listing von dem, was zB Bascom für drehgeber assembliert
http://www.rn-wissen.de/index.php/Bascom_Inside-Code#.28Quadratur-.29_ENCODER

theoretisches:

http://www.rn-wissen.de/index.php/Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C _Geschwindigkeit_und_Position_mit_Doppellichtschra nke_ermitteln

Ostermann
14.07.2011, 12:42
Ohne mir jetzt deinen Code im Detail angesehen zu haben: Der Encoder kennt nur 4 Zustände. Die kann man einfach in einer Tabelle ablegen und dann jeweils den alten mit dem aktuellem Zustand vergleichen. Über die Tabelle "sieht" man dann, ob es vorwärts oder rückwärts geht. Ein Sprung über 2 oder mehr Tabelleneinträge stellt einen Fehler da. Die Eingänge müssen also schnell genug abgetastet werden.

Pos. Signale
0 00
1 01
2 11
3 10
4=0 00
usw.

Mit freundlichen Grüßen
Thorsten Ostermann

PICture
14.07.2011, 12:52
Hallo!

Hoffentlich hilft dir die Beschreibung und Programablaufdiagramm (PAD): http://www.rn-wissen.de/index.php/PIC_Assembler#Mausrad_bzw._Drehencoder . ;)

PicNick
14.07.2011, 13:13
@ostermann, mit verlaub, ich muss dich korrigieren: wenn du nicht beide Flanken auswertest, ist bei einer drehrichtungsumkehr die sache nicht präzise. Und dadurch hast du 8 zustände.
Bin sicher, ich muss bei dir nicht klitzeklein aufzeichnen, wo das problem liegt :-)

Ostermann
14.07.2011, 14:50
Hallo Robert!

Man wertet nach meiner Methode überhaupt keine Flanken aus, sondern Zustände und Zustandsübergänge. Von einem Zustand zum nächsten gibt es immer genau zwei erlaubte Übergänge, nämlich linksrum oder rechtsrum. Ich sehe nicht, so du da 8 Zustände herholen willst...

Mit freundlichen Grüßen
Thorsten Ostermann

PicNick
14.07.2011, 16:20
Dacht' ich doch, dass es da nur um missverständnisse geht, hätt mich gewundert.
ich bezeichne halt Zustandsübergänge als Flanken, soll sein.
nach so einem Übergang haben ich also 4 Zustände, wie o. beschrieben.
Da aber jeder Übergang in zwei Richtungen erfolgen kann, habe ich, eben 8 Möglichkeiten.

mit Interrupt müsste man aber eben beide Edges (flanken?) mit rising & falling konfigurieren, und das wären in der Tat 8 Irrpts

Ostermann
14.07.2011, 17:17
Hallo Robert!

Es sind auch nur 4 Flanken, auch wenn es 8 Möglichkeiten sind (abhängig vom vorherigen Zustand).

Aber unabhängig davon würde ich nicht empfehlen, dass mit Interrupts zu machen, sondern einfach die beiden Pins in einem festen Zeitraster samplen und auswerten. Dann braucht man sich über die Flanken keine Gedanken mehr zu machen, und hat auch keine Probleme mit Störimpulsen.

Mit freundlichen Grüßen
Thorsten Ostermann

PicNick
14.07.2011, 19:17
Hi, Thorsten !
Über die effektive Durchführung der Auswertung gibt's ja die allgemeine und fundamentale Regel: Es kommt darauf an :-)
Ich hab das vor einiger Zeit bei einer Anwendung für einen Filmschneidetisch, wo ka kein pieps verlorengehen soll, ebenfalls mit sampeln gemacht und recht einfach AB u. ABvorher zu 4 Bit zusammen gelegt und mit lookup aus einer tabelle die notwendigen werte für counter u. direction bekommen

Is es nicht so heikel, dann nehm ich für A einen Interrupt, B zeigt dann up/down. Wenn der µC auch noch andere Jobs hat, ist das insgesamt einfacher.

Bin sicher, es gibt noch mehr methoden und meinungen

Schmidtbot
14.07.2011, 21:57
Danke schon mal für die vielen und schnellen Antworten!!!!
Aber ich muss euch enttäuschen aber ich bin keine guter Assembler Programmierer ganz im Gegenteil ich Stümper ein bisschen rum aber mehr auch nicht ich versuche mir gerade C beizubringen.

Ich verstehe die Lösung mit der Tabelle leider nicht. Ein Arbeitskollege der C gut kann hat mir heute diesen Quelle-Code erstelle.


//alte werte müssen global deklariert sein


steuerfunktion(void)
{
a = portB & 0x8;
b = portB & 0x10;

if ((a == 1) && (a_old == 0)) P_a = 1;
else P_a = 0;

if ((b == 1) && (b_old == 0)) P_b = 1;
else P_b = 0;


temp_a = a & P_a;
temp_b = a & P_b;

portb2 = temp_a & !temp_b;
portb1 = temp_b & !temp_a;

//Löschen des alten Outputs
PORTB &= ~0x4;
PORTB &= ~0x2;

//zurdnung auf Ports (setzen des bits)
PORTB |= portb2;
PORTB |= portb1;


a_old = a;
b_old = b;
}

Allerdings weiß er nicht wie ich das mit der Ein- und Ausgabe machen kann.

Kann mir hier vielleicht einer von euch weiterhelfen, denn das Code an sich sollte laut ihn Funktionieren.

Ich bin für jede kleine Hilfe sehr dankbar!

Gruß
Rolf

PicNick
15.07.2011, 07:19
Richte deinem Kollegen einen Gruss von mir aus :mrgreen:

Folgendes:


a = portB & 0x8; // ergibt 0x00 oder 0x08 !!!
b = portB & 0x10; // ergibt 0x00 oder 0x10 !!!
// daher kann man man nicht auf == 1 abfragen, sondern auf ==0x08 bzw ==0x10
// IMHO aber besser und besser lesbar
// Abfrage auf NULL und NICHT NULL
if (a && !a_old) P_a = 1; else P_a = 0;
if (b && !b_old) P_b = 1; else P_b = 0;


Ich werd' mir aber meine Assembler-routine raussuchen (auswendig erzähl' sonst vielleich einen stiefel)

Schmidtbot
22.07.2011, 04:04
Habe so eben die Steuerung in Betrieb genommen. Und Sie funktioniert einwandfrei.
Besten danke für euere Hilfe.

Das ist der Quellcode mit dem ich meinen ATtiny gefütter haben. Funktioniert sogar alles besser als gedacht :cool:

Gruß
Rolf


#include <avr/io.h>
unsigned char chanal_a;
unsigned char chanal_b;
unsigned char temp_a;
unsigned char temp_b;
unsigned char a_old;
unsigned char b_old;
unsigned char Flanke_a;
unsigned char Flanke_b;
unsigned char links;
unsigned char rechts;

#define F_CPU 1000000 // definiert die Frequenz des Microcontrollers 3,68 MHz
#include <avr/io.h>

int main(void)
{
DDRB = DDRB | (1<<PB1) | (1<<PB2) | (1<<PB0) ; // PORTB.1 und PORTB.2 als Ausgang verwenden der rest ist automatisch ein eingang

while(1)
{
PORTB = PORTB & 0xFE;
chanal_a = ((PINB & 0x8)>>3);
chanal_b = ((PINB & 0x10)>>4);

if ((chanal_a == 1) && (a_old == 0))
{
Flanke_a = 1;
}
else
{
Flanke_a = 0;
}

if ((chanal_b == 1) && (b_old == 0))
{
Flanke_b = 1;
}
else
{
Flanke_b = 0;
}


temp_a = chanal_b & Flanke_a;
temp_b = chanal_a & Flanke_b;

links = ((temp_a == 1) & (temp_b == 0));
rechts =((temp_a == 0) & (temp_b == 1));


//zurdnung auf Ports
if ((links==1) && (rechts==0))
{
PORTB = PORTB | 0x04;
}
else
{
PORTB = PORTB & 0xFD;
}
if ((rechts==1) && (links==0))
{
PORTB = PORTB | 0x02;
}
else
{
PORTB = PORTB & 0xFB;
}
a_old = chanal_a;
b_old = chanal_b;

PORTB = PORTB | 0x01;
}
}