Basic für das fischertechnik Robo Interface


Vorwort


Diese Software entstand aus der Idee heraus, eine Scriptsprache für das Robo Interface zu entwickeln, die auch downloadfähig ist – d.h.: die auch autonom ohne PC im Interface funktioniert. Zwar existiert bereits eine downloadfähige Scriptsprache in Form von C, allerdings ist dies sehr fehleranfällig und für Anfänger gänzlich ungeeignet.

Es ist in Zukunft geplant, die Sourcen zu veröffentlichen – allerdings müssen die erst einmal bereinigt und lesbar gemacht werden ;-)


Ich kann leider nicht garantieren, dass die vorliegende Software völlig fehlerfrei ist. Eine Benutzung geschieht deshalb auf eigenes Risiko, eine Haftung bei Folgeschäden durch die Benutzung dieser Software schließe ich aus.

Momentan hat die Software Beta-Status.


1. Funktionsprinzip


Das Basic besteht aus zwei Komponenten:


Bei der Planung des Basic habe ich viele verschiedene Möglichkeiten geprüft und – meines Erachtens – die bestmögliche verwirklicht. In der Anfangsphase sollte es ein reinrassiger Interpreter werden, dies hätte den Vorteil gehabt, dass man theoretisch ohne einen PC ausgekommen wäre (über die serielle Schnittstelle wäre der Anschluss eines Displays und einer Tastatur durchaus möglich). Allerdings hätte dies bedeutet, dass der Prozessor des Interface sehr stark mit den Übersetzungsaufgaben beschäftigt gewesen wäre. Eine Zwischenlösung ist die Umsetzung von Befehlen in Tokens, d.h. der Klartext der Befehle wird in Bytecode umgesetzt (dies war z.B. das Prinzip des C64-Basic). Bei dieser Methode bleiben aber immer noch: Berechnung der Sprungadressen, Berechnung der Variablenadressen, Syntaxprüfung etc. Gerade die Syntaxprüfung ist ein Punkt, der für einen vom Interpreter getrennten Compiler sprach.

In dem nun vorliegenden Programm sind im Bytecode alle Adressen von Variablen, Funktionen und sonstigen Sprüngen vorberechnet und auch die Konstanten liegen nicht als Ascii, sondern im Binärformat vor, was die Ausführungszeiten positiv beeinflusst.

Theoretisch wäre es auf Grundlage des Bytecodes auch möglich, andere Sprachen (z.B. Pascal o.ä.) zu realisieren.

Beim Übersetzungsvorgang des Compilers wird ein .hex-File erzeugt, das mit dem von ft gelieferten „FtLoader“ in das Robo Interface übertragen werden kann. Das .hex-File beinhaltet den Interpreter und den Bytecode, ist also ohne weitere Downloads funktionstüchtig.


1.1 Features


In den letzten Monaten hat sich die Liste der Features von Woche zu Woche geändert – meistens wurde sie verlängert ;-). Mittlerweile bin ich der Überzeugung, dass man mit diesem Basic durchaus ein interessantes Werkzeug zur Verfügung hat. Bei der Realisierung bin ich hauptsächlich von meinen Bedürfnissen ausgegangen – falls jemand ein Feature vemisst, das realisierbar scheint, kann ich nochmal etwas nachlegen.

Eine kurze Liste der realisierten Features (ausführliche Information gibt es in der Befehlsreferenz)






1.2 Multitasking

Das mit Sicherheit interessanteste Feature ist das Multitasking. Nachdem ich es in C aufgegeben habe, weil es sehr viel Durcheinander auf dem Stack gegeben hat, war die Realisierung innerhalb eines Interpreters geradezu einfach. Die Tasks (bis zu 10 + Hauptprogramm) werden scheinbar gleichzeitig ausgeführt und auch Wartebefehle haben darauf keinen Einfluss.

Die einzige Einschränkung: Für einen Taskwechsel muss der mathematische Stack leer sein. Vor allem bei rekursivem Aufruf von Funktionen kommt es vor, dass ein Berechnungsergebnis auf dem mathematischen Stack verbleibt, dies hat zur Folge, dass die komplette Rekursion erst aufgelöst werden muss, bevor der nächste Task ausgeführt werden kann. Diese Eigenheit lässt sich ohne weiteres durch eine Erweiterung lösen, allerdings sehe ich es eher als Feature, weil man hierdurch eine Art Priorisierung erreichen kann.


1.3 Globale und lokale Variablen

Neben den globalen Variablen, die im gesamten Programm sichtbar sind, können für jede Funktion und jeden Task separat lokale Variablen definiert werden. Die lokalen Variablen sind nur innerhalb des Tasks / der Funktion sichtbar, in dem / der sie definiert wurden, nicht darüber hinaus.


1.4 Funktionen mit Parametern

Funktionen können mit oder ohne Parameter definiert werden. Werden Parameter definiert, sind diese automatisch als lokale Variable innerhalb der Funktion definiert. Ein Rückgabeparameter wird mit dem return Befehl übergeben.

Dadurch, dass lokale Variablen für jeden Task getrennt gespeichert werden, können Funktionen aus mehreren Task unabhängig voneinander und gleichzeitig aufgerufen werden. Die Aufrufe beeinflussen sich nicht gegenseitig. In diesem Zusammenhang ist aber Vorsicht bei globalen Variablen geboten.



Die restlichen Features sind im Kapitel 2 besprochen.



1.5 Bekannte Bugs

Wo Licht ist, ist auch Schatten – es gibt noch einige Bugs, die mir zwar bekannt sind, aber bei denen momentan einfach die Zeit fehlt.

Eine aktuelle Liste ist im Text-File „bekannte_bugs.txt“ abgelegt. Diese Liste sollte unbedingt gelesen werden, bevor man mit dem Programmieren anfängt. Ansonsten bin ich natürlich immer froh, wenn mir Bugs mitgeteilt werden.


2. Programmieren in BASIC


Ich gehe davon aus, dass die grundlegenden BASIC-Begriffe und Programmiertechniken bekannt sind.

Die wichtigsten Unterschiede zu einem Standardbasic bestehen in den Elementen für strukturierte Programmierung und darin, dass es keine Zeilennummern gibt. Ferner müssen – BASIC-untypisch – Variablen im Voraus definiert werden. Dies hat seine Begründung darin, dass der Compiler schlichtweg einfacher ist, wenn man Variablen deklariert.

Bei den meisten mir bekannten BASIC-Dialekten konnte man kaum unterschiedliche Datentypen verwenden, bestenfalls kann man zwischen Integer (= ganze Zahlen) und Float (= Gleitkommazahl) wählen. Beim hier vorliegenden BASIC hat man die Wahl zwischen Byte, Int, Long, Float und Double. Warum diese Vielfalt? Ich möchte eine möglichst optimale Ausnutzung von Speicher und Prozessor erreichen. Wenn z.B. ein Array mit 1000 Elementen definiert wird, braucht es als long 4000 Bytes, als byte nur 1000 Bytes.


2.1 Datentypen:


Byte:

Ganze Zahlen mit einem Wertebereich von -128 bis +127. Benötigt 1 Byte (8 Bit).

Int: (Integer)

Ganze Zahlen mit einem Wertebereich von -32768 bis +32767. Benötigt 2 Byte (16 Bit).

Long: (long Integer)

Ganze Zahlen mit einem Wertebereich von -2147483648 bis +2147483647. Benötigt 4 Byte (32 Bit).

Float: (Gleitkommazahl)

Gleitkommazahlen mit einem Wertebereich von (+/-)3,4*(+/-)10^38. Benötigt 4 Byte (32 Bit). Die Genauigkeit liegt bei max. 7 Ziffern.

Double: (Gleitkommazahl)

Gleitkommazahlen mit einem Wertebereich von (+/-)1,7*(+/-)10^308. Benötigt 8 Byte (64 Bit). Die Genauigkeit liegt bei max. 15 Ziffern.


Vor der Programmierung sollte man sich im Klaren sein, welche Variablen welchen Zahlenbereich annehmen können und dementsprechend einen Datentyp wählen. Die Benutzung von float und double sollte man so weit als möglich vermeiden, denn Gleitkommazahlen verbrauchen überdurchschnittlich viel Rechenleistung.


2.1 Rechenoperationen und Zahlen


Bei den Rechenoperationen ist (fast) alles erlaubt. Dieser Teil des Compilers hat mich einige

graue Haare gekostet, aber scheint nun zu funktionieren.

Es gilt: Punkt vor Strich, Klammern können auch gesetzt werden.


+ Addieren

- Subtrahieren

* Multiplizieren

/ Dividieren

^ Potenzieren

& Bitweise UND (nur für long, bei double ist das Ergebnis immer 0)

| Bitweise OR s.o.

! Bitweise XOR s.o.


Funktionen:

sin() sinus

cos() cosinus

tan() tangens

cot() cotangens

asin() Umkehrfunktion zu sin()

acos() Umkehrfunktion zu cos()

atan() Umkehrfunktion zu tan()

acot() Umkehrfunktion zu cot()


Es gibt momentan einen bekannten Fehler im Parser:

Ist innerhalb einer Konstanten ein nicht für Rechenoperationen reserviertes Zeichen (z.B. 4t5), wird kein Fehler ausgegeben. Die Zahl wird bis zum Vorkommen des Zeichens ausgewertet, der Rest wird verworfen. Im obigen Beispiel würde die Zahl also als 4 interpretiert.


Zu beachten ist: Bei den Rechenoperationen müssen die unterschiedlichen Zahlentypen "gecastet", also

angepasst werden, sonst kann man long (Ganzzahl) und double (Gleitkomma) nicht miteinander verknüpfen.

Dies macht der Interpreter automatisch. Dabei setzt sich immer das genauere (größere) Zahlenformat durch. Die Reihenfolge: Byte – Integer – Long – Float – Double.


Beispiele:


a = (b+c)*(d+e)


a = 5*(b+8)*(-c-d)


Der Compiler nimmt dem Interpreter bereits die arbeit ab, die Zahlen aus dem Klartext in ein dem Rechner

verständliches Format umzuwandeln. Konstanten werden direkt im Bytecode abgespeichert, wobei ganze Zahlen 4 Bytes benötigen, Gleitkomma 8 Bytes. Ist in einer Konstanten ein "." (Dezimalpunkt) vorhanden, wird sie als double behandelt, sonst als long. Wenn man von vornherein weiß, dass eine Berechnung in double ausgeführt wird, kann man die Laufzeit optimieren, indem man den Compiler dazu zwingt, eine double abzuspeichern. Dann muss der Interpreter kein Typcasting mehr durchführen.

Es ist geplant, durch Bereichsprüfung bei den Konstanten auch die fehlenden Datentypen byte, integer und float zu implementieren.


1234 long - Konstante

1234.0 double

1234.0E4 double, entspricht 12340000

1234E4 long, das E wird nicht interpretiert, entspricht 1234


2.2 Reihenfolge der Definitionen


Der Compiler akzeptiert die verschiedenen Definitionen nur in einer bestimmten Reihenfolge.


  1. Globale Variablen

  2. Funktionen (globale Variablen)

  3. Tasks (globale Variablen)

  4. Hauptprogramm (main:)


Grundsätzlich muss eine Funktion definiert sein, bevor sie aufgerufen werden kann. Ein rekursiver Aufruf (die Funktion ruft sich selbst auf) ist deshalb möglich, da die Definition vor dem eigentlichen Aufruf innerhalb der Funktion steht.


Die Definitionen von Tasks und Funktionen können miteinander vertauscht werden, sollen Funktionen aus Tasks heraus aufgerufen werden, müssen die Funktionen wiederum vor dem Task definiert sein.


Globale Variablen müssen direkt nach der Kopfzeile der Funktion / des Tasks definiert werden. Hierzu gibt es im Anhang noch einige Beispiele.



3. Wie schreibe und compiliere ich Programme?


Im Paket ist der kostenlose Editor "Programmers Notepad" enthalten, der auch unter http://www.pnotepad.org heruntergeladen werden kann. Er bietet Syntax-Highlighting und Tools für den einfachen Aufruf der benötigten Programme.

An der Dateistruktur des Verzeichnisses "RoboInt_Basic_Compiler" sollte nichts verändert werden, da der

Editor diese Struktur erwartet und nur dann richtig funktioniert.


3.1 Schreiben eines Programms


Starten des Editors mit Doppelklick auf "Programmers Notepad Editor.bat". Beim ersten Aufruf speichert der Editor seine Konfigurationsdaten in "C:\Dokumente und Einstellungen\(Username)\Anwendungsdaten\Echo Software". Hierbei werden auch die Tools automatisch definiert.

Nun kann das Programm eingegeben oder eines der Beispielprogramme im Ordner "Testprogramme" geladen werden. Wichtig: Das Label main: muss auf jeden Fall im Programm vorhanden sein, da der Compiler hierduch mitgeteilt bekommt, wo das Hauptprogramm beginnt.


3.2 Compilieren eines Programms


Mit Klick auf "Tools" und dann auf "[RoboInt] compile" wird das Programm compiliert und gelinkt. Beim Link-

Vorgang wird der Interpreter automatisch zum generierten Bytecode hinzugefügt, so dass immer ein lauffähiges Programm entsteht. Dies ist auch der Grund, weshalb die Programme mehr als 32 KByte haben.

Im Output-Fenster am unteren Rand wird entweder die erfolgreiche Compilierung oder ein Fehler angezeigt.

Bei der Ausgabe "Process Exit Code: 0" ist alles in Ordnung. Der Compiler kann auch mit der Taste "F5" aufgerufen werden. Zusätzliche Optionen (nur für "Fortgeschrittene")


- compile (Line Numbers) : Fügt Informationen der Zeilennummern hinzu. Bei Runtime-Fehlern wird diese

Information benutzt, um die Zeilennummer auszugeben, in der der Fehler passierte.

- compile (Detailed) : Der Compiler gibt alle Zwischenschritte aus, die er beim übersetzen des Codes

benötigt. Dient zum Debuggen des Compilers.


Beim "Process Exit Code: 1" ist ein Fehler beim Compilieren aufgetreten, üblicherweise sollte eine entsprechende Fehlermeldung angezeigt werden, die die Ursache des Fehlers zeigt.


3.3 Übertragen eines Programms zum Interface


Während des compilierens wird ein "hex"-File erzeugt, das den Interpreter und den Bytecode enthält.

Das Hex-File hat den gleichen Namen, wie der Sourcetext.

Durch Aufruf des Tools "FtLoader" wird das von der Fa. Knobloch zur Verfügung gestellte Download-Programm gestartet. Das Interface muss angewählt und das Hex-File geöffnet werden, dann kann das Programm in das Interface geladen und gestartet werden. Die Programme werden zur Zeit für das RAM des Interface compiliert, später ist geplant, dies auch für die beiden Flash-Speicher zu ermöglichen.


3.4 Ausführen des Programms


Im FtLoader den Button "Prg Ram Start" drücken.


3.5 Anzeige von Meldungen des Interface


Momentan missbrauche ich die Message-Funktion des Interface, um via serieller Schnittstelle eine Möglichkeit zum Debuggen zu haben. Leider kann diese Funktion keine beliebigen Texte aussenden, es werden zusätzliche Informationen mit übertragen, die bei einem normalen Terminal-Programm nur zu unleserlichen Ausgaben führen würden. Hierzu habe ich ein kleines Windows-Programm geschrieben (RoboIntSerialConnect.exe), das die uninteressanten Daten ausfiltert und die Daten, die mit dem "print" Befehl ausgegeben werden, vom Interface ausliest und in einem Fenster ausgibt. Auch Runtime-Fehler werden über die serielle Schnittstelle ausgegeben.

Beim normalen Programmende blinkt die rote Fehler-LED des Interface 1x. Sollte ein Runtime-Fehler auftauchen,schaltet die rote LED auf dauerblinken.



4. Befehlsreferenz


<Ausdruck>


Ein Ausdruck kann aus Variablen, Konstanten oder Kombinationen davon bestehen (z.B. eine komplette Berechnungsformel). Der jeweilige Befehl verwendet dann das Ergebnis der Berechnung als Parameter.


Gültige Ausdrücke:

10 Konstante

a Variable.

a+5 Formel

(a+5)*(b+2)


nicht erlaubt:

10>5


<Vergleich>


Ein Vergleich stellt eine Beziehung zwischen zwei Ausdrücken her.

Gültige Vergleiche:


a = 0

b > 5

(a+b) = (c * d)

input (x) = 0

sin(0) > 1


<variable>


Ein Variablenname kann aus bis zu 15 Zeichen bestehen


<Ergebnis> = analog(<Ausdruck>)


Fragt einen der analogen Eingänge des Robo Interface ab. <Ausdruck> gibt die Nummer des Analogeingangs an. Um die Programmierung zu vereinfachen, sind Konstanten vordefiniert. Eine Liste der Konstanten finden man im Anhang.


dim <variable> (<Konstante>[,<Konstante>]) <typ>


Definiert ein Array (Feld). Felder sind immer global und müssen an der entsprechenden Stelle im Programm (direkt am Anfang) definiert werden. Arrays können ein- oder zweidimensional sein.

Gerade bei Arrays ist der Speicherbedarf sehr groß, deshalb sollte der Typ des Arrays sorgfältig gewählt werden.

Beispiele:


dim positionen (100,4) int

dim werte (20) float


edge <Typ> <Eingang>


Wartet auf eine Flanke am angegeben Eingang. Als Typ kann any (irgendeine), up (steigend, also 0->1) oder down (fallend, also 1->0) angegeben werden.

Beispiele:


edge any 5 Wartet auf eine Änderung an Eingang 1

edge down 4 Wartet auf einen 1->0 Übergang (loslassen des Tasters) an Eingang 4



for <variable> = <Ausdruck 1> to <Ausdruck 2> [step <Ausdruck 3>]

<Anweisungsblock>

next


Führt einen Anweisungsblock ein oder mehrfach aus, bis die Endbedingung <variable> = <Ausdruck 2> erreicht ist. „step“ gibt die Schrittweite an, wird „step“ weggelassen, wird +1 angenommen.


For-Next Schleife. Die Variable <variable> wird von <Ausdruck 1> bis <Ausdruck 2> mit der Schrittweite <Ausdruck 3> durchgezählt.

Bei negativen <step> kann auch rückwärts gezählt werden. Die Angabe von <step> ist optional,

wird sie weggelassen, wird automatisch 1 angenommen. Hinter next wird nicht, wie teilweise üblich, die Zählvariable angegeben - sie wird automatisch zugewiesen.


(Einschalten der Motoren 1 bis 4)


for x=1 to 4

motor x,8

next


(Zählen von 10 bis 0 rückwärts)


for x=10 to 0 step -1

<Anweisungsblock>

next


(Verschachtelte Schleifen)


for x=1 to 10 step 2

for y=20 to 25

<Anweisungsblock>

next

next


(Schrittweiten kleiner als 1)


for x = 1.0 to 1.5 step 0.1

<Anweisungsblock>

next


Gibt die Zahlen von 1 bis 10 auf die serielle Schnittstelle aus.



function <Funktionsname> [(<variable> <typ> , ...) [typ]]


Definiert eine Funktion mit dem entsprechenden Namen.

Eine Funktion kann keine oder bis zu vier Übergabeparameter haben und / oder bis zu einen Rückgabeparameter.


Beispiele:

function test Definiert eine Funktion ohne Parameter

function test1 (a long,b float) Definiert eine Funktion mit zwei Übergabeparametern

function test2 (a long) long Definiert eine Funktion mit einem Übergabe- und einem

Rückgabeparameter


Beispiel für eine Funktion mit Parametern:


function summe (a long, b long) long

return a+b


Der Aufruf dieser Funktion:


x = summe (10,20)


Beispiel für eine rekursive Funktion (gesamtes Programm)


var x long

function summe (parameter long) long

var a long

var b long

if parameter <= 1

a = 1

else

b = summe (parameter – 1)

a = parameter + b

end

return a


main:

print summe (10)


Tipp: Es fällt auf, dass im else – Teil des Unterprogramms unnötigerweise die Variable b benutzt wird. Kürzer könnte es heißen:


else

a = parameter + summe(parameter – 1)

end


Dies hat allerdings den Nachteil, dass hierdurch ein Taskwechsel verhindert wird, weil die Variable parameter zur Berechnung auf den mathematischen Stack gelegt wird. Erst nach Rückkehr der Funktion (also nach Ausführung aller Rekursionen) ist der mathematische Stack bereinigt. Außerdem hat der mathematische Stack nur 10 Speicherplätze, eine Berechnung von summe (11) wäre nicht möglich. Durch das Speichern des Ergebnisses in der lokalen Variable b werden diese Effekte verhindert.

Diese Eigenart kommt bei normaler Nutzung kaum zu tragen, sollte es zu überdurchschnittlich vielen Problemen führen, kann sie durch eine Änderung am Interpreter eliminiert werden.


goto <label>


Ganz ohne goto gehts in basic leider nicht. Da es keine Zeilennummern gibt, müssen Labels definiert werden. Das Label main hat eine besondere Bedeutung, es teilt dem Compiler mit, wo das Hauptprogramm beginnt. Dieses Label muss auf jeden Fall in jedem Programm vorhanden sein.


Beispiele:


if a=b

goto label1

end



endloop:

goto endloop


If <Vergleich>

<Anweisungsblock1>

[else

<Anweisungsblock2>]

end


Falls ... dann ... sonst ...“


Falls die im Vergleich angegebene Gleichung wahr ist, wird Anweisungsblock 1 ausgeführt, ansonsten (sofern vorhanden) Anweisungsblock 2.

Ein Anweisungsblock kann aus einem oder mehreren Anweisungen bestehen. Es dürfen beliebig viele if-else-end Anweisungen ineinander verschachtelt werden.


Beispiel A:


if a = 0

motor 1,0 // a ist gleich 0

else

motor 1,8 // a ist ungleich 0

end


Falls a = 0, wird der Motor abgeschaltet, bei allen anderen Werten von a wird er eingeschaltet.



Beispiel B:


if a = 0

motor 1,0

end


Falls a = 0, wird der Motor abgeschaltet, bei allen anderen Werten von a passiert nichts.


<Ergebnis> = input (<Ausdruck>)


Fragt die digitalen Eingänge des Robo Interface ab. <Ausdruck> kann Werte zwischen 1 und 32 annehmen, das Ergebnis ist entweder 0 (Eingang nicht betätigt) oder 1 (Eingang betätigt).


Beispiele:


a = input (8) Fragt Eingang 8 ab und speichert das Ergebnis in a


if input (1) = 1 Schaltet den Motor 1 ein, wenn Eingang 1 betätigt wird.

motor 1,8

end


<Ergebnis> = ir


Fragt den Infrarot-Eingang des Robo Interface ab. Ist keine Taste auf der Fernbedienung gedrückt, ist das Ergebnis 0.

Um Abfragen zu vereinfachen, sind Konstanten vordefiniert. Eine Liste der Konstanten findet man im Anhang.


kill <taskname>


Beendet einen Task und initialisiert ihn neu. Bei einem weiteren start <taskname> startet der Task

neu.



motor <Ausdruck 1>,<Ausdruck 2>


Steuert einen Motorausgang des Robo Interface an. <Ausdruck 1> gibt die Nummer des Motors an (1..16), <Ausdruck 2> die Drehrichtung und Geschwindigkeit. <Ausdruck 2> kann Werte zwischen -8 und 8 annehmen, bei negativen Werten dreht der Motor links herum, bei positiven rechts herum. Bei 0 wird der Motor abgeschaltet. Größere oder kleinere Werte bei den Parametern werden ignoriert.


Beispiele:


motor 1,8 Schaltet Motor 1 ein, rechtsdrehend, mit maximaler Geschwindigkeit.

motor a,b Je nach Inhalt der Variablen a und b wird ein Motor ein- oder ausgeschaltet.


output <Ausdruck 1>,<Ausdruck 2>


Steuert einen Ausgange des Robo Interface an. <Ausdruck 1> gibt die Nummer des Ausgangs an (1..32), <Ausdruck 2> die Geschwindigkeit. <Ausdruck 2> kann Werte zwischen 0 und 8 annehmen. Bei 0 wird der Ausgang abgeschaltet.


print <Ausdruck>


Zwar hat das Robo Interface keinen Bildschirm, aber eine serielle Schnittstelle. Mit diesem Befehl können Variablenwerte oder Konstanten (z.B. zur Fehlersuche) über die serielle Schnittstelle ausgegeben werden.


Beispiele:


print a Gibt den Inhalt der Variablen a aus.

print a+5 Addiert 5 zur Variablen a hinzu und gibt das Ergebnis aus.

print 10 Gibt die Konstante 10 aus.



repeat

<Anweisungsblock>

until <Vergleich>


Führt den Anweisungsblock so lange aus, bis der Vergleich wahr ist.


Beispiel:


a = 1 Stoppt die Motoren 1 - 4

repeat

motor a,0

a = a + 1

until a >= 4




Select <Ausdruck>

case <Konstante 1>

<Anweisungsblock 1>

case <Konstante 2>

<Anweisungsblock 2>

.

.

end


Mit der „select – case“ - Anweisung können Programmverzweigungen elegant gelöst werden. Beispiel: Die Abfrage der IR-Schnittstelle. Hierbei wird der Ausdruck mit den Konstanten verglichen und bei Gleichheit der entsprechende Anweisungsblock ausgeführt. Gleich danach springt die Programmausführung komplett an das Ende der Select-Anweisung, es werden also keine weiteren Vergleiche durchgeführt.



select ir Abfrage des IR - Eingangs

case #m1l1 ist die Taste „Motor 1 Links“ gedrückt ?

motor 1,-8 Motor 1 linksherum einschalten

while ir <> 0 warten, bis die Taste wieder losgelassen wird

end

motor 1,0 Motor 1 abschalten


case #m1r1 das Gleiche für rechtsherum

motor 1,8

while ir <> 0

end

motor 1,0


case #m2l1 weitere Abfragen können folgen

.

.

case #m2r1

.

.

end


start <taskname>


Startet einen Task.Wurde der Task vorher durch „stop“ unterbrochen, wird er an der gleichen Stelle ausgeführt, an der er unterbrochen wurde. Ein Task, dessen Ausführung beendet ist (keine Schleife), muss erst durch „kill“ neu initialisiert werden.


stop <taskname>


Stoppt einen Task. Wird der task mit start <taskname> gestartet, macht er dort weiter, wo er aufgehört

hat. stop <taskname> ist so etwas wie eine "pause" - Taste.


task <taskname>


Definiert einen Task. Man kann sich einen Task vorstellen, wie ein Unterprogramm, das zwischendurch

immer wieder aufgerufen wird. Die Definition muss immer durch ein "end" beendet werden.

Siehe auch : start, stop, kill

Wird der Programmteil innerhalb des Tasks nicht durch eine Schleife wiederholt (z.B. durch ein goto), wird der Task nur ein einziges mal ausgeführt, kann aber durch „kill“ und anschließendes „start“ neu gestartet werden.

Innerhalb eines Tasks können lokale Variablen definiert werden. Wichtig ist: Die Variablendefinition muss direkt hinter der Taskdefinition stehen.


Beispiele:


Task ohne lokale Variablen, nur einmal ausgeführt:


task reset

motor 1,0

end


Sobald bei der Ausführung das „end“ erreicht wird, bleibt der Task an dieser Stelle stehen.


Task ohne lokale Variablen, dauernd ausgeführt:


task blinken

blinkloop:

output 1,8

waitms 200

output 1,0

waitms 200

goto blinkloop

end


Task mit lokalen Variablen:


task test

var a long

var b long

a = 1

b = 0

loop:

motor a,b

a = a + 1

if a > 4

a = 1

end

goto loop

end


var <variable> <typ>


Definiert (je nach Position innerhalb des Programms) eine globale oder lokale Variable.

Beispiele:


var variable1 long

var x int

var y double


Variablennamen müssen immer mit einem Buchstaben beginnen und dürfen anschließend nur Buchstaben, Zahlen oder den Unterstrich _ beinhalten.


waitms <Ausdruck>


Wartet die angegebene Zeit in ms. Währenddessen werden andere Tasks weiter ausgeführt.


Beispiele:


waitms 1000

waitms a


while <Vergleich>

<Anweisungsblock>

end


Führt den Anweisungsblock so lange aus, wie der Vergleich wahr ist.


5. Beispielprogramme



Dieses Programm schaltet Motor 1 und 2 links oder rechts abhängig

vom Zustand der Eingänge 1 und 2. Wird Eingang 8 betätigt, wird das

Programm beendet.

-------------------


main:

if input(1)=1

motor 1,8

else

motor 1,-8

end

if input(2)=1

motor 1,8

else

motor 1,-8

end

if input(8)=0

goto main

end


-------------------



Das gleiche Programm, nur mit Multitasking mit einem Task für jeden

Motor.

-------------------

task motor1

t1loop:

if input(1)=1

motor 1,8

else

motor 1,-8

end

goto t1loop

end

task motor2

t2loop:

if input(2)=1

motor 2,8

else

motor 2,-8

end

end


main:

start motor1

start motor2

endloop:

if input(8)=0

goto endloop

end


-------------------



Für dieses Programm sollte man 8 Lampen an die Ausgänge anschließen.

Die Lampen blinken unabhängig voneinander durch die unterschiedlichen

Wartezeiten. Dies ist der Funktionstest für die Multitasking-Funktion.

-------------------

task motor1

motor1loop:

motor 1,8

waitms 500

motor 1,-8

waitms 500

goto motor1loop

end


task motor2

motor2loop:

motor 2,8

waitms 525

motor 2,-8

waitms 525

goto motor2loop

end


task motor3

motor3loop:

motor 3,8

waitms 550

motor 3,-8

waitms 550

goto motor3loop

end


task motor4

motor4loop:

motor 4,8

waitms 575

motor 4,-8

waitms 575

goto motor4loop

end


main:

start motor1

start motor2

start motor3

start motor4

endloop:

goto endloop