PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : IQ Signal (Drehgeber) Auswertung



jaepen
21.08.2008, 10:26
Hallo
Ich möchte gerne einen Dreggeber auswerten der mir ein klassisches IQ Signal liefert. (Also auch vor- rückwärts bestimmen)
Hat jemand damit Erfahrungen gemacht, oder kann mir ein paar Tipps geben?

Gruß Jens

fhs
21.08.2008, 10:45
Hi,

http://www.mikrocontroller.net/topic/6526
http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

MfG

Fred

jaepen
21.08.2008, 13:40
Hi
Ja, mit der Drehrichtungsauswertung das hab ich jetzt verstanden, mit den möglichen Zustandwechseln usw.
Aber wie mach ich die Geschwindigkeits Bestimmung? Genügt es wenn ich dazu eine der beiden Spuren nutze und einfach die Flanken "zähle" in einem bestimmten Zeitintervall. Oder ist das zu unsauber?

gruß

fhs
21.08.2008, 13:48
Hallo,

die beste Technik ist das Abfragen der Zustände in Intervallen (z.B. mit Hilfe eines Timers). Die Geschwindigkeit ergibt sich dann aus der Anzahl der (validen) Zustandsänderungen pro Zeiteinheit.

Gruß

Fred

jaepen
21.08.2008, 14:14
hi
also bis jetzt verfolge ich folgende Taktik.
Ich lasse einen Timer laufen.
Bei einer pos. Flanke speichere ich den Timerwert.
Bei der nächsten pos. Flanke speichere ich wieder den timerwert und bilde die Differenz. Damit kann ich mir dann die Zeit berechnen.
Das müsste so doch auch funktionieren oder hat das einen Nachteil?
Hier mal der Code:


_interrupt(9) void PCA0(void){ // Interrupt PCA0 P0_0 Drehzahl
char temp = SFRPAGE;
SFRPAGE = PCA0_PAGE;

P1_4=LED; //Kontrolle ob Interrrupt ausgeführt wird
x++; //Zählen wie oft der Interrupt ausgeführt wird

wert_alt=wert_neu; //Speichern des alten Werts

low =PCA0L; //Lowbyte auslesen
high=PCA0H; //Highbyte auslesen
high=high<<8; //Highbyte um 1Byte nach rechts schieben
wert_neu=high+low; //High und Lowbyte addieren

time=wert_neu-wert_alt; //Berechnung der Zeitdifferenz

CLR_CF;
SFRPAGE =temp;
}

Mein Problem ist jetzt nur das der Interrupt aus irgend einem Grund viel öfter ausgeführt wird als ich Flanken auf dem eigentlichen Interrupt pin hab und dadurch die zeitdifferenz immer 0 ist

gruß

jaepen
21.08.2008, 16:29
... also das Problem mit dem Interrupt ist gelöst.....
bleibt nur noch die Frage ob das ganze vom Prinzip her richtig ist, oder ob es da was besseres gibt

fhs
21.08.2008, 16:33
Hallo,

handelt es sich um einen optischen Encoder? Dann sollte es mit der Flankenmethode gehen. Bei einem mechanischen wäre es wegen des Prellens wesentlich besser, einfach etwa alle 10 ms nachzusehen, welches Signal anliegt und nur bei unverändertem Signal zu agieren.

Den Controller, den Du verwendest, kann ich nicht aus Deinem Code erkennen.

Gruß

Fred

jaepen
21.08.2008, 16:50
Hi
Es ist ein C8051F040 von Silabs (vorgabe).
Verwendet wird ein Optischer Encoder.

gruß

Endress
22.08.2008, 11:20
Hallo,

ich stehe vor dem selben Problem. Aber ich fürchte eher, ich seh den Wald vor lauter Bäumen nicht.

Könnte mir bitte jemand in ein paar einfach Worten erklären wie das funktioniert mit dem Timer ?

Ich benutze eine C-Control Mega 128 und hab die Abschnitte über die Timer geschichten bestimmt schon 20 mal gelesen, bekomm das aber einfach nicht in den richtigen Zusammenhang um eine Routine programmieren zu können.

Danke und Gruss, Endress

fhs
22.08.2008, 11:39
Hi Endress,

Da sagt nicht, welches Material Du schon gelesen hast. Ich kann es sicher nicht so gut erklären wie die Tutorials:
https://www.roboternetz.de/wissen/index.php/Timer/Counter_(Avr) (C Beispiele)
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer (Assembler Beispiele)
http://winavr.scienceprog.com/avr-gcc-tutorial/program-16-bit-avr-timer-with-winavr.html
Eigentlich geben auch die entsprechenden AVR-Datenblätter alle Informationen zu den Timern her.

Gruß

Fred

oberallgeier
22.08.2008, 12:00
... Ich lasse einen Timer laufen. Bei einer pos. Flanke speichere ich den Timerwert. Bei der nächsten pos. Flanke speichere ich wieder den timerwert und bilde die Differenz. Damit kann ich mir dann die Zeit berechnen ...
Genauso mache ich das auf (m)einem ATMEL mega168, 20 MHz, Gabellichtschranke am Motor, programmiert in C. Es funktioniert zumindest so gut, dass meine beiden deutlich unterschiedlichen Motoren mit dieser Messung und der Regelung sehr schön synchron laufen - bei gleichen Vorgaben.


... Das müsste so doch auch funktionieren oder hat das einen Nachteil? ...
Nachteil: bisher hatte ich die relative Ungenauigkeit des Zeitsignals als Nachteil angesehen. Meine Motoren drehen bis 750 Hz, ich bekomme bis 1500 Interrupts pro Sekunde und messe mit meinem internen Zeitsignal, das in Zeiteinheiten zu 50 µs vorliegt. Dabei treten bei den typischerweise gemessenen 20 Zeiteinheiten für eine halbe Umdrehung schon deutliche Rundungsfehler auf - denn das geht ja nur auf 5 % genau. In der Praxis hat der Gleichlauf der beiden Motoren gezeigt, dass die Regelung diese Rundungsfehler locker ausgleichen kann. Es sind ja wohl, genau genommen, keine Rundungsfehler, sondern Ungenauigkeiten, wobei der Fehlbetrag ja bei der nächsten Messung wieder ins Messergebnis aufgenommen wird (Ausnahme: Drehrichtungswechsel).

jaepen
22.08.2008, 12:17
mein motor hat ein festes getriebe, daher komm ich beim motor nur auch 60 Hz

Endress
22.08.2008, 14:22
Das ist mal wieder typisch, erst posten, dann suchen ;)

Hätt ich auch die Suche bemühen können, dennoch vielen Dank Fred, werd ich mir am Wochenende zu eigen machen ;)

jaepen
25.08.2008, 16:15
Hallo
Also hier ist mal meine selbst geschriebene Drehrichtungsauswertung.
Der Code sollte durch die Kommentare selbsterklärend sein.
Für Tipps und/oder Hinweise bin ich dankbar


////////////////////Richtungszustände
int d1=0; int d2=0; int d3=0; //Drehgeber 1....3, Vorwärts=1 Rückwärts=0
int encoder1 [4]={0,0,0,0}; //alte und neue Drehgebersignale
int encoder2 [4]={0,0,0,0}; //[0] Spur A neu [1] Spur B neu
int encoder3 [4]={0,0,0,0}; //[2] Spur B alt [3] Spur B alt

////////////////////////////////////////////////////////////////////////////////////////////////
//// MAIN //////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////

void main(void){
Init_Device(); // Initialisierung des Devices

while(TRUE){

d1=drehrichtungsauswertung (encoder1,d1); // Aufruf der Funktion zum auswerten der Drehrichtung
d2=drehrichtungsauswertung (encoder2,d2);
d3=drehrichtungsauswertung (encoder3,d3);
.
.....
........

int drehrichtungsauswertung (int *encoder,int r){

int neu=00;
int alt=00;

encoder[2] = encoder[0]; //Speichern der alten Encoderzustände
encoder[3] = encoder[1];

encoder[0]=A1; //Einlesen der neuen Encoderzustände
encoder[1]=B1;

if (encoder[0]==0 && encoder[1]==0) neu=00; //Wandeln des neuen Zustandes in dez. Zahl
if (encoder[0]==0 && encoder[1]==1) neu=01;
if (encoder[0]==1 && encoder[1]==0) neu=10;
if (encoder[0]==1 && encoder[1]==1) neu=11;

if (encoder[2]==0 && encoder[3]==0) alt=00; //Wandeln des alten Zustandes in dez. Zahl
if (encoder[2]==0 && encoder[3]==1) alt=01;
if (encoder[2]==1 && encoder[3]==0) alt=10;
if (encoder[2]==1 && encoder[3]==1) alt=11;

if (alt!=neu ) //Abfrage ob sich die Zustände des Drehgebers überhaupt geändert haben
{
if (alt==00) { // Welchen "alten" Zustand hatten wir
switch (neu){
case 01 :{ r=1; break;} //Je nach neuem Zustand vor oder rückwärts
case 10: { r=0; break;} } } //Je nach neuem Zustand vor oder rückwärts

if (alt==01) { // Welchen "alten" Zustand hatten wir
switch (neu){
case 11 :{ r=1; break;} //Je nach neuem Zustand vor oder rückwärts
case 00: { r=0; break;} } } //Je nach neuem Zustand vor oder rückwärts

if (alt==10) { // Welchen "alten" Zustand hatten wir
switch (neu){
case 00 :{ r=1; break;} //Je nach neuem Zustand vor oder rückwärts
case 11: { r=0; break;} } } //Je nach neuem Zustand vor oder rückwärts

if (alt==11) { // Welchen "alten" Zustand hatten wir
switch (neu){
case 10 :{ r=1; break;} //Je nach neuem Zustand vor oder rückwärts
case 01: { r=0; break;} } } //Je nach neuem Zustand vor oder rückwärts

//Mögliche Fehler werden nicht abgefragt. Treten diese auf, bleibt die alte Drehrichtung gespeichert
} // Klammer der if (alt!=neu ) Schleife


return r; // Die Drehrichtung wir zurück gegeben (r=1 Vorwärts, r=0 Rückwärts)
} // Ende Funktion "drehrichtungsauswertung"

Endress
27.08.2008, 16:34
Vielleicht täusche ich mich, aber kann man sich da nicht ettliches an Code sparen, wenn man die Interrupts benutzt ?

Endress
28.08.2008, 09:02
und weil wir gerade beim Thema sind:

Ich möchte nach folgender Manier einen Drehgeber auswerten:



void Timer1_ISR(void)
{
PM_WertHoch=Timer_T1GetPM();
Irq_GetCount(INT_TIM1CAPT);
Ausgabe();
}

Interruptroutinen sind definiert:

void init(void)
{
Irq_SetVect(INT_TIM1CAPT, Timer1_ISR);
}

void ausgabe(void)
{
Msg_WriteWord(PW_WertHoch);
}

void main(void)
{
while(1)
}


Ich hab den zweiten Timer ausgelassen, zur Veranschaulichung reicht einer. Eine Auswertung des Wertes kommt dann, wenn ich nen Wert bekomme....
A+ und B+ des Drehgebers liegen direkt auf PD6 und PE6, der Drehgeber wird mit 5 Volt gespeist.

Ich hab auch schon Testausgaben in die Interruptroutinen eingefügt, sie werden definitiv nicht angesprungen.
An den beiden Timern liegen die 5 Volt messbar an.

Was mach ich falsch ?

Danke und Gruss, Endress