EM2010WSP05

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche
Lego Brick, der gerade einen RFID Tag mit dem CODATEX RFID Sensor liest

Hinweis

Da aktuell das VS-Wiki nicht fehlerfrei funktioniert, bzw keine Bilder hochgeladen werden können, haben wir ein seperates Wiki aufgesetzt: wiki.sebblog.de[1]. Wenn das VS-Wiki wieder voll einsatzfähig ist werden wir den Inhalt hierher umziehen.


Beschreibung der Hardwareumgebung

Der NXT[1] von Lego ist ein Mikrocomputer aus der Mindstorms Serie mit einem 32bit ARM-Prozessor und einem kleinen LCD-Display. Es können analoge sowie I²C-Sensoren angeschlossen werden, außerdem kann der NXT über Bluetooth mit anderen Geräten kommunizieren. Die I²C Schnittstelle erlaubt die Verwendung von Sensorhardware von Drittherstellern. Mit Lego verfügt der NXT über eine vielseitige Basis, mit der sehr einfach ein komplettes System aufgebaut werden kann, dessen Teile zudem voll wiederverwendbar sind. Dadurch erfreut sich die Mindstorms Plattform besonders in der Lehre einer großen Beliebtheit. Wegen dieser Beliebtheit entstanden zahlreiche Open Source Programmierumgebungen für den NXT, die es z.B. ermöglichen NXT-Programme in C, C++ oder Java zu entwickeln.

I²C

Der I²C-Bus ist ein von Philips entwickelter serieller Master-Slave-Datenbus. Je nach Spezifikation unterstützt I²C Systeme mit bis zu 112 oder 1136 Knoten. I²C benötigt zwei Signalleitungen, eine für das Clock- und eine für das Datensignal.

NXT Connector

Zum Verbinden des Bricks mit Aktoren und Sensoren wird ein 6-Pin RJ-Anschlusskabel verwendet.

Pinbelegung
Pin Name Funktion Farbe
1 ANA Analog, +9V weiß 160px
2 GND Masse schwarz
3 GND Masse rot
4 POWER +4.3V grün
5 DIGIAI0 I²C Clock (SCL) gelb
6 DIGIAI1 I²C Data (SDA) blau

Beschreibung der Programmierschnittstellen und Tools

leJOS

leJOS[2] ist ein alternatives Betriebssystem für den NXT. Die Besonderheit von leJOS ist die enthaltene Java Virtual Machine, die es ermöglicht, Java-Anwendungen auf dem Brick auszuführen. Das Projekt ist die umfangreichste Open Source Programmierumgebung für Mindstorms. Neben den Treibern für die Lego Sensorhardware sind auch Treiber für zahlreiche Geräte von Drittherstellern implementiert. Außerdem gibt es leJOS-Plugins für die Entwicklungsumgebungen Eclipse und NetBeans, welche das direkte Übertragen der Programme auf den Brick ermöglichen. Obwohl der letzte Release Anfang 2009 war ist das Projekt sehr aktiv. Besonders die Treiberschicht, die ebenfalls bei nxtOSEK verwendet wird, wurde mehrmals stark verändert. Der nxtOSEK Release, der leJOS Release und der aktuelle leJOS trunk verfügen alle über eine stark unterschiedliche Version dieser Treiberschicht.


Tools

leJOS bietet einen Installer sowie Plugins für Eclipse und NetBeans an.


Treiberschicht

Datei:Lejosdriver.png
leJOS I²C Treiber im Klassendiagramm

Alle leJOS I²C Treiber sind ähnlich aufgebaut. Sie sind von der Klasse I2CSensor abgeleitet, deren Methoden über JNI auf die darunterliegende native Treiberschicht zugreift. Diese in C programmierte Schicht übernimmt die I²C Kommunikation. Die Klasse I2CSensor abstrahiert und vereinfacht also die nativen Funktionen ähnlich wie die Ecrobot API bei nxtOSEK. Neue Treiber lassen sich dementsprechend sehr einfach umsetzen, indem man eine neue Klasse von I2CSensor ableitet und Methoden implementiert, die die Funktionalität des Sensors abstrahiert.

Unter den implementierten Treibern befindet sich auch ein funktionierender Treiber für den Codatex-RFID Sensor, welcher sich anbietet, um ihn für nxtOSEK zu portieren.

nxtOSEK

Das Betriebssystem für den NXT basiert auf einer OpenSource-Implementierung von dem aus dem Automotive Bereich stammenden Echtzeitbetriebssystem für Steuergeräte in Autos. Es kann auf dem Lego Mindstorms NXT-Brick als alternatives Betriebssystem installiert werden. Vielmehr ist es hierbei so, dass ein sehr rudimentäres Betriebssystem auf den Brick geflashed wird, das es ermöglicht, seine Anwendung, die mit den Betriebssystembibliotheken zusammen in ein Executable verpackt ist, zu flashen und auszuführen.

Tools

Für die Entwicklung von Software für das Echtzeitbetriebssystem nxtOSEK sind folgende Komponenten nötig:

  • Der Lego-Treiber:[3]
  • cygwin [4]
  • Crosscompiler GNUARM [5][6]
  • nexttool[7]
  • Die nxtOSEK Sourcefiles und nötige Bibliotheken[8]
  • sg.exe (Systemgenerator, ist seit Anfang 2010 nicht mehr enthalten)[9]

Der Konfigurationsaufwand ist gleich 0, wenn man alles in C:\cygwin installiert respektive entpackt. Das nexttool also in C:\cygwin\nexttool, GNUARM in C:\cygwin\GNUARM (wobei bei der Installation von GNUARM darauf geachtet werden sollte, NICHT die Cygwin-DLL mitzuinstallieren, da es diese ja bereits gibt) und nxtOSEK in C:\cygwin\nxtOSEK. Der Systemgenerator sg.exe sollte in C:\cygwin\nxtOSEK\toppers_osek\sg liegen.

Es muss sichergestellt werden, dass in der Datei tool_gcc.mak in cygwin/nxtOSEK/ecrobot die Pfade zu GNUARM und dem nexttool richtig gesetzt sind. Zum Beispiel so:

   # specify GNU-ARM root directory
   ifndef GNUARM_ROOT
   GNUARM_ROOT = /cygdrive/c/cygwin/GNUARM
   endif
   
   # specify NeXTTool root directory
   ifndef NEXTTOOL_ROOT
   NEXTTOOL_ROOT = /cygdrive/c/cygwin/nexttool
   endif

Kompilieren und Übertragen von Applikationen

Befindet man sich in dem Ordner, in dem ein gültiges und an die Pfade angepasstes Makefile liegt, tippt man ein:

make all

um sein Programm zu Kompilieren und gegen die Ecrobot-Bibliothek (C/C++) zu linken.

bash biosflash.sh
bash appflash.sh

Mit biosflash.sh kommt das BIOS auf den Brick (und überschreibt ggf. eine vorhandene andere Firmware - der Aufruf ist also nur bei Erstkontakt mit nxtOSEK nötig); mit appflash.sh wird das Applikationsimage mittels dem nexttool auf den Brick kopiert. Damit das Flashen übrigens erst funktionieren kann, muss man den Brick in den Firmware Update Modus versetzen. Das geht, in dem man die Tasten

 <

und

 []

gleichzeitig gedrückt hält, bis auf dem Bildschirm die "Bereit"-Nachricht erscheint.

Programmierschnittstellen

Datei:Nxt-osek-schichten.png
Schichten von nxtOSEK

Dem Anwendungsprogrammierer steht eine abstrahierende Schicht zur Verwendung von vorhandener Sensor- und Aktorhardware zur Verfügung. Sie heißt Ecrobot.

Ecrobot API

Dieses Interface vereinfacht den Zugriff auf die I/O-Treiber für etwa den Lichtsensor, den Motor und dessen Umdrehungsmesser, oder eben den RFID-Sensortreiber. Es liegt als Schicht über der I/O-Treiberschicht. Die I/O-Treiberschicht unter Ecrobot kann jedoch auch direkt vom Programmierer der Applikation verwendet werden. Ecrobot gibt es sowohl in einer C- Ausführung als auch in einer objektorientierten C++-Implementierung, die ihrerseits eine objektorientierte Abstraktion der C-Funktionen von Ecrobot bietet - ähnlich ist es bei LeJOS mit seinen Java-Klassen.

Konzeption für die Einbindung von neuen Treibern

Datei:Ecrobot layers.png
Der RFID-Treiber im Schichtenmodell
Das Einbinden von neuen Gerätetreibern in das System lässt sich anhand eines einfachen Schaubildes illustrieren: Man stellt sich das Treibermodell in Schichten vor. Dann gilt für leJOS und nxtOSEK grundsätzlich unten beschriebenes Vorgehen.

nxtOSEK

Bei nxtOSEK bildet das Ecrobot Interface die Hardwarefunktionen des NXT Bricks ab, neue Treiber sollten also in dieser Schicht angesiedelt werden. Das Ecrobot Layer besteht aus einer C-Api und einer C++-Api, die weitestgehend die Funktionalität der C-Api objektorientiert abbildet.


Für C

  • Die neue Treiberdatei wird im Ordner nxtOSEK\ecrobot\c erstellt, dabei sollte der Name in die Ecrobot Api passen. Bei uns waren das die Dateien ecrobot_ctrfid.c und ecrobot_ctrfid.h.
  • Die neuen Source Files müssen in nxtOSEK\ecrobot\c\Makefile folgendermaßen eingetragen werden:
C_LIB_SOURCES = \
	ecrobot_mindsensors.c \
	ecrobot_rs485.c \
	ecrobot_usb.c \
	ecrobot_HiTechnic.c \
	ecrobot_device_hook.c \
	ecrobot_interface.c \
	ecrobot_ctrfid.c \ # neue Treiberdatei
	NxtCam.c \
	colorsensor.c \
	osek_hook.c
  • Anschließend in ecrobot\c mit make all die Bibliothek neu kompilieren.

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/Makefile

Für C++

  • Bei neuen Treibern für die C++-Api verhält es sich fast genauso, der neue Treiber wird im Ordner nxtOSEK\ecrobot\c++\device angelegt. In unserem Fall waren dass die Dateien CtRfidSensor.cpp und CtRfidSensor.h
  • Die eigentliche Treiber muss nicht erneut implementiert werden, sondern nur "eine Klasse um die C-Funktionen herum" programiert werden.
  • Die Source Files müssen im nxtOSEK\ecrobot\c++\Makefile eingetragen werden.
  • Die Bibliothek muss mit Hilfe des Makefiles kompiliert werden.

Portierung der leJOS I²C-Treiberschicht für nxtOSEK

Ziel des Projekts ist die Entwicklung eines nxtOSEK-Treibers für das Codatex RFID-Modul. Bisherige Entwicklungen in diese Richtung schlugen fehl, da das Codatex-RFID-Modul das I²C-Protokoll nicht in seiner reinen Form implementiert. Codatex benutzt die Implementierung des Protokolls, das bei Lego zum Einsatz kommt. Dies führt dazu, dass zwar Kommunikation stattfindet, die übertragenen Daten von keiner der beiden Seiten (Brick und Device) richtig interpretiert werden können. Das fand unsere Vorgängergruppe[10] durch Verwendung eines Logikanalysators auch heraus. Im Sourcecode der i2c.c in den zum diesem Zeitpunkt (Winter 2010/2011, aber auch schon Anfang 2009) aktuellen nxtOSEK Bibliotheken (Stable, Release) findet sich ein Kommentar, der eben diese Problematik beschreibt:

// Port 4 on the nxt is a little odd. It has extra hardware to implement
// an RS485 interface. However this interacts with the digital I/O lines
// which means that the original open collector driver used in this code
// did not work. The code now uses a combination of open collector drive
// on the clock lines (with pull up resistors enabled), plus a fully
// driven ineterface on the data lines. This differs from the Lego
// firmware which uses a fully driven clock inetrface. However doing so
// means that it is hard (or impossible), to operate with the devices
// that make use of clock stretching. It is hoped that the compromise
// implemented here will work with all devices.

Da die Treiberschicht bei LeJOS und nxtOSEK konzeptionell gleich ist, sind wir auf die Idee gekommen, den RFID-Sensor mit LeJOS auszuprobieren. Einen Treiber für den Codatex-RFID-Sensor gibt es bei LeJOS bereits. Das Ergebnis des Tests war: Der Sensor lässt sich auslesen.

Ein Vergleich der Implementierungen der I/O-Schict beider Betriebssysteme bestätigte: Der leJOS Code ist aktueller als der Code von nxtOSEK. Nicht nur das: er ist auch sauberer. Ein Kommentar in den Sourcefiles der aktuellen leJOS Distribution, der sich unter oben gelistetem Kommentar befindet, erklärt, was im neuen Code besser ist.

// In attempt to make the code work with a wider range of sensors I've
// reworked it to bring it more into line with the i2c spec and the
// Lego implementation. There are now more partial transactions and
// there is a mode setting to allow standard mode or Lego mode operation.
// In addition tests showed that the i2c code was using up to 40% of the
// available cpu. To reduce this teh state machine has been simplified
// and modified to require only 2 states per clock instead of 4 (and thus
// allowing the use of a lower interrupt rate). We now track the usage of
// the i2c system. When no ports are active the clock is disabled.
// Finally the overall i2c operation has been split into 3 stages...
// 1. Start the transaction.
// 2. Wait for the transaction to complete.
// 3. Read the results.

So wurde die Statemachine um einige States erweitert, sodass sie eher den I²C Spezifikationen und der Lego Implementierung entspricht. Außerdem werden zuverlässigere Fehlercodes zurückgegeben. Das geht bei der nxtOSEK-Implementierung nicht. Dort muss man mit gekreuzten fingern eine gewisse Zeit lang warten und holt sich dann das Ergebnis ab, was dann entweder funktioniert - oder nicht. Die neue Statemachine ist weniger CPU-Lastig, da pro Takt nur zwei States angefordert werden anstatt der bisher vier.

Die Erste Überlegung war, die komplette Treiberschicht aus leJOS zu übernehmen, um vor allem die Kompatibilität von nxtOSEK mit den schon vorhandenen Applikationen und Geräten nicht zu gefährden. Da dies aber einen außerordentlichen Zeitaufwand mit sich brachte, haben wir diese Idee verworfen. Eine zweite Möglichkeit wäre gewesen, die beiden Implementierungen (I/O-Treiber von nxtOSEK und leJOS) koexistieren zu lassen, was uns aber als unsauber erschien weswegen wir uns dafür entschieden haben, die I²C-Sourcen von nxtOSEK mit der Funktionalität von der leJOS-Implementierung auszustatten und per Durchschleifen von Funktionsaufrufen darauf zu achten, dass bestehende Treiber weiter funktionieren.

Vergleich zwischen i2c.* alt und neu
nxtOSEK vorher nxtOSEK nachher
Funktion i2c_start_transaction() Funktionen i2c_start() und
i2c_complete()
Keine Möglichkeit, Fertigstellung
oder Fehler zu prüfen
Möglichkeit, Fehlerzustände
zu erhalten
Funktioniert nicht mit RFID-Sensor:
Timing sehr wichtig
Funktioniert mit RFID-Sensor


Abwärtskompatibilität

Die alte I²C-API verwendet nur die Funktion i2c_start_transaction(), um Daten vom I²C-Port zu lesen. Mit dieser Funktion wird der Leseprozess angestoßen und nach etwa 50 ms kann die Antwort gelesen werden.

Bei der neuen I²C-API findet der Aufruf asynchron statt. Mit der Funktion i2c_start() wird die Kommunikation gestartet, mit i2c_busy() kann überprüft werden, ob die Transaktion abgeschlossen wurde. Danach wird mit i2c_complete() die Transaktion beendet und der Rückgabewert in den Buffer geschrieben.

Da alle alten Treiber mit der alten i2c_start_transaction() funktionieren, war es notwendig diese Funktion mit der neuen i2c_start() abzubilden. Dadurch ist die neue Treiberschicht voll abwärtskompatibel und alle Treiber weiterhin verwendbar.


int
i2c_start_transaction(int port, 
                      U32 address, 
                      int internal_address, 
                      int n_internal_address_bytes, 
                      U8 *data, 
                      U32 nbytes,
                      int write)
{
	int ret;
	volatile U32 time_out;
	
	time_out = systick_get_ms() + 1000;

	ret = i2c_start(port, address, internal_address, n_internal_address_bytes, data, nbytes, write);
	if (ret != 0) return ret;
	
	if(!write)
	{
		while (i2c_busy(port) != 0) {
			systick_wait_ms(5);
			if (systick_get_ms() > time_out)
			{
				return -1;
			}
		}
		ret = i2c_complete(port, data, nbytes);
		return (ret < 0 ? ret : (ret == nbytes ? 0 : -1));
	}
	else return 0;
}

Benchmarks haben gezeigt, dass die neue Implementierung sehr viel schneller vom I²C-Port ließt. Alle alten Treiber warten nach i2c_start_transaction() 50 ms und verwendet danach das Ergebnis. Die Laufzeit der alten i2c_start_transaction() Funktion war minimal, die neue braucht ca. 5 ms. Der Unterschied ist, das nach diesen 5 ms der Rückgabewert auch wirklich da ist und nicht noch 50 ms gewartet werden muss. Das führt allerdings auch dazu, dass alle alten Treiber, die die neue Implementierung von i2c_start_transaction() verwenden und trotzdem danach 50 ms warten nun 55 ms für das Lesen benötigen.

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/i2c.c

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/i2c.h

Implementierung des Codatex RFID Treibers für nxtOSEK

Da die neuere Version der I²C Treiberschicht erfolgreich von leJOS zu nxtOSEK portiert wurde und für leJOS ein Treiber für den RFID-Sensor von Codatex existiert liegt es nahe, diesen Treiber ebenfalls zu portieren. Diesen portierten Treiber haben wir in der Ecrobot API untergebracht.

Die wichtigste Funktion dieses Treibers ist U8* ecrobot_ctrfid_read(int port, int continious). Diese Funktion ließt die Transponder ID vom Sensor und schreibt sie in ein Byte-Array. Die Funktion U64 ecrobot_ctrfid_read_U64(int port, int continuous) schreibt dieses Byte-Array in einen long long int Wert.

/**
* Read a transponder id.
* Reads the transponder using either continuous or single shot mode. If
* using single shot mode the tag must me available now. If using continuous
* mode then after the first call, the tag can be presented at any time
* and a subsequent read will return the results.
* @param port: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
* @param continuous Should we use continuous mode
* @return null if error or no data available, otherwise an array of five id bytes
*/
U8* ecrobot_ctrfid_read(int port, int continious)
{
	int i;
	int valid;
	U8 buf1[1];

	ecrobot_wait_until(next_read[port]);
	ecrobot_ctrfid_wake_up(port);

	if (continious)
	{
		if (ecrobot_ctrfid_get_status(port) <= 0)
		{
			ecrobot_ctrfid_start_continuous_read(port);
			systick_wait_ms(CTRFID_DELAY_ACQUIRE);
		}
	}
	else
	{
		ecrobot_ctrfid_start_single_read(port);
		systick_wait_ms(CTRFID_DELAY_ACQUIRE);
	}

	// make a note of when it is safe to do another read.
	next_read[port] = systick_get_ms() + CTRFID_DELAY_READ;
	// Get the data
	valid = 0;
	
	static U8 ret[CTRFID_LEN_DATA];

	for (i = 0; i < CTRFID_LEN_DATA; i++)
	{
		if (ecrobot_ctrfid_get_data(port, CTRFID_REG_DATA+i, buf1, 1) != 0)
			return 0;
		ret[i] = buf1[0];
		
		if (buf1[0] != 0)
			valid = 1;
	}
	
	return (valid ? ret : 0);
}

Die Funktion SINT ecrobot_ctrfid_get_data(int port, int reg, U8* buf, int len) vereinfacht dabei lediglich den Aufruf von i2c_start_transaction(). Bei i2c_start_transaction() handelt es sich um die im Funktion, die wir zur Abwärtskompatibilität erstellt haben. Wir verwenden diese, da sie nach außen hin synchronen Charakter besitzt, welcher hier benötigt wird.

SINT ecrobot_ctrfid_get_data(int port, int reg, U8* buf, int len) 
{	
	return i2c_start_transaction(port, CTRFID_I2C_ADDR, reg, 1, buf, len, 0);
}

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/ecrobot_ctrfid.c

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/ecrobot_ctrfid.h

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/ecrobot_interface.c

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/ecrobot_interface.h

Entwicklung einer Beispielanwendung

Um die Funktionstüchtigkeit des entstandenen Treibers und die der vorhandenen Treiber zu zeigen, haben wir eine kleine Beispielanwendung entwickelt.

BottleScan

BottleScan ist eine Anwendung für nxtOSEK. Es läuft auf einem Mindstorms NXT Brick, das mit verschiedenen Sensoren und Aktoren verbunden ist. Die Anwendung liest speziell präparierte Flaschen und bestimmt ihren fiktiven Pfandwert. BottleScan enthält zwei Tasks: einen Time-triggered Task, der die Sensoren überwacht und die Ergebnisse der Überwachung in einen gemeinsamen Speicherbereich schreibt und einen, der den Hintergrundtask, der sich in einer Forerver-Loop befindet, darstellt, welcher die Sensorwerte verarbeitet und entsprechend die Aktoren steuert.

Der Sensortask, der in regelmäßigen Zeitabständen gestartet wird, fragt zunächst den Sonarsensor ab. Wenn dieser einen Wert liest, der im Toleranzbereich liegt, wird ein Flag beschrieben das indiziert, dass eine Flasche auf das Förderband gelegt wurde. Dieses Flag sorgt auch dafür, dass nun der Sensortask auch den RFID-Sensor abfragt. Der Task endet nach einer genau vorhersagbaren Zeit: 55ms werden für das Abfragen des Sonarsensors benötigt, 250ms wird gewartet bis der RFID-Sensor gelesen wird und 5ms braucht der Treiber, um den RFID-Sensor auszulesen. Scheinbar ist das Timing sehr bedeutsam für das hier implementierte I²C Protokoll, da es ohne die Wartezeit zwischen dem Auslesen des einen Sensors und zwischen dem Auslesen des anderen Sensors nicht zuverlässig funktioniert.

Der Hintergrundtask wiederum liest, dass eine Flasche eingelegt wurde, fixiert die Flasche mit den Greifarmen und steuert den Förderbandmotor, durch den die Flasche an den RFID-Sensor herangefahren wird. Sobald der Sensor etwas liest, schreibt er diesen Erfolg in ein weiteres Flag und beim nächsten Durchlauf im Hintergrundtask wird die Flasche wieder ausgegeben und der Pfandwert auf dem Display ausgegeben.

Wird eine Flasche eingelegt, die keinen RFID-Tag enthält oder einen, der nicht lesbar ist, versucht BottleScan durch teilweises Ausgeben der Flasche und erneutes Heranziehen an den Sensor die Flasche zu verarbeiten. Nach drei fehlgeschlagenen Versuchen wird der Vorgang abgbrochen und die Flasche ausgegeben.


Über die Anwendungsentwicklung für nxtOSEK

Die Entwicklung einer Anwendung beginnt sinniger Weise mit dem Modellieren der möglichen Zustände und Aktivitäten. Dann schreibt man ein OIL-File. OIL bedeutet "OSEK/VDX Implementation Language" und dient zur statischen Konfiguration des Betriebssystems und der Anwendung mitsamt aller Betriebsmittel, wie Stacksize, Semaphoren, Tasks. In der Industrie kommen Tools zum Einsatz, die eigens dafür entwickelt wurden, komplexe OIL-Dateien komfortabel zu erzeugen. Da es sich bei BottleScan allerdings um eine sehr einfache Anwendung handelt, reicht hier Notepad als Werkzeug.

Der Aufbau der Konfiguration ist identisch mit dem in der OSEK/VDK-Spezifikation vorgegebenen.

Die Konfiguration in der OIL

Im folgenden wird anhand der BottleScan-Implementierungskonfiguration ein typisches, einfaches OIL-File erklärt.

Zunächst wird bestimmt, auf welcher Hardware die Konfiguration laufen soll.

CPU ATMEL_AT91SAM7S256
{

Dann wird das Betriebssystem konfiguriert. Gemäß OSEK/VDX Spezifikation werden u.a. Hook-Routinen (Funktionen, die bei bestimmten Zuständen ausgeführt werden) definiert.

	/**
	 * Definition des Betriebssystems
	 */
	OS LEJOS_OSEK
	{
		STATUS = EXTENDED;
		STARTUPHOOK = FALSE;
		ERRORHOOK = FALSE;
		SHUTDOWNHOOK = FALSE;
		PRETASKHOOK = FALSE;
		POSTTASKHOOK = FALSE;
		USEGETSERVICEID = FALSE;
		USEPARAMETERACCESS = FALSE;
		USERESSCHEDULER = FALSE;
	};

Es muss mindestens ein Application-Mode definiert werden. Die Tasks werden später den verschiedenen Application-Modes zugewiesen. Application Modes sind dazu da, die Anwendung in mehrere Betriebsmodi aufzuteilen. Bei unserer Beispielanwendung reicht ein Modus.

  
	/* Definition of application mode */
	APPMODE bottleScanMode{};

Der Task, der die Sensoren überwacht, wird in diesem Abschnitt konfiguriert. So wird definiert, dass er nicht automatisch gestartet wird; es wird ihm somit auch kein APPMODE zugewiesen. Daneben wird ihm eine Priorität zugewiesen und die Scheduling-Eigenschaft "vollpräemptiv", was bedeutet, dass er von Tasks Höherer Priorität (von denen es keinen gibt) unterbrochen werden kann. Zu den Prioritäten muss erwähnt werden, dass OSEK/VDX und damit auch nxtOSEK Fixed Priority Scheduling beherrschen. Die Wahl adäquater Prioritäten für die jeweiligen Tasks ist von außerordentlicher Wichtigkeit.

	/**
	 * Definition des vom Alarm 
	 * gestarteten Sensor-Abfrage
	 * -Tasks mit der hoechsten prio
	 */
	TASK SensorListener
	{
		AUTOSTART = FALSE;
		PRIORITY = 3;
		ACTIVATION = 1;
		SCHEDULE = FULL;
		STACKSIZE = 512;
	};

Dies hier ist der Hintergrundtask, der Ständig läuft und Intervallmäßig vom o.g. Task unterbrochen wird. Er Fragt die Werte, die vom SensorListener-Task in den gemeinsamen Speicher geschrieben wurden, ab und führt Aktionen durch.

	/**
	 * Definition des BottleScan 
	 * Main Tasks
	 */
	TASK BottleScanMain
	{
		AUTOSTART = TRUE
		{
			APPMODE = bottleScanMode;
		};
		PRIORITY = 2; 
		ACTIVATION = 1;
		SCHEDULE = FULL;
		STACKSIZE = 512;
	};

Die Definitionen des Counters und des Alarms stehen in unmittelbarer Verbindung zum SensorListener-Task. Der SysTimerCnt-Counter wird pro Millisekunde aktualisiert (und zwar in der prädefinierten ISR der Kategorie 2user_1ms_isr_type2()). In der Definition von SensorAlarm ist zu sehen, dass die Aktion bei abgelaufenem Counter das Aktivieren des SensorListener-Tasks ist.

	/**
	 * Defintion des Systemcounters
	 */
	COUNTER SysTimerCnt
	{
		MINCYCLE = 1;
		MAXALLOWEDVALUE = 10000;
		TICKSPERBASE = 1;
	};  

	/**
	 * Definition des Alarms, der 
	 * nach seinem Ablauf (dieser 
	 * wird im Programm bestimmt) den
	 * SensorListener Task ausfuehrt
	 */
	ALARM SensorAlarm
	{
		COUNTER = SysTimerCnt;
		ACTION = ACTIVATETASK
		{
			TASK = SensorListener;
		};
		AUTOSTART = FALSE;
	};

  
};

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/bottlescan2.oil

Implementierung

Deklarationen der Tasks, Counter und Alarme

DeclareTask(SensorListener);
DeclareTask(BottleScanMain);
DeclareCounter(SysTimerCnt);
DeclareAlarm(SensorAlarm);

Diese Hookroutine ist vordefiniert und wird jede Millisekunde von einer Interrupt Service Routine der Klasse 2 ausgeführt. Hier wird also jede Millisekunde der Systemcounter, der in der OIL definiert ist, inkrementiert.

void user_1ms_isr_type2(void){ 
	(void)SignalCounter(SysTimerCnt); 
}

Desweiteren gibt es Methoden von Ecrobot, die aufgerufen werden wenn das System startet, um verschiedene Geräte aufzuwecken. So sind folgende Aufrufe nötig, um mit dem Sonarsensor arbeiten zu können. Der RFID-Sensor wird über eine im Treiber implementierte Initialisierungsfunktion aufgeweckt.

void ecrobot_device_initialize(){ecrobot_init_sonar_sensor(SENSORPORT_SONAR);}
void ecrobot_device_terminate(){ecrobot_term_sonar_sensor(SENSORPORT_SONAR);}

Diese Methode sorgt dafür, dass sich das Förderband in richtung RFID Sensor bewegt, bis etwas gelesen wird. Dann stoppt es.

status_t move_bottle_to_rfid_sensor(){}

gibt die flasche wieder aus (band laeuft 1 sekunde)

void eject_bottle(status_t half){}

Initialisiert das System, indem zunächst beide Lampen aufleuchten, der Greifarm in Ausgangsstellung zurückversetzt wird, der RFID-Sensor aufgeweckt wird und der Alarm mit einem Task verbunden wird. SetRelAlarm() nimmt als ersten Parameter den Tasknamen, als zweiten, in wie vielen Millisekunden der Task zum ersten Mal gestartet wird und als dritten, den Intervall.

void init_device(){

	m_total_pfand = 0;
	green(ok_t);
	red(ok_t);
	unstabilize();
	ecrobot_ctrfid_init(SENSORPORT_RFID);
	m_bottle_inserted = notok_t;
	m_bottle_identifier = 0;
	SetRelAlarm(SensorAlarm, 1000, 250);
	green(ok_t);
	red(notok_t);
}


Dies hier ist die Definition des Sensor-Abfragetask, der mittels des Alarms angestoßen um alle Sensorwerte auszulesen.

TASK(SensorListener){
	unsigned int distance;

	distance = ecrobot_get_sonar_sensor(SENSORPORT_SONAR);
	if (distance > 0 && distance <= DISTANCE_MAX) {
		if(!m_bottle_inserted){
			m_bottle_inserted = ok_t;
		}
		systick_wait_ms(150);
		m_bottle_identifier = ecrobot_ctrfid_read_U64(SENSORPORT_RFID,1);
	} else {
		if(m_bottle_inserted){
			m_bottle_inserted = notok_t;
		}
		m_bottle_identifier = 0;
	}

	TerminateTask();
}

Und hier ist die Implementierung des Haupttask mit niederer Priorität, der zwar ständig läuft um den Taskübergreifenden Speicher auszulesen, aber regelmäßig von dem periodischen Task verdrängt wird.

TASK(BottleScanMain){


	float geld = 0;


	init_device();

	while(1){

		display_bs_statistics("Bereit", 0, m_total_pfand);
	
		// wenn eine flasche eingelegt ist, fahren wir sie
		// naeher an den RFID sensor ran.
		systick_wait_ms(500);
		if(m_bottle_inserted != 0){
			// stabilisieren
			stabilize();
			status_t success = notok_t;
			// flasche zum rfid-sensor bewegen
			display_bs_statistics("Lese...", 0, m_total_pfand);
			success = move_bottle_to_rfid_sensor();
			if (success == notok_t){
				success = retry_bottle_insertion(2);
			}
			// hier noch den letzten status abfragen			
			if (success == abort_t){
				display_bs_statistics("ABRT", geld, m_total_pfand);
				m_bottle_inserted = NO_BOTTLE;
				abort();
				//continue;
			} else if (success == ok_t){
				// alles okay, jetzt geld zusammentragen
				geld = get_money_from_db(m_bottle_identifier);
				m_total_pfand += geld;

				display_bs_statistics("OK", geld, m_total_pfand);
				eject_bottle(notok_t);
				unstabilize();
			} else if (success == notok_t){
				eject_bottle(notok_t);
				unstabilize();
			}
		}
		systick_wait_ms(500);
	}
	TerminateTask();
}

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/bottlescan2.c

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/embedded_systems/bottlescan2.h

Ausblick: Aktualisierung der nxtOSEK Treiberschicht

Da die leJOS Treiberschicht ständig weiterentwickelt wird, wäre es an der Zeit, die nxtOSEK Treiberschicht zu aktualisieren.

Die Treiberschicht befindet sich in folgendem Ordner: nxtOSEK_v214\nxtOSEK\lejos_nxj\src\nxtvm\platform\nxt.

Die Version in nxtOSEK ist allerdings mittlerweile fast 3 Jahre alt. Dadurch sind die Erneuerungen nur schwer überschaubar. 35 Dateien vom leJOS-Layer sind seitdem verändert worden (inkl. Header). Es reicht dabei von kleinen Bugfixes über neue hinzugefügte Funktionen bis hin zu kompletten Änderungen, wie wir sie bei i2c.c gesehen haben. Durch die zahlreichen Wechselwirkungen mit dem Ecrobot-Layer muss also ein großer Teil von Ecrobot an die neuen Funktionen angepasst werden. Bei unseren Versuchen, den kompletten Layer zu aktualisieren, hat es sich als besonders schwer herausgestellt, die vielen, zum Teil sehr kryptischen Compilerfehler, die unweigerlich entstehen mussten, zu beseitigen.

Änderungen bei sensors.h

alt:

typedef struct {
  char type;
  char mode;
  short raw;
  short value;
  char boolean;
} sensor_t;


#  define N_SENSORS (4)

extern sensor_t sensors[N_SENSORS];
extern void init_sensors(void);
extern void poll_sensors(void);
extern void read_buttons(int, short *);
extern void check_for_data(char *valid, char **nextbyte);
extern void set_digi0(int);
extern void unset_digi0(int);
extern void set_digi1(int);
extern void unset_digi1(int);

#endif // _SENSORS_H

neu:

typedef struct
{
  int pins[2];
  U32 ADCChan;
  AT91_REG * ADCData;
} port_pins;

extern const port_pins sensor_pins[];


extern void sp_reset(int);
extern void sp_init(void);
extern void sp_set(int, int, int);
extern int sp_get(int, int);
extern void sp_set_mode(int, int, int);
extern int sp_read(int, int);
extern void sp_set_power(int, int);
extern int sp_check_event(int);

hier sieht man sehr gut, wie stark einige Dateien geändert wurde, bei sensors.c wurden nicht nur alle Namen geändert, sondern auch die meisten Übergabeparameter.


Sourcecode

http://www.cs.hs-rm.de/~beckmann/lv/ds10ws/EM2010WSP05/nxtOSEK.zip

Links

  1. http://mindstorms.lego.com/en-us/Default.aspx
  2. http://lejos.sourceforge.net/
  3. Lego Fantom-Treiber: http://mindstorms.lego.com/Support/Updates/
  4. http://www.cygwin.com/install.html
  5. http://www.gnuarm.com/bu-2.16.1_gcc-4.0.2-c-c++_nl-1.14.0_gi-6.4.exe
  6. http://www.gnuarm.com/
  7. Upload-Programm zum Flashen des Bricks mit Bios und Applikation: http://bricxcc.sourceforge.net/utilities.html
  8. http://sourceforge.net/projects/lejos-osek/files/nxtOSEK/
  9. Download der OSEK Systemdateien, in denen auch der Systemgenerator sg.exe als Binary vorliegt: http://www.toppers.jp/osek-download.html
  10. https://wwwvs.cs.hs-rm.de/vs-wiki/index.php/EM2009WSP04_LEGO_NXT-Sensorhardware