NXC: Einfache Programme für Einsteiger

NXC, C/C++, Lejos, pbLua, RobotC...

Moderator: Moderatoren

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:19

ein paar Programm-Beispiele für Einsteiger, entnommen aus Debacher Wiki http://www.debacher.de/wiki/NXC#Das_Men.C3.BC_Tools,
erweitert um ein paar eigene Beiträge von mir selber:

Das erste Programm

Als erstes Beispiel soll ein Klang abgespielt werden, dazu kann das folgende einfache Programm dienen:

Code: Alles auswählen

/* Programm zur Erzeugung eines einzelnen Tones
   geschrieben von Uwe Debacher 2008            */

task main()
{
 PlayTone(262, 400);
 Wait(500);
}


Hier kann man schon sehr viele Elemente von C bzw. NXC erkennen. Jedes Programm besteht aus einem Hauptprogramm (bzw. Task) namens main(). Alles was zu main gehört befindet sich innerhalb der geschweiften Klammern {..}.

Innerhalb des Hauptprogrammes stehen zwei Befehle. Der Befehl

PlayTone(Frequenz, Dauer)

verfügt über zwei Parameter, die Frequenz des Tones in Hz und die maximale Dauer in Millisekunden. Bei NXC ist es aber ähnlich wie bei der grafischen Umgebung, der Roboter wartet nicht unbedingt auf das Ende eine Befehles. Damit man den Ton dann auch wirklich hört muss man das Programm dazu bringen etwas zu warten, dafür gibt es auch hier einen Wartebefehl nämlich

Wait(Dauer)

Auch hier ist die Dauer wieder in Millisekunden angegeben.

Am Anfang des Programmes stehen noch zwei Zeilen, die vor allem bei größeren Projekten wichtig sind, nämlich Kommentarzeilen. Ein guter Programmierer kommentiert seinen Quelltext ausführlich, wobei der Kommentar leicht einmal umfangreicher werden kann, als das eigentliche Programm. In dem Beispiel ist ein mehrzeiliger Kommentar zu sehen, der mit /* beginnt und */ endet. Man kann auch einzeilige Kommentare benutzen, die müssen nicht beendet werden und beginnen mit der Zeichenkombination //.

Wie bei allen Programmierumgebungen sollte man sich auch bei BricxCC angewöhnen seine Projekte regelmäßig zu speichern. Als Datei-Typ wählt man NXC-File aus, dann bekommt die Datei die Endung .nxc angehängt.

Das Programm kann man nun leicht erweitern:

Code: Alles auswählen

/* Programm zu Ausgabe eines Liedes
   geschrieben von Uwe Debacher 2008  */
   
#define d6 1175
#define e6 1319
#define f6 1397
#define g6 1568
#define a6 1760
#define h6 1976

task main()
{
 PlayTone(d6, 400);  Wait(500);
 PlayTone(e6, 400);  Wait(500);
 PlayTone(f6, 400);  Wait(500);
 PlayTone(g6, 400);  Wait(500);
 PlayTone(a6, 800);  Wait(900);
 PlayTone(a6, 800);  Wait(900);
 PlayTone(h6, 400);  Wait(500);
 PlayTone(h6, 400);  Wait(500);
 PlayTone(h6, 400);  Wait(500);
 PlayTone(h6, 400);  Wait(500);
 PlayTone(a6, 800);  Wait(900);
}

Hier wird der erste Teil von "Alle meine Entchen" angespielt. Zwei Dinge sind neu in dieser Erweiterung. Die Wait-Befehle stehen jetzt hinter den PlayTone Befehlen, man darf also mehrere Befehle in eine Zeile schreiben.

Am Anfang des Programmes werden die Notentöne definiert. Dazu werden Konstanten benutzt. Eine Konstanten-Definition fängt immer an mit

#define

danach folgt dann ein Name für die Konstante und zuletzt ihr Wert. Eine Konstante kann im Laufe des Programmes nicht verändert werden, ein wichtiger Unterschied zu Variablen.
Zuletzt geändert von HaWe am 30. Jan 2011 11:50, insgesamt 2-mal geändert.

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:23

Das zweite Programm

NXC kann auch das Display des Roboters ansteuern. Ein einfaches Beispiel zeigt das folgende Listing.


Code: Alles auswählen

// Programm zur Ausgabe auf das Display
task main()
{
 TextOut(0, LCD_LINE2, "Hallo Welt");
 Wait(5000);
}

In der ersten Zeile taucht zur Abwechslung ein einzeiliger Text-String auf. Im Hauptprogramm ist dabei die zentrale Zeile

Code: Alles auswählen

TextOut(x, y, Nachricht, (optional:) clear=false)


Anm.: das
clear=false
kann man auch komplett weglassen!




Die ersten beiden Angaben bezeichnen die Position, an der die Textausgabe beginnt. Da man oft mit mehreren Zeilen arbeiten möchte gibt es für die Zeilenposition vordefinierte Konstanten LCD_LINE1 bis LCD_LINE8. Der dritte Parameter ist der eigentliche Text. Der vierte Parameter ist optional und legt fest, ob das Display vor der Ausgabe gelöscht werden soll, oder nicht. Wenn man den Parameter weglässt, dann wird nicht gelöscht.

Da aber die Ausgabe normalerweise durch das folgende Programmende wieder gelöscht würde folgt noch ein Warteblock.

Im Gegensatz zu der grafischen Programmierumgebungen gibt es hier auch die Möglichkeit, außer Zeichen auch Zahlen auf dem Bildschirm auszugeben, wie folgende Erweiterung des Programmes zeigt.

Die Funktion

Code: Alles auswählen

NumOut(x, y, Wert, clear=false)
gibt eine Zahl direkt auf dem Bildschirm aus.




Code: Alles auswählen

// Programm zur Ausgabe auch von Zahlen auf das Display
task main()
{
 TextOut(0, LCD_LINE2, "Hallo Welt");
 NumOut(0, LCD_LINE8, 2009);
 Wait(5000);
}


HaWe aka Ford hat geschrieben:Das gleiche, etwas abgewandelt mit Integer- und Fliesskomma (float-) Variablen:

Code: Alles auswählen

// Programm zur Ausgabe auch von Zahlen auf das Display, verändert
task main()
{
  int i;
  float f;
  i=7;
  f=8.123;
  TextOut(0, LCD_LINE2, "Hallo Welt");
  NumOut(0, LCD_LINE7, i);
  NumOut(0, LCD_LINE8, f);
  Wait(5000);
}



Es gibt auch eine Reihe von weiteren Befehlen, mit denen man recht aufwändige Zeichnungen erstellen kann. Es gibt sogar Spiele für den NXT, die in NXC geschrieben wurden.

Code: Alles auswählen

ClearScreen() löscht den Bildschirminhalt

NumOut(x, y, Wert,clear=false) gibt eine Zahl aus

TextOut(x, y, Nachricht, clear=false) gibt einen Text aus

GraphicOut(x, y, filename, clear=false) gibt eine Bitmapdatei aus, die sich auf dem Roboter befindet

CircleOut(x, y, Radius, clear=false) zeichnet einen Kreis mit dem Mittelpunkt (x,y) und dem angegeben Radius

LineOut(x1, y1, x2, y2, clear=false) zeichnet eine Linie vom Punkt (x1,x2) zum Punkt (x2,y2)

PointOut(x, y, clear=false) zeichnet einen einzelnen Punkt

RectOut(x, y, Breite, Höhe, clear=false) zeichnet ein Rechteck mit der linken oberen Ecke in in (x,y) und den angegebenen Abmessungen

ResetScreen() setzt den Bildschirm zurück auf den Standard-Inhalt eines laufenden Programmes
Zuletzt geändert von HaWe am 19. Mai 2010 22:18, insgesamt 7-mal geändert.

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:26

Motoren an

Für die Ansteuerung der Motoren gibt es natürlich eine Vielzahl von Funktionen. Die Mindstorms verfügen bekanntlich über drei Motorausgänge mit den Bezeichnungen A, B und C. Für diese Ausgänge sind passende Konstanten definiert:

OUT_A
OUT_B
OUT_C

Da man häufiger mehrere Motoren gleichzeitig ansteuern möchte sind auch alle Kombinationen vorgesehen:

OUT_AB
OUT_AC
OUT_BC
OUT_ABC

Ein einfaches Programm könnte also folgendermaßen aussehen:

Code: Alles auswählen

// Programm Motoren an

task main()
{
 OnFwd(OUT_BC, 50);
 Wait(5000);
 Off(OUT_BC); 
}


Hier werden zuerst die Motoren B und C eingeschaltet und zwar auf 50% der Leistung

OnFwd(Ausgang, Leistung)

Dann muss wieder gewartet werden, damit der Roboter sich auch wirklich bewegen kann, bevor das Programm beendet ist und nach Ablauf der Wartezeit werden die Motoren abgeschaltet.

Off(Ausgang)

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:28

Wiederholungen allgemein

NXC kennt eine Vielzahl von Wiederholstrukturen, die sogenannten Schleifen:


while (Bedingung) Befehl

Die Übersetzung für das while wäre solange. Solange wie die Bedingung erfüllt ist wird der Befehl ausgeführt. Beispiel:

Code: Alles auswählen

x=0;
while (x<10)
{
  x = x + 1;
  NumOut(0,LCD_LINE1,x);
  Wait(500);
}



until (Bedingung) Befehl

Bei dieser Art von Wiederholung wird die Bedingung logisch umgedreht:

Code: Alles auswählen

x=0;
until (x>9)
{
  x = x + 1;
  NumOut(0,LCD_LINE1,x);
  Wait(500);
}

Mit der Kurzform

until (Bedingung);

dieser Schleife kann man die Warteblocks vom NXT-G nachbilden:

until(Sensor(S3>10));



Anmerkung:
"until" ist kein Original-C-Befehl, er ist in der Definition der C-Sprache unbekannt.
Man sollte "until" daher möglichst vermeiden und sich auf echte C-Befehle wie "while" etc. beschränken.


Im nächsten Abschnitt findet sich dazu ein Anwendungsbeispiel.

do { Befehle } while ( Bedingungen )
// Befehle im "Körper" in geschweiften Klammern, die Bedingungen in runden Klammern!

Ähnelt der vorherigen Struktur, mit dem Unterschied, dass hier der Befehl mindestens einmal ausgeführt wird, da die Bedingung erst hinterher überprüft wird.

Code: Alles auswählen

x=0;
do
{
  x = x + 1;
  NumOut(0,LCD_LINE1,x);
  Wait(500);
}
while (x<10);


for(ausdruck1; bedingung; ausdruck2) Befehl

ausdruck1 ist die Start-Einstellung(z.B. x=1)
bedingung ist die Gültigkeit, dass z.B. dass immer x<11 sein soll, ab x=11 ist dann Schluss
ausdruck2 ist der Schleifenzähler, z.B. nach jedem Duchlauf x um 1 erhöhen (x=x+1).

Es ist eine sehr oft genutzte Art der Wiederholung, vor allem dann nützlich wenn die Zahl der Wiederholungen bzw. Anfangs- und Endwert einer Schleifenvariaben bekannt sind.

Code: Alles auswählen

 for(x=1; x<11; x=x+1)
 {
   OutNum(0,LCD_LINE1,x);
   Wait(500);
 }

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:32

Die Sensoren in NXC

Im Beispielprogramm Hindernis wird bereits ein Sensor eingesetzt. Im Prinzip arbeiten alle Sensoren ähnlich, sie liefern irgendwelche Zahlenwerte an einem der vier möglichen Ports. Die Zahlenwerte sind erst einmal Roh-Werte (RAW) und können in leichter interpretierbare Werte umgerechnet werden, wenn der Typ des Sensors bekannt ist. Die RAW-Werte des Ultraschall-Sensors lassen sich z.B. als Entfernungsangaben in Zentimetern oder Zoll auslesen.
Grundlagen

Bevor also ein Sensor sinnvoll abgefragt werden kann muss erst einmal festgelegt werden, von welchem Typ er ist und an welchen Port er angeschlossen ist. Dazu gibt es einige spezifische Befehle:

Code: Alles auswählen

SetSensorTouch(port)     Erklärt den Sensor am port zum Berührungssensor (z.B.: S1)
SetSensorSound(port)     Erklärt den Sensor am port zum Soundsensor (z.B.: S2)
SetSensorLight(port)     Erklärt den Sensor an port zum Lichtsensor (z.B.: S3)
SetSensorLowspeed(port)  Erklärt den Sensor am port zum digitalen Sensor I²C, z.B. für Ultraschall (z.B.: S4).

Beim letzten Beispiel wird festgelegt, dass es sich um einen digitalen I2C-Sensor handelt. Von den mitgelieferten Sensoren gehört aber nur der Ultraschallsensor in diese Gruppe, viele verschiedene andere (Kompass, Accelerometer oder verschiedene Multiplexer) kann man aber noch dazukaufen.
HaWe aka Ford hat geschrieben:LowSpeed ist eine etwas seltsame Bezeichnung für i2c; die Übertragungsrate ist hier normalerweise 9600 bps und für i2c wirklich "low speed" - und es soll wahrscheinlich nur den Unterschied zu rs485 (high speed) an S4 deutlich machen, das über 400000bps übertragen kann. Ich finde allerdings, dass die Bezeichnung sehr unglücklich gewählt ist.


Abgefragt wird der Sensor dann mittels

Code: Alles auswählen

x = Sensor(S1);


oder, falls es sich um den Ultraschallsensor handelt mittels:

Code: Alles auswählen

x = SensorUS(S4);


Der Berührungssensor


Der Berührungssensor ist relativ kompliziert in der Abfrage, eventuell weil er so einfach ist. Wenn man nur den Zustand des Tasters abfragen will, so langt das folgende kleine Programm:

Code: Alles auswählen

// Nutzung des Berührungs-Sensors

task main()
{
  SetSensorTouch(S1);
  while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
  {
     ClearScreen(); 
     NumOut(0, LCD_LINE1, Sensor(S1));
     Wait(100); 
  }
}


Als Anzeige bekommt man eine 1, wenn der Taster gedrückt ist, ansonsten eine 0.

Wenn man den Berührungs-Sensor dazu nutzen will, um Aktionen zu starten oder zu stoppen kann es sinnvoll sein darauf zu achten, dass der Wert einmal von 0 auf 1 wechselt und dann wieder von 1 auf 0. Dann ist der Sensor gedrückt und wieder losgelassen worden. Auch zum Zählen von Tastendrücken ist der doppelte Wechsel wichtig.

Für das Zählen der Tastendrücke gibt es noch zwei besondere Modi:

Code: Alles auswählen

SetSensorMode(S1, SENSOR_MODE_EDGE); zählt alle Wechsel zwischen 0 und 1, also die klassischen Tastendrücke
SetSensorMode(S1, SENSOR_MODE_PULSE); zählt alle Wechsel zwischen 0 und 1 und zurück, also sowohl drücken als auch loslassen als eigene Aktionen


Hierzu ein kleines Beispiel:

Code: Alles auswählen

// Nutzung des Berührungs-Sensors als Zähler

 task main()
{
  SetSensorTouch(S1);
  SetSensorMode(S1, SENSOR_MODE_PULSE);
  while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
  {
     ClearScreen();
     NumOut(0, LCD_LINE1, Sensor(S1) );
     Wait(100);
  }
}

Gerade beim Zählen der Tastendrücke kann es sinnvoll sein den Zähler auch wieder zurück setzen zu können:

Code: Alles auswählen

ClearSensor(S1);


macht genau das.

Der Soundsensor

Der Soundsensor misst die Intensität von Geräuschen, wobei Werte auftreten. die in Dezibel (dB) skaliert sind. Dabei kann man sich gut in Erinnerung bringen, dass die Schmerzschwelle des Menschen bei einem Geräuschpegel von 130 dB liegt. Eine Erhöhung des Schalldruckpegels um 10dB wird subjektiv als Verdoppelung der Lautstärke empfunden. Eine Verdoppelung der Schallsignale, also zwei gleiche Quellen statt einer, bewirken eine Erhöhung um 3dB.

Code: Alles auswählen

// Nutzung des Sound-Sensors

task main()
{
   SetSensorSound(S2);
   while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
   {
      ClearScreen();
      NumOut(0, LCD_LINE1, Sensor(S2) );
      Wait(100);
   }
}


Der Lichtsensor

Der Lichtsensor misst die Intensität des Lichtes, wobei Werte zwischen 0 und 100 auftreten können.

Code: Alles auswählen

// Nutzung des Licht-Sensors

task main()
{
  SetSensorLight(S3);
  while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
  {
     ClearScreen();
     NumOut(0, LCD_LINE1, Sensor(S3) );
     Wait(100);
  }
}


Der Befehl SetSensorLight aktiviert dabei auch die Lichtquelle des Sensors, so dass in der Regel die Menge des reflektierten Lichtes gemessen wird. Soll die interne Lichtquelle nicht aktiviert sein, so muss man sie mittels

Code: Alles auswählen

SetSensorType(S3, SENSOR_TYPE_LIGHT_INACTIVE);


ausgeschaltet werden.

Code: Alles auswählen

// Nutzung des Licht-Sensors ohne eigene Lichtquelle

task main()
{
  SetSensorLight(S3);
  SetSensorType(S3, SENSOR_TYPE_LIGHT_INACTIVE);
  while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
  {
      ClearScreen();
      NumOut(0, LCD_LINE1, Sensor(S3) );
      Wait(100);
  }
}



Der Ultraschallsensor

Der Ultraschallsensor unterscheidet sich in seiner Ansteuerung von den anderen mitgelieferten Sensoren. Es handelt sich bei ihm um einen digitalen I2C-Sensor, der über spezielle Kommandos zur Ansteuerung verfügt. Die Messwerte des Sensors geben die Entfernung zu einem Hindernis in cm an.

Code: Alles auswählen

// Nutzung des Ultraschall-Sensors

task main()
{
   SetSensorLowspeed(S4);
   while(true)                  // wiederholt den "Körper" der zwischen den geschweiften Klammern { } steht, dauerhaft
   {
      ClearScreen();
      NumOut(0, LCD_LINE1, SensorUS(S4) );
      Wait(100);
   }
}
Zuletzt geändert von HaWe am 19. Mai 2010 22:20, insgesamt 8-mal geändert.

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 25. Okt 2009 13:35

Die (Dreh-) Sensoren der Motoren

In die Motoren der NXT-Roboter sind Drehsensoren eingebaut, über die die Position recht exakt kontrolliert werden kann. Diese Sensoren verfügen über keinen zusätzlichen Anschluss, werden also über die Motor-Ausgänge abgefragt. Diese Drehsensoren nennt man auch "Encoder".

Code: Alles auswählen

// Nutzung der Rotations-Sensoren

task main()
{
 OnFwd(OUT_B, 50);
 while(true)
 {
   NumOut(0, LCD_LINE1, MotorRotationCount(OUT_B), true);
   Wait(100);
 }
}


Der Sensor registriert nicht nur die Veränderungen, die aktiv vom Motor erzeugt werden, man kann auch einfach an dem entsprechenden Rad drehen, worauf sich der Wert ebenfalls verändert.

Die Messwerte sind übrigens auf 1° genau, eine vollständige Umdrehung des Motors ergibt also 360° .

Den Sensor kann man mittels

Code: Alles auswählen

 ResetRotationCount(OUT_B);


jederzeit auch zurück setzen.

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 8. Sep 2012 17:41

Unterprogramme: Prozeduren und Funktionen

Wie in C können häufig gebrauchte Berechnungen in Unterprogramme verpackt werden.

Unterprogramme ohne Rückgabewert werden als void deklariert ("void" heißt soviel wie "ohne Wert"), z.B.
void tuwas();
sollen Unterprogramme einen Wert an den aufrufenden Befehl zurückgeben, werden sie entsprechend dem Rückgabewert deklariert:
int addiere();
float dividiere();
string schreibe();

In die Klammern hinter die Unterprogramm-Deklaration können Parameter oder Variablen übergeben werden (z.B. int Zahl1, int Zahl2), was dann zu tun ist, beschreibt der Funktions-Code, man schreibt ihn hinter die Deklaration in geschweifte Klammern; das ist der sog. "Körper" des Unterprogramms.
Unterprogrammen mit Rückgabewert muss man in ihrem "Programm-Körper" sagen, wann sie welchen Wert zurückgeben sollen, dies geschieht mit dem Befehl return(...).;
wenn kein Rückgabewert deklariert wird ("void"), braucht man das natürlich nicht.
z.B.:

Code: Alles auswählen

int addiere(int Zahl1, int Zahl2) {
  int Ergebnis;
  Ergebnis=Zahl1 + Zahl2;
  return (Ergebnis);
}

task main() {
  int test;
  test=addiere(6, 7);    // addiert: 6+7, also 13
  NumOut(0,56, test);    // Ausgabe: 13

  test=addiere(12, 200); // addiert: 12+200, also 212
  NumOut(0,48, test);    // Ausgabe: 212

  Wait(3000);
}

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 8. Sep 2012 18:34

Multitasking

NXC kann außer dem task main() noch eine ganze Reihe von parallel-laufenden Tasks steuern
(die genaue Anzahl ist nicht exakt limitiert, nur die Gesamtzahl von Funktionen, Prozeduren und Tasks darf max. 127 nicht überschreiten (IIRC) ).

Alle Tasks werden im Programm deklariert wie eine Prozedur, nur mit dem Schlüsselwort task anstelle von void - hinter dem Tasknamen eine Doppel-Klammer ()!
Genau wie Prozeduren werden Tasks von Anfang bis Ende Zeile für Zeile durchlaufen, erreicht man die lezte Zeile, wird der Task automatisch beendet.
Will man also einen Task dauerhaft am Laufen halten, muss man die Befehle in eine Endlos-Schleife packen.
Gestartet wird jeder Einzel-Task durch den Befehl start taskname ohne die Doppel-Klammer ()

Code: Alles auswählen

// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen
// und per Hand an den Motoren drehen!
// die Encoderwerte werden simultan angezeigt!

#define CLEOL  DRAW_OPT_CLEAR_EOL

task  DisplayValues() {
  while(true) {
    TextOut(0,56, "Enc.A:", CLEOL); NumOut(42,56, MotorRotationCount(OUT_A));
    TextOut(0,48, "Enc.B:", CLEOL); NumOut(42,48, MotorRotationCount(OUT_B));
    TextOut(0,40, "Enc.C:", CLEOL); NumOut(42,40, MotorRotationCount(OUT_C));
    Wait(10);
  }
}


task main() {
  start DisplayValues;

  while(true);

}


Jetzt könnte man einen weiteren Task laufen lassen, der die Motoren automatisch steuert. Das könnte man hier zur Demonstration per Zufallszahlengenerator Random() machen:

Code: Alles auswählen

// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen !
// die Motoren werden automatisch angesteuert,
// die Encoderwerte werden simultan angezeigt!

#define CLEOL  DRAW_OPT_CLEAR_EOL

task  DisplayValues() {
  while(true) {
    TextOut(0,56, "Enc.A:", CLEOL); NumOut(42,56, MotorRotationCount(OUT_A));
    TextOut(0,48, "Enc.B:", CLEOL); NumOut(42,48, MotorRotationCount(OUT_B));
    TextOut(0,40, "Enc.C:", CLEOL); NumOut(42,40, MotorRotationCount(OUT_C));
    Wait(10);
  }
}


task MotorControl() {
  int speed;
 
  while(true) {
    speed=Random(201) - 100; // ergibt eine Zufallszahl für die Motorleistung  zwischen -100 und +100
    OnFwd(OUT_A,speed);

    speed=Random(201) - 100;
    OnFwd(OUT_B,speed);

    speed=Random(201) - 100;
    OnFwd(OUT_C,speed);
   
    Wait( 200+ (Random(2800)) ); // ergibt eine Zufallszahl für die Aktionsdauer von 200 - 3000 ms
  }
}


task main() {
  start DisplayValues;
  start MotorControl;

  while(true);

}

encoder_000.zip
avi-Video von der Display-Anzeige
(Refresh-Cycles hier nur alle 50ms, also deutlich langsamer als original!)
(129.19 KiB) 299-mal heruntergeladen

Wichtig: alle Tasks laufen maximal solange wie der task main(). Wird der task main() also beendet, werden auch alle restlichen Tasks mit beendet.
Anders herum: will man, dass Tasks andauernd laufen, muss man auch den task main() andauernd am Leben halten (z.B. durch while(true); am Ende).
Will man einen Task von einem anderen Task aus vorzeitig beenden, verwendet man den Befehl
stop taskname;

Sinn und Unsinn von Multitasking:

Sinn macht Multitasking allerdings nur dann, wenn Vorgänge oder Aufgaben absolut asynchron und unabhängig ablaufen müssen und man sie nicht gemeinsam in 1 Schleife hintereinander schreiben kann. Auch macht es keinen Sinn, Tasks schneller laufen zu lassen, als es die Hardware hergibt:
Sensorwerte brauchen nicht viel schneller ausgelesen werden als alle 5 - 20 ms, es verschwendet sonst nur unnötig CPU-Power: Analogsensoren geben nur ca. alle 5-ms neue Werte, I²C-Sensoren nur ca. alle 20 ms. Andernfalls liest man nur jeden identischen Wert doppelt und dreifach ohne jeden erkennbaren Nutzen, und im Falle von I²C-Sensoren wird der I²C-Bus unnötig oft durch Abfragen blockiert.

Programmtechnisch macht es auch wenig Sinn, Werte im 10ms-Takt anzuzeigen, wenn das Auge nur ca. alle 30ms Bilder voneinander unterscheiden kann. Also könnte im Anzeige-Task ein Wait(30) oder Wait(40) in der Schleife stehen - es geht ja nur ums Sichtbarmachen.
Es kann aber durchaus notwendig sein, die Encoderwerte deutlich schneller (z.B. alle 2 ms) auszulesen als sie im Display-Task angezeigt werden, um Grenzwerte schnell zu erkennen und entsprechend schnell darauf zu reagieren.

Eine mögliche Erweiterung für das obige Demo-Programm könnte jetzt sein:
Encoder-Werte im 2-ms-Takt in einem Encoder-Task auslesen, global abspeichern, aber nicht direkt anzeigen, sondern die globalen Variablen-Werte im Display-Task zur Anzeige einreihen.
Dann verschiedene Sensoren anschließen und in einem zusätzlichen Sensor-Task abfragen, z.B. alle 20 ms. Man speichert sie dann ebenfalls sofort in globalen Variablen ab, deren Werte könnten dann (schnell) weiterverarbeitet werden, aber gleichzeitig deutlich weniger schnell (alle 40ms) im Display-Task unter den Encoder-Werten angezeigt werden.

Schöne Übung zum Selberprobieren!
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5398
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC: Einfache Programme für Einsteiger

Beitragvon HaWe » 9. Mär 2014 10:36

weiterführende Tutorials und Literatur (vgl. auch allgemeine Link-Listen hier im Forum!):

Debacher Wiki http://www.debacher.de/wiki/NXC

John C. Hansen - Lego (R) Mindstorms NXT Power Programming: Robotics in C
ISBN-10: 0973864974
ISBN-13: 978-0973864977

Roberta - Programmieren mit NXC
von Daniele Benedettelli, Übersetzung und Ergänzungen von Thorsten Leimbach, Sebastian Trella
http://roberta-home.de/sites/default/files/Roberta_Band_NXT_Programmierung_FINAL.pdf

sowie:
http://lukas.internet-freaks.net/nxt.php#programmierung
http://www.ist.uni-stuttgart.de/robolab/NXC-Hilfe.pdf

edit,
sorry, wie konnte ich dieses vergessen zu erwähnen:
von Frank Engeln: http://www.engeln.info/NXC_2011.pdf :D

admin hat geschrieben:Hab ja mal so´n Büchlein vor einiger Zeit geschrieben. Hier das komplette Skript:
http://www.engeln.info/NXC_2011.pdf
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E


Zurück zu „textbasierte Programmiersoftware“

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 20 Gäste

Lego Mindstorms EV3, NXT und RCX Forum : Haftungsauschluss