Teilbericht

Das Inhaltsverzeichnis steht hier.

Beta-Knoten: Odometrie SAUS: Beta-Knoten: Odometrie

Autoren:

Datum: 31.01.1996

Das Stichwortregister steht hier.

Zusammenfassung:

In diesem Dokument soll die Funktionalität des Beta-Knotens vorgestellt werden, der für die Odometriesensoren zuständig ist. Der Bericht gliedert sich dabei grob in die 2 Punkte: Soft- und Hardwareentwicklung.

Im ersten Teil erfolgt eine Beschreibung des Programms und der verwendeten Algorithmen. Ferner wird eine Einführung in die Funktionsweise des Timer-Interrupts gegeben, der das automatische Versenden der Odometrie-Daten in geregelten Abständen erlaubt. Anschließend sollen die notwendigen Hardwareanpassungen für die Odometrieauswertung dargelegt und allgemeine Aspekte der verwendeten Knoten behandelt werden.


Softwareentwicklung

Spezifikation

Bevor die Erläuterung zum Programm-Code beginnt, soll zunächst beschrieben werden, was das Programm leisten soll. Dazu werden die einzelnen Aufgaben des Knotens, die die Odometriesensoren betreffen, kurz informell spezifiziert.

Die Werte der im folgenden erwähnten Konstanten können im Kap. Konstanten nachgeschlagen werden. Weiterhin gilt, daß alle Nachrichten, die von oder zu dem Beta-Knoten gesendet werden, den Identifier BETA_ID tragen müssen.

Definition "Odometrie-Daten":
Die Odometrie-Daten bestehen aus 6 Werten, die
  1. die Geschwindigkeit im letzten Zeitintervall (pos. Werte = vorwärts und neg. Werte = rückwärts),
  2. die insgesamt zurückgelegte Strecke des Rollstuhls (Vorwärtsfahren erhöht und Rückwärtsfahren reduziert Zähler) und
  3. die Anzahl der unkorrekten Sensorübergänge (Fehler)
getrennt für das linke und rechte Rad erfassen. Dabei ist zu beachten, daß die Strecken- und Geschwindigkeitsangaben sich auf die gezählten Löcher beziehen. Hier gilt, daß bei jedem Lochübergang 4 Messungen der Induktivsensoren unterschieden werden können.

Aufgaben des Beta-Knotens:

Vorbemerkung zur Source-Code-Beschreibung

Das auf dem MC verfügbare MICRO/C-51 ("Kernighan & Ritchie C") läßt eine Definition eigener Datentypen oder die Einschränkung von Wertebereichen vorhandener Datentypen nicht zu. Aus diesem Grund mußten z.B. Flag-Variablen, wie send, als char deklariert werden, obwohl send lediglich die Werte von 0 bis 2 annehmen kann. Zum besseren Verständnis wurden jedoch die einzelnen, bedeutsamen Werte derartiger Flag-Variablen als Konstanten definiert. Weiterhin sind alle als char definierten Variablen in diesem Programm als "Byte"-Werte zu verstehen.

Import-Anweisungen

In mccan.h sind Prozeduren zum Senden und Empfangen von Nachrichten über den CAN-Bus vereinigt. Dieses Programm benötigt aus mccan.h: CANPutBuffer, CANTrRequest, CANRecBuffer, CANGetBuffer, CANReleaseBuffer, initCAN. Auf diese Prozeduren wird im folgenden nur oberflächlich eingegangen. Eine genaue Beschreibung liefert die Dokumentation zum "mccan.h"-Modul.

#include "mccan.h"

Konstanten

Zur übersichtlichen und flexiblen Handhabung des Programms folgt nun die Definition einiger Konstanten. Die zugewiesenen Werte ergeben sich aus ...

Somit können Änderungen, beispielsweise an den Hardware-Anschlüssen, durch Anpassung der entsprechenden Konstanten unproblematisch durchgeführt werden. /* default time-base (5333 = 1 sec.) */ #define DEFAULT 5333 /* it takes 250 cycles until timer-overflow */ #define PERIOD 5 /* beta-message-id */ #define BETA_ID 16 /* sub-id interpretation */ #define RESET_ODO 0 #define ODO_MODE 2 #define OVERFLOW 3 #define ODO_DATA 4 /* values of the flag "send" */ #define BRDC_NOT 0 #define BRDC_AUTO 1 #define BRDC_ONCE 2 /* values of the flag "overflow" */ #define NO_OFL 0 #define OFL_RIGHT_FWD 1 #define OFL_RIGHT_BWD 2 #define OFL_LEFT_FWD 3 #define OFL_LEFT_BWD 4 /* odo-connections (port 4): pin 4 and 5 = 2^4 + 2^5 = 48 = left wheel and pin 6 and 7 = 2^6 + 2^7 = 192 = right wheel */ #define ODO_LEFT 48 #define ODO_RIGHT 192 /* to shift the odo-values from P4 to the 2. and 3. bits */ #define LEFT_SHIFT 2 #define RIGHT_SHIFT 4 /* boolean */ #define FALSE 0 #define TRUE 1

Globale Variablen

Da dem Timer-Interrupt keine Variablen übergeben werden können, ist es notwendig Variablen, die sowohl im Hauptprogramm als auch in der Interrupt-Routine benötigt werden, global zu definieren. Beispiele derartiger Variablen sind : errcntright, fwdcntright, send, corrmod ... . Überdies mußten Variablen, deren Werte bis zum nächsten Interruptaufruf erhalten bleiben sollten (static), global deklariert werden (sec).

/* error-counter */ int errcntright, errcntleft; /* speed-counter : positive values = agent moves forward negative values = agent moves backward */ int fwdcntright, fwdcntleft; /* distance-counter : increasing values = forward decreasing values = backward */ int distright, distleft; /* flag : send=BRDC_ONCE -> broadcast an odo-msg. after the next period (once !) send=BRDC_AUTO -> periodic broadcasting send=BRDC_NOT -> do not broadcast */ char send; /* interrupt-counter */ int sec; /* time-base (5333 = 1 sec) : corrmod = (533 * mode) oder (DEFAULT=5333) */ int corrmod; /* CAN-bus-datas */ char datas[2];

Timer Interrupt

Der Timer-Interrupt (hier: Timer-0-Interrupt = Interrupt 1) wird bei jedem Überlauf des TL0-Registers aufgerufen (Vgl. Kap. Timer) und soll somit das periodische Versenden der Odometrie-Daten realiseren.

Der Zusatz "using 1" gibt dabei noch zusätzlich an, welche Registerbank (0 bis 3) angesprochen werden soll. Bei 8051-Architekturen kann bei jeder Funktion durch die Angabe des Attributes using angegeben werden, welche Register-Bank gewählt werden soll. Wird keine Angabe gemacht, wird automatisch die Register-Bank 0 verwendet. Nach Beendigung der Funktion wird zur vorherigen Einstellung zurückgewechselt. Das using-Attribut wird im allgemeinen dazu benutzt, in Interrupt-Funktionen Registerkonflikte zu vermeiden.

Bei der Programmentwicklung traten an dieser Stelle erhebliche Probleme auf, da die zunächst aus einem vorliegenden Beispiel-Programm übernommene Einstellung "using 2" für eine Vielzahl von Programmabstürzen verantwortlich war. So hatten beispielsweise kleinste Änderungen/Verkürzungen der Interrupt-Routine oder eine Umstellung in der Variablendeklaration dramatische Programmreaktionen zur Folge. Durch Experimentieren sind wir schließlich zur Einstellung "using 1" gekommen, die sich für unsere Zwecke als unproblematisch und sicher erwies.

/* function : timer0() interrupt 1 description : timer-interrupt to broadcast the odo-messages in periodical time-steps last change : 25.01.96 */ timer0() interrupt 1 using 1 { Es folgt die Deklaration der lokalen Variablen. Darüberhinaus werden mit EA=0 sämtliche Interrupts ausgeschaltet, damit ausgeschlossen ist, daß der Interrupt wieder durch einen Interrupt unterbrochen wird. /* CAN-bus-variables */ char data[8]; /* disable interrupts */ EA=0; Der Interrupt-Zähler (sec) wird in jedem Interrupt um eins erhöht. Erst wenn der Interrupt-Zähler größer als die gewählte Zeitbasis (corrmod) ist, kann eine Odometrie-Nachricht gesendet werden. Mit corrmod wird demzufolge festgelegt, wie groß das Zeitintervall zwischen der Übermittlung zweier Odo.-Nachrichten ist. Gilt sec > corrmod, dann wird der Interrupt-Zähler zurückgesetzt und es wird weiter geprüft, ob das Versenden der Odometrie-Daten auch gefordert wurde. Diese Information ist dem Sendeflag (send) zu entnehmen. Steht dieses auf einmaliges oder automatisches Senden, also nicht auf BRDC_NOT, sollen die aktuellen Odo.-Daten gesendet werden. /* interrupt-counting: if the time-base (corrmod) is reached, then broadcast odo-msg. */ if (sec++>corrmod){ /* reset interrupt-counter */ sec = 0; if (send != BRDC_NOT) { Um eine Odo.-Nachricht abzusetzten, ist es zunächst einmal erforderlich, die Strecken-, Geschwindigkeits- und Fehlerzähler als Daten-Bytes der Nachricht zuzuweisen. Mit CANPutBuffer und CANTrRequest erfolgt anschließend die eigentliche Übermittlung. /* initialize CAN-bus-message */ data[0] = fwdcntleft; data[1] = fwdcntright; data[2] = distleft >> 8; /* high-byte of distleft */ data[3] = distright >> 8; /* high-byte of distright */ data[4] = distleft; /* low-byte of distleft */ data[5] = distright; /* low-byte of distright */ data[6] = errcntleft; data[7] = errcntright; /* broadcast odo-message */ CANPutBuffer(0, BETA_ID, ODO_DATA, 8, data); CANTrRequest(); Sollten die Odometrie-Daten lediglich einmal gesendet werden, wird das Sendeflag entsprechend zurückgesetzt. /* should the odo-msg. be broadcast only a single time then reset flag (bit BRDC_DATA is masked out by &) */ send=send & (255-BRDC_DATA); } Am Ende müssen die Geschwindigkeitszähler zurückgesetzt werden, damit sich die Geschwindigkeiten immer auf das letzte Intervall beziehen. /* reset speed-counter */ fwdcntleft=0; fwdcntright=0; } Am Ende der Interrupt-Routine werden die Interrupts wieder zugelassen. /* enable interrupts */ EA=1; }

Hauptprogramm

Beginn des Hauptprogramms, das im wesentlichen für das Empfangen und Auswerten von Nachrichten sowie für die Odometrie-Messungen zuständig ist. Dies bedarf jedoch zuvor der Deklaration einiger Variablen.

Bemerkenswert dabei ist, daß das Array data[8] im Programm nie benutzt wird, jedoch das Fehlen dieser Deklaration den Empfang von Nachrichten verhindert. Ohne das Array sind sämtliche nachfolgend definierte Variablen bei der Entgegennahme von (korrekt versendeten) Nachrichten mit der Funktion CANGetBuffer(...) stets Null, was dementsprechend eine Auswertung von Nachrichten unmöglich macht. Mit der Deklaration des Feldes tritt dieser Fehler nicht auf.

main() { /* CAN-bus-variables */ char data[8]; /* do not delete !!! even if this variable seems to be unnecessary otherwise it is not possible to receive a CAN-message */ char length; char rtr; char id; char sub; /* odo-variables: 3. and 4. Bit = current measurement both first Bits = previous measurement */ char valright, valleft; /* overflow-flag for distance-counters : overflow=NO_OFL -> no overflow overflow=OFL_RIGHT_FWD -> distright-ofl. while move forward overflow=OFL_RIGHT_BWD -> distright-ofl. while move backward overflow=OFL_LEFT_FWD -> distleft-ofl. while move forward overflow=OFL_LEFT_BWD -> distleft-ofl. while move backward */ char overflow; Die nachfolgende Initialisierung der Variablen und Register darf nicht von einem Interrupt unterbrochen werden, da auch für den Interrupt erst entscheidende Variablen gesetzt werden müssen. Deshalb werden alle Interrupts kurzfristig ausgeschaltet, indem im IEN0-Register das EA-Bit (Interrupt-Enable) auf Null gesetzt wird. /* disable interrupts */ EA=0;

Variablen-Initialisierung

Das Überlaufflag wird auf NO_OverFLow und die Odometrie-Variablen auf Null gesetzt.

overflow=NO_OFL; fwdcntright=fwdcntleft=0; errcntright=errcntleft=0; distright=distleft=0; valleft=valright=0; Der Interrupt-Zähler für das automatische Senden der Odometrie-Daten wird mit Null initialisiert. Ferner wird die Zeitbasis (corrmod), die das periodische Versenden der Odometrie-Zähler realisiert auf den DEFAULT-Wert gesetzt, was jedoch zunächst keine große Auswirkung hat, da das Sendeflag (send) im nächsten Schritt auf BRoaDCast_NOT gestellt wird. sec=0; corrmod=DEFAULT; send=BRDC_NOT;

Erläuterung der Timer-Funktionsweise

Im MC P8xC592 stehen insgesamt drei 16-Bit Timer (T0, T1 und T2) zur Verfügung. Die Timer 0 und 1 können als Ereignis- oder aber als Zeitzähler eingesetzt werden. Das Zählen von Ereignissen bedeutet in diesem Zusammenhang, daß fallende Pegel an bestimmten Portpins (Pin 4 bzw. 5 vom Port 3) zur Inkrementierung des Timers führen. Die Funktionsweise kann mittels dreier Modi definiert werden:

Mode 0:
8-Bit Zeit- oder Ereigniszähler.
Mode 1:
16-Bit Zeit- oder Ereigniszähler.
Mode 2:
8-Bit Zeit- oder Ereigniszähler mit automatischem "reload" bei einem Überlauf.
Wir wollen den Timer 0 als Zeitzähler im Mode 2 verwenden, um in geregelten Abständen einen Timer-Interrupt auszulösen. Die interne Arbeitsweise des Timers läßt sich folgendermaßen darstellen:

Das LSB des Timer 0 (tl0) wird mit einer Frequenz von 1,33 MHz inkrementiert. Wird tl0 zu Beginn z.B. mit 5 initialisiert, müssen 250 Takte abgewartet werden, bis der erste Überlauf erfolgt. Beim Überlauf wird tl0 auf den "reload"-Wert (th0 ebenfalls mit 5 initialisiert) gesetzt, so daß wiederum 250 Takte gezählt werden müssen, bis zum nächsten Überlauf usw.. Bei jedem Überlauf wird der Timer-Interrupt aufgerufen. Damit läßt sich jetzt auch die Festlegung der Zeitbasis (corrmod) für das periodische Versenden der Odometriedaten erklären:

Wenn die Taktfrequenz bei 1.33 MHz liegt und sich alle 250 Takte einmal ein Überlauf ereignet, folgt daraus, daß 5333 (=1.333.333/250) mal in der Sekunde der Timer-Interrupt ausgelöst wird. Ein corrmod-Wert von beispielsweise 533 gestattet das Versenden der Daten nur jedes 533ste Mal, wodurch etwa alle 5333/533 = 0,1 Sekunden gegebenenfalls eine Nachricht zum PC gesendet werden kann. Die momentane Belastung des CAN-Busses beeinflußt jedoch die Ankunft der vom Knoten übermittelten Daten, so daß die Einhaltung der gewünschten Zeitintervalle zwischen den Odometrie-Nachrichten nicht garantiert werden kann. Weitere Einzelheiten und eine Erklärung des Timer T2 liefert die Beschreibung zum CAN-Modul-592.

Register-Initialisierung

Indem im Register TMOD das 2. Bit gesetzt wird, stellt man den Timer 0 auf den Mode 2 ein. Ferner wird der eigentliche Timer-Counter TL0 (LSB) und der "reload"-Wert TH0 (HSB) mit dem anfangs definierten PERIOD-Wert (=5) initialisiert. Das Setzten von ET0 und TR0 bewirkt, daß der Timer 0 eingeschaltet und dann gestartet wird.

/* timermode is set to "auto-reload" */ TMOD = TMOD | 2; /* initialize timer-interrupt: first value and "reload"-value. The first value is increased until it overflows and then intialized with the reload-value */ TL0 = PERIOD; TH0 = PERIOD; /* enable Timer-0 and Timer-0-Run */ ET0 = 1; TR0 = 1; CAN-Controller-Initialisierung: Es werden die Werte für Acceptance-Mask und -Code übergeben, so daß nur die für den Beta-Knoten relevanten Nachrichten Beachtung finden. /* initialize CAN-controller */ initCAN(0,BETA_ID); Vor dem Beginn der Hauptroutine werden die Interrupts wieder eingeschaltet. /* enable interrupts */ EA=1;

Auswertung empfangener Nachrichten

Im folgenden sollen ankommende Nachrichten ausgewertet und die Odometriemessungen gezählt werden. Diese Aufgaben des Knotens müssen stets wiederholt werden, so daß sie in eine Endlos-Schleife eingeschlossen sind.

while (1) { Die Evaluation ankommender Nachrichten setzt voraus, daß zunächst geprüft wird, ob eine neue Nachricht vorliegt und sollte das der Fall sein, daß diese entgegengenommen wird. Mit der Prozedur CANGetBuffer wird eine Message empfangen und deren Inhalt in die angegebenen Variablen geschrieben, so werden z.B. die eigentlichen Daten in das Array datas geschrieben.

Die exakte Beschreibung der aus mccan.h importierten Funktionen CANRecBuffer, CANGetBuffer und CANReleaseRecBuffer kann der Dokumentation zu mccan.h entnommen werden.

if (CANRecBuffer()) { /* receive message */ CANGetBuffer(&rtr, &id, &sub, &length, datas); CANReleaseRecBuffer(); Sämtliche Nachrichten, die für den Beta-Knoten bestimmt sind, tragen den Identifier BETA_ID. Anhand der Sub-ID erfolgt dann die weitere Differenzierung, die es erlaubt, einzelne Funktionen bezüglich der Odometrie anzusprechen. if (id==BETA_ID) { Ist die Sub-ID gleich RESET_ODO, werden alle Strecken- und Fehlerzähler auf Null gesetzt und das Sendeflag (send) wird auf "nicht Senden" gestellt. /* reset Odo: reset distance- and error-counter and stop broadcasting */ if (sub==RESET_ODO) { distleft=distright=errcntright=errcntleft=0; send = BRDC_NOT } Eine Sub-ID von ODO_DATA soll den Knoten veranlassen, die Odometrie-Daten einmal zu senden. Dazu müssen sowohl die Geschwindigkeitszähler als auch der Interruptzähler (sec) auf Null gesetzt werden. Weiterhin wird die Zeitbasis (corrmod) mit dem DEFAULT-Wert (5333 entspricht einer Sekunde) und das Sendeflag mit BRDC_ONCE initialisiert. Sollte zuvor das automatische Senden verwendet worden sein, wird dieses nach dem einmaligen Senden der Odometrie-Daten ausgestellt.

In dieser zu empfangenen Nachricht sollte das RTR-Bit auf 1 gesetzt sein, so daß eine Anforderung der Odo.-Daten bei gleichzeitigem Versenden der entsprechenden Nachricht unterdrückt werden kann.

/* broadcast odo-information (once) */ if (sub==ODO_DATA) { fwdcntleft=fwdcntright=0; sec=0; corrmod = DEFAULT; send = BRDC_ONCE; } Sub-ID=ODO_MODE bedeutet, daß die Odometrie-Daten periodisch gesendet werden sollen gemäß dem angegebenen Modus. Zunächst müssen auch hier wieder die Geschwindigkeits- sowie der Interruptzähler zurückgesetzt werden. Anschließend wird geprüft, ob der vom PC übermittelte Modus, der im ersten Datenbyte von datas steht, gleich Null ist. Sollte dies der Fall sein, wird das Senden ausgestellt. Anderenfalls wird die Zeitbasis (corrmod) auf 533 * dem gesendeten Modus gestellt, so daß z.B. ein Modus-Wert von 1 einer zehntel Sekunde entspricht. Schließlich muß noch das Sendeflag auf das automatische, periodische Versenden der Odometrie-Daten umgestellt werden. /* broadcasting the odo-messages in periodical time-steps according to the transmitted mode (datas[0]) */ if (sub==ODO_MODE) { /* initialize speed- and interrupt-counter */ fwdcntleft=fwdcntright=0; sec=0; if (datas[0]==0) { /* disable broadcast */ corrmod = DEFAULT; send = BRDC_NOT; } else { /* the corrected mode is set according to the transmitted mode * 533 */ if (datas[0] > 0) corrmod = 533 * datas[0]; send = BRDC_AUTO; } } } }

Odometriemessungen

Zur Erkennung von Bewegung und Bewegungsrichtung separat für die beiden Antriebsräder sind auf jeder Seite des Rollstuhls 2 Induktivsensoren notwendig. Insgesamt gibt es somit 4 dieser Sensoren, wobei jeder Sensor einen Port-Pin benötigt.

Die Induktivsensoren für die Odometrie-Messungen wurden an dem digitalen Port 4 angeschlossen. Der Zustand der Sensoren kann somit aus dem Register P4 entnommen werden. Die Konstanten ODO_LEFT und ODO_RIGHT geben an, an welchen Pins die Sensoren des jeweiligen Rades angeschlossen sind. Ein Port-Pin wird auf LOW (0) gesetzt, wenn ein Loch detektiert wird, sonst auf HIGH (1).

Um das Auslesen des Eingabeports zu ermöglichen, müssen zunächst die entsprechenden Bits für das linke und rechte Rad auf HIGH gesetzt werden, weil der Port sonst u.U. einen so geringen Widerstand aufweist, daß keine meßbare Spannung (>2.5 V) am Port anliegt.

P4 = P4 | ODO_RIGHT | ODO_LEFT; In den Variablen valleft und valright soll jeweils getrennt nach rechter und linker Seite die aktuelle Messung der Sensoren gespeichert werden. Diese aktuelle Messung soll in das 3. und 4. Bit von valleft bzw. valright geschrieben werden. Damit wird eine Verschiebung der Odometrie-Bits von P4 notwendig. Diese Verschiebung ist abhängig davon, an welchen Pins die Sensoren angeschlossen sind, und wird über die Konstanten LEFT_SHIFT und RIGHT_SHIFT festgelegt.

Da für die Bewegungserkennung aber die aktuellen Werte nicht ausreichen, müssen auch die Sensordaten der vorherigen Messung gespeichert und mit den aktuellen Werten verglichen werden. Deshalb werden die Variablen valleft und valright bevor die neuen Werte gespeichert werden um 2 Bits nach rechts geshiftet, so daß die Werte der vorherigen Messung im 1. und 2. Bit beider Variablen gesichert sind.

valright = ((P4 & ODO_RIGHT) >> RIGHT_SHIFT) | (valright >>2); valleft = ((P4 & ODO_LEFT) >> LEFT_SHIFT) | (valleft >>2); Diese Speicherung der Odometrie-Sensoren erlaubt es nun, die Bewegung des rechten und linken Rades anhand von charakteristischen Bitmustern der ersten 4 Bits von valright und valleft zu interpretieren. Die Auswertung wird zunächst für das rechte Rad und anschließend analog für das linke Rad vorgenommen.

Auswertung: Rechtes Rad

Zunächst wird geprüft, ob sich das rechte Rad bewegt hat, d.h. ob die aktuelle Messung mit der vorherigen nicht übereinstimmt, da nur in diesem Fall eine nähere Auswertung Sinn macht. Demzufolge werden die ersten beiden Bits mit den 3. und 4. Bits verglichen.

if ((valright >>2) != (valright & 3)) {

aktuelle vorherige
Messung Messung
00 10

Bitmuster: Vorwärtsbewegung
Ein valright-Wert von 2 (Vgl. Tabelle Bitmuster-Bsp.1), 4, 11 und 13 ist signifikant für eine Vorwärtsbewegung des Rades. Jeder Lochübergang erzeugt somit 4 Erhöhungen der Geschwindigkeitszähler (fwdcntright). Bevor auch der Streckenzähler (distright) für das rechte Rad inkrementiert wird, erfolgt die Prüfung, ob ein Überlauf droht, was im positiven Fall zum Setzen des Überlaufflags und zur Initialisierung des Streckenzählers mit Null führt. /* the right wheel moves forward */ if (valright==2 || valright==4 || valright==13 || valright==11) { fwdcntright++; /* if distance-counter overflows then set overflow-flag */ if (distright==32767) { overflow=OFL_RIGHT_FWD; distright=0; } distright++; } Ein valright-Wert von 8 (Vgl. Tabelle Bitmuster-Bsp.2), 1, 7 und 14 zeigt eine Rückwärtsbewegung an.

aktuelle vorherige
Messung Messung
10 00

Bitmuster: Rückwärtsbewegung
Die weitere Befehlsfolge ist analog zu der der Vorwärtsbewegung. /* the right wheel moves backward */ if (valright==8 || valright==1 ||valright==7 || valright==14 ||) { fwdcntright--; /* if distance-counter overflows then set overflow-flag */ if (distright==-32767) { overflow=OFL_RIGHT_BWD; distright=0; } distright--; } Ist der valright-Wert 3, 6, 9 oder 12 entspricht dies einem fehlerhaften Sensor-Verhalten, da beide Induktivsensoren gleichzeitig eine Änderung registrieren. Ein Fehlerzähler (errcntright) für das rechte Rad zählt das Auftreten dieser unerwünschten Bitmuster. Stark steigende Werte des Fehlerzählers weisen auf eine schlechte Einstellung der Induktivsensoren hin! /* faulty counter-changes */ if (valright==3 || valright==6 || valright==9 || valright==12) errcntright++; } Eine Übersicht der möglichen Bitmuster ist der Tabelle Bitmuster zu entnehmen.

valright Bedeutung
0000 keine Bewegung
0001 rückwärts
0010 vorwärts
0011 Fehler
0100 vorwärts
0101 keine Bewegung
0110 Fehler
0111 rückwärts
1000 rückwärts
1001 Fehler
1010 keine Bewegung
1011 vorwärts
1100 Fehler
1101 vorwärts
1110 rückwärts
1111 keine Bewegung

Bitmuster-Übersicht
Auswertung: Linkes Rad Analog zur Auswertung des rechten Rades. /* Does something change at the left wheel ? */ if ((valleft >>2) != (valleft & 3)) { /* the left wheel moves forward */ if (valleft==2 || valleft==4 || valleft==13 || valleft==11) { fwdcntleft++; /* if distance-counter overflows then set overflow-flag */ if (distleft==32767) { overflow=OFL_LEFT_FWD; distleft=0; } distleft++; } /* the left wheel moves backward */ if (valleft==8 || valleft==1 ||valleft==7 || valleft==14 ||) { fwdcntleft--; /* if distance-counter overflows then set overflow-flag */ if (distleft==-32768) { overflow=OFL_LEFT_BWD; distleft=0; } distleft--; } /* faulty counter-changes */ if (valleft==3 || valleft==6 || valleft==9 || valleft==12) errcntleft++; } Wurde das Überlauf-Flag (overflow) gesetzt, wird dieses zum PC gesendet und zurückgesetzt. /* if an overflow exists then send a message to the PC */ if (overflow != NO_OFL) { CANPutBuffer(0, BETA_ID, OVERFLOW, 1, &overflow); CANTrRequest(); /* reset overflow-flag */ overflow=NO_OFL; }

Hardwareentwicklung

In diesem Kapitel soll zunächst auf einige allgemeine Eigenarten der verwendeten Knoten eingegangen werden. Anschließend erfolgt dann die den Beta-Knoten betreffende Hardwarebeschreibung.

Digitale Ein-/Ausgabeports

Bei der Benutzung der Ports bietet sich hauptsächlich der Port 4 an, da Experimente mit den anderen Ports oft Abstürze des Knotens verursachten. Die übrigen Ports sind gemäß Phytec-Unterlagen (Schaltplan) auch für andere IO-Aufgaben verwendet, z.B. RAM-MC-IO.

Die Ports müssen um ein korrektes Eingangssignal zu bekommen zunächst auf HIGH gesetzt werden, d.h. man schreibt zunächst eine 1 in das Bit, welches man auswerten will und liest es direkt danach wieder aus. Diese Vorgehensweise ist zumindest bei hochohmigen Eingangssignalen notwendig, denn u.U. kann über den Port soviel Strom abfließen, daß die Eingangsspannung zum Schalten nicht mehr ausreicht.

Beta-Knoten

Der Beta-Knoten übernimmt die Kontrolle über die Odometriesensoren, also über die Induktionsschalter. Die Beschreibung des Widerstandsnetzwerks läßt sich aus dem folgenden Absatz entnehmen.

Induktionsschalter

Die Induktionsschalter (bzw. Induktivsensoren ) reagieren, wie der Name schon sagt, durch Induktion auf die Anwesenheit von Metall. Dazu wird ein hochfrequentes elektrisches Feld erzeugt, wobei in dieses Feld eindringendes Metall den Polaritätswechsel erschwert. Dies registrieren die Induktionsschalter und liefern ein digitales Ausganssignal (ein bzw. aus). Die vier digitalen Ausgangssignale zwischen 0V und 9V werden dann über die vier auf der Karte aufgebauten Spannungsteiler auf etwa 3,5 V heruntergeteit. Die nahe an der Schaltschwelle von 2,5 V liegende Spannung erfordert bei der Software vor dem Auslesen das Setzen auf 5 V, ist die Spannung am Mittelabgriff des Spannungsteilers ausreichend, so bleibt das Signal in jedem Fall über 3,5 V, andernfalls fließt der Strom über den Spannungsteiler (evtl. auch über die Sensoren) nach Masse ab. Die Schaltskizze des Beta-Knotens ist in der Abbildung Schaltskizze dargestellt.

Die Induktionsschalter sind aus technischen Gründen (s.o.) an Port 4 angeschlossen. (Abbildung: Schaltskizze des Beta-Knotens )

Lochscheibe

Die Messung der Geschwindigkeit und der zurückgelegten Entfernung geschieht auf dieselbe Weise, wie sie in Mäusen und anderen Eingabegeräten tausendfach zum Einsatz kommt: Es gibt zwei Sensoren mit je zwei Zuständen, die z.B. nach dem Lichtschrankenprinzip das Eintreffen von Licht oder die Okklusion durch eine Lochscheibe (oder auch Schlitzscheibe) erkennen können.

Die Sensoren sind so angebracht, daß einer gerade umschaltet, wenn der andere gerade "in der Mitte " des jeweiligen Zustandes ist. So wird erreicht, daß die zeitlichen Abstände zwischen den Zustandswechseln des Sensorenpaares gleich groß sind. Dies ist natürlich nur dann möglich, wenn die An- und die Auszeiten des Sensors gleich lang sind. Diese Forderung ist schon aus Performace-Gründen wichtig, ganz zu schweigen davon, daß dies die Justage enorm erleichtert.

Zwei Beispiele: Ist das An-Aus Verhältnis 1:1 reichen im Idealfall 4 periodische Zustandsabfragen, um alle Sensorübergänge zu registrieren. Ist das Verhältnis 4 : 1, was von unser ersten Lochscheibe eher noch übertroffen wurde, so muß man im günstigsten Fall 10 Abfragen machen.

Die Lochscheibe wurde zu Beginn des Projekts mit 30 Löchern zu 16 mm gebohrt. Dies schien aus zwei Gründen recht sinnvoll zu sein: Zum einen waren die Löcher somit etwas größer, als der Durchmesser der Sensoren, zum anderen waren damit Metall und Löcher im Bereich der Sensoren in etwa gleichverteilt. Für Lichtsensoren beispielsweise wäre dies auch genau die richtige Größe gewesen. Die Induktivsensoren schalten jedoch schon bei entfernter Anwesenheit von Metall. Da der Schaltpunkt sich nicht einstellen ließ, versuchten wir zunächst, das Problem dadurch zu umgehen, daß wir die Sensoren weiter von der Lochscheibe entfernten. Dadurch "verschwomm" aber der Unterschied zwischen Loch und Metall, d.h. war die Lochscheibe nur etwas weiter von dem Sensor entfernt (weil das Rad unruhig lief oder eine Kurve gefahren wurde), so wurde überhaupt kein Metall mehr detektiert. Dies führte dann zu solchen Ergebnissen, daß die Odometrie für einen halben Radumlauf gut funktionierte, während sie bei der nächsten halben Radumdrehung überhaupt keine Bewegung mehr registrierte.

Im 3. Projekt-Semester, als der Rollstuhl quasi zum ersten Mal auf seine Odometrie- und Lenkwinkeleinstellungsmöglichkeiten getestet wurde, sind solche Effekte aufgetreten, nachdem der Rollstuhl auf den Rädern umherbewegt wurde. Zu dieser Zeit blieben noch zwei Optionen offen: Das Anpassen des Metall-Loch-Verhältnisses durch das Aufbohren der Löcher oder die Verwendung neuer Sensoren. Wir entschieden uns aus Kosten und Zeitgründen für die erste Lösung, obwohl die Gefahr bestand, daß das Verhältnis immer noch nicht ausgewogen genug war. Die bisherigen Tests geben jedoch Grund zu Optimismus.