PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Inkrementale Gabellichtschranke am PIC



impact
02.09.2007, 14:59
Hallo!
Ich überlege nun schon eine weile, wie ich am besten folgendes Problem löse.
ich habe 2 Inkrementale Gabellichtschranken (GP1A038RBK) mit Encoderscheibe. Nun möchte ich beide Lichtschranken möglichst auf einen PIC-Controller (16F88 o.ä.) mit 20MHz betreiben, meine bedenken liegen jedoch da, ob der PIC die flanken erkennt, wenn ich z.b. nebenher noch den I²C Bus anspreche. nicht das er 1-2 Takte verpasst und somit den Weg/position verfälscht. leider haben die dinger ja nur 1 Timer der "Takte" zählen kann :(

Hat vielleicht jmd hiermit schon erfahrungen gesammelt und könnte mir Tipps geben? :)

Danke im vorraus, Stefan

pctoaster
02.09.2007, 16:56
Das kommt natürlich auf die Taktfrequenz der Scheibe an.
Prinzipiell würde ich einen IRQ Timer vorsehen, der min 2x so schnell getrigget wird wie die höchste vorkommende Taktfrequenz. In diesem IRQ liest man dann beide Lichtschranken (genauer 2x2) und wertet die Positionen aus. Sonst darf nichts in diesen IRQ.
Wenn Du diesen IRQ geschrieben hast, kannst Du innerhalb dieses IRQ temporär noch einen freien Port hin und herschalten und mit einem Oszi schauen, wie schnell der IRQ läuft (Anfang IRQ-> PIN ein, Ende IRQ -> Pin aus). Dann siehst Du ob es für deine geforderte Geschwindigkeit reicht.

Gruß
pctoaster

digger
02.09.2007, 17:28
Der PortB-Change-Interrupt bietet sich hier an. (RB4-RB7)
Der meldet sich nur, wenn sich an den Eingängen was ändert, und man kann den Prozessor inzwischen mit anderen Sachen beschäftigen.

Mfg
Digger

impact
04.09.2007, 12:38
Erstmal danke für die "Denkanstöße" ;-)
auf die Idee, die Interrupt Pins von dem PIC zu benutzen bin ich noch garnicht gekommen.

Habe es nun wie folgt gemacht: Würde ich alle 4 Eingänge der 2 Drehgeber auf die Interrupt Pins legen, würde er zwar brav inner die ISR aufrufen, jedoch wüsste ich nur umständlich welcher Port nun wirklich was macht. Deshalb habe ich nun die beschaltung wie folgt gemacht.
Auf RB0 (Flankenerkennung und Interrupt) -> Drehgeber 1 eingang A
Auf RB4 (Interrupt on change) -> Drehgeber 2 eingang A
RA1,2 (nix besonderes ;)) -> Drehgeber 1, 2 eingang B
somit würde dann jeder Drehgeber seinen eigenen Interrupt aufrufen und vergleichen ist leicht gemacht. hoffe es funktioniert - werd es heute oder morgen mal ausprobieren.

pctoaster
04.09.2007, 16:44
Find ich nicht so dolle. Vor allem, wenn es Dir auf Geschwindigkeit ankommt.
Jeder Drehgeberausgang bekommt einen IRQ. Dann fragst Du ab, in welche Richtung sich dieser Porteingang geändert hat. Diesen Gesamtzustand (aus allen 4 Ports) speicherst Du anschließend ab und hast dann den Ausgangszustrand für den nächsten IRQ.
Jetzt kannst Du die Drehrichtung auswerten und die aktuelle Position absspeichern.
Alles andere außerhalb des IRQ.

Guckst Du hier (für Atmega in C). So habe ich es mal gemacht:



#define TURN_ENC_1 PD7
#define TURN_ENC_2 PD6

volatile uint8_t iTurnOldEncPort;
volatile uint8_t iTurnNewEncPort;
volatile int16_t lTurnActPosition;
volatile int16_t lTurnTargetPosition;


ISR(INT0_vect) { VerticalEncoderRead(); }

void TurnEncoderRead(void) {
iTurnNewEncPort = PIND;
if ( iTurnOldEncPort & (1<<TURN_ENC_1) ) {
if ( iTurnNewEncPort & (1<<TURN_ENC_1) ) {
if ( iTurnOldEncPort & (1<<TURN_ENC_2) ) {
if (!( iTurnNewEncPort & (1<<TURN_ENC_2) )) lTurnActPosition++;
}
else {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lTurnActPosition--;
}
}
else {
if ( iTurnOldEncPort & (1<<TURN_ENC_2) ) {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lTurnActPosition--;
else lErrorFlags |= ERR_TURN_ENC;
}
else {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lErrorFlags |= ERR_TURN_ENC;
else lTurnActPosition++;
}
}
}
else {
if ( iTurnNewEncPort & (1<<TURN_ENC_1) ) {
if ( iTurnOldEncPort & (1<<TURN_ENC_2) ) {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lTurnActPosition++;
else lErrorFlags |= ERR_TURN_ENC;
}
else {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lErrorFlags |= ERR_TURN_ENC;
else lTurnActPosition--;
}
}
else {
if ( iTurnOldEncPort & (1<<TURN_ENC_2) ) {
if (!( iTurnNewEncPort & (1<<TURN_ENC_2) )) lTurnActPosition--;
}
else {
if ( iTurnNewEncPort & (1<<TURN_ENC_2) ) lTurnActPosition++;
}
}
}
iTurnOldEncPort = iTurnNewEncPort;
}


Das "TURN" kannst Du weglassen. Das ist für eine Mehrachsensteuerung.)

Gruß
pctoaster

impact
05.09.2007, 23:50
hm, ja sicher wäre das flotter.
Dummerweise ist leider bei den 16F88 der I²C Bus auf RB1 und 4. bin mal gespannt ob er da auch nen interrupt auslöst wenn der i2c bus aktiv ist...

pctoaster
06.09.2007, 07:02
Also, sollstest Du vorhaben einen Pin für einen IRQ und I2C zu verwenden, kannst Du das vergessen. Entweder, oder.
Wenn es aber darum geht, nur bestimmte Pins als IRQ zu definieren, sollte das bei einem Pic aber auch möglich sein.
Mit den Pics kenne ich mich nicht so aus. Wenn Du keinen IRQ frei hast, mußt Du doch pollen.
Um welche Geschwindigkeiten geht es hier denn ?

Gruß
pctoaster

impact
06.09.2007, 13:05
Also bei den PICs kann man lediglich die ISR für alle Ports (RB4-7) ein bzw ausschalten. [BSF INTCON, RBIE]. das ich einen port net für i2c und als takt eingang nehmen kann ist mir auch klar ;)


Zur Geschwindigkeit, die Scheibe hat 120 Makierungen - die Motoren drehen mit 80 & 40 rpm.

für 80 Rpm -> 160 "makierungen" pro sek.
für 40 rpm -> 80 "makierungen" pro sek.

pctoaster
06.09.2007, 13:35
Da kannst Du locker pollen wie ich es anfangs beschrieben habe. Der IRQ Code, den ich oben für den AVR geschrieben habe, ist in längstens 20uS durchgelaufen.

Gruß
pctoaster