Preemptives Multithreading: libs für Sketch?

hallo, gibt es denn nun eigtl Multithreading libs für Sketch? Ich habe da dunkel was von OSEK in Erinnerung (gibt es ja auch für den NXT und Toppers/ATK C/C++) ; aber auch POSIX libs (pthread) sind ja in C/C++ geschrieben, kann man die ggf implementieren? http://en.wikipedia.org/wiki/POSIX_Threads

  • Oder evtl gibt's vllt auch was ganz anderes?

Leider sagst du nicht, auf welchem Kessel das laufen soll......

Für die kleinen kannste preemtives Multi-threading/tasking fast vergessen. Zu wenig Speicher für die Taskcontrollblocks, kein Speicherschutz.

Kooperativ geht da was. Ich nutze http://dunkels.com/adam/pt/

Beim den ARM basierenden ist da mehr drin..

ja, ich meinte eigtl auch für den Due.

und hast du die proto threads schon mal ausprobiert?

#include "pt.h"

struct pt pt;
struct timer timer;

PT_THREAD(example(struct pt *pt))
{
  PT_BEGIN(pt);

  while(1) {
    if(initiate_io()) {
      timer_start(&timer);
      PT_WAIT_UNTIL(pt,
         io_completed() ||
         timer_expired(&timer));
      read_data();
    }
  }
  PT_END(pt);
}

und hast du die proto threads schon mal ausprobiert?

Ja, ist im Dauereinsatz bei mir! Tuts.

Ein paar Besonderheiten sind zu beachten. Stichwort lokale Variablen.

Und es bringt eine einfache Semaphoren Verwaltung mit. Für meine Anwendungen ein "MUSS SEIN" Kriterium.

Tuts auch auf dem DUE.

DUE: Im Grunde sollte man auf dem DUE ein kleines RTOS implementieren können. Leider habe ich bisher sowas noch nicht wirklich gefunden. Man wird dann wohl auf das ganze Arduino Gedöns verzichten müssen. Vieles in den Arduinolibs ist nicht Reentrant.

super, das gibt ja zu Hoffnung Anlass... 8) Dass die Funktionen nicht re-entrant sind, ist allerdings sicherlich ein Nachteil.

Kannst du das mit den lokalen Variablen etc. noch ein wenig erklären?

Wie wird die lib für Sketch installiert?

Und hast du vllt sogar ein kleines, praktisches Beispiel, das man evtl sogar auch auf dem Mega mal austesten könnte?

Wie wird die lib für Sketch installiert?

Meinst du damit die Arduino IDE? Dann:

Wie das installiert wird?

http://arduinode.googlecode.com/files/pt.zip Ich werfe den pt Ordner in C:\Dokumente und Einstellungen\UserNamei\Eigene Dateien\Arduino\libraries Fertig.

Ein Beispiel ist auch dabei.

hi, das einzige Beispiel, das ich hier finde, heißt pt_led_example.pde

das ist bei mir aber ein processing file, kein Sketch (*.ino) - ist da was falsch?

Nichts falsch. Die hießen in der alten Arduino Version mal .pde. Dann hat man .ino eingeführt. Genau aus dem Grund um sie von Processing zu unterscheiden. Du kannst einfach die .pde Datei mit der Arduino IDE öffnen und dann als .ino speichern

aso… :slight_smile:
aus dem File werde ich absolut nicht schlau und habe keine Idee, was da MT-mäßig passiert.

/* Copyright (c) 2011, Jan Clement
 * licenced under the GPL
 *
 * Author: Jan Clement <jan.clement@audiokits.de>
 * 
 * Example code to demonstrate the use of protothreads 
 * in an arduino sketch. It toggles an LED  using two 
 * independent protothreads. One pt toggles every 
 * 1000ms, the other one every 900ms. The result is an 
 * erratic blinking pattern.
 */

#include <pt.h>   // include protothread library

#define LEDPIN 13  // LEDPIN is a constant 

static struct pt pt1, pt2; // each protothread needs one of these

void setup() {
  pinMode(LEDPIN, OUTPUT); // LED init
  PT_INIT(&pt1);  // initialise the two
  PT_INIT(&pt2);  // protothread variables
}

void toggleLED() {
  boolean ledstate = digitalRead(LEDPIN); // get LED state
  ledstate ^= 1;   // toggle LED state using xor
  digitalWrite(LEDPIN, ledstate); // write inversed state back
}

/* This function toggles the LED after 'interval' ms passed */
static int protothread1(struct pt *pt, int interval) {
  static unsigned long timestamp = 0;
  PT_BEGIN(pt);
  while(1) { // never stop 
    /* each time the function is called the second boolean
    *  argument "millis() - timestamp > interval" is re-evaluated
    *  and if false the function exits after that. */
    PT_WAIT_UNTIL(pt, millis() - timestamp > interval );
    timestamp = millis(); // take a new timestamp
    toggleLED();
  }
  PT_END(pt);
}
/* exactly the same as the protothread1 function */
static int protothread2(struct pt *pt, int interval) {
  static unsigned long timestamp = 0;
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, millis() - timestamp > interval );
    timestamp = millis();
    toggleLED();
  }
  PT_END(pt);
}

void loop() {
  protothread1(&pt1, 900); // schedule the two protothreads
  protothread2(&pt2, 1000); // by calling them infinitely
}

Was ich nicht verstehe - jetzt am Beispiel eines anderen Programms -
gesetzt den Fall, wir haben ein hypothetisches Programm mit 5 Threads mit gleicher Priorität.

Jede Zeitscheibe soll 200µs dauern dürfen.
1 Scheduler-Dauer ist insgesamt also 1ms.

2 Threads (1+2) führen sehr lange, komplexe Berechnungen durch (z.B. eine lange Taylorreihenentwicklung oder Fourier-Transformation, für die sie durchaus jeweils mehrere 100ms pro Berechnung brauchen könnten),

1 fragt alle 200µs, jeweils wenn er dran ist, eine ganze Reihe von Sensordaten ab (digital+analog,+ i2c, so weit er gerade kommt)

1 Thread schreibt Werte und Grafen aufs Display (z.B. aus vorliegenden Sensorwerten und Fourier-Transformationen)

1 steuert das Fahrzeug, per Navigationsroutine und Odometrie und abhängig von den Sensordaten durch ein Labyrinth, ohne anstoßen zu dürfen .

  • wird sichergestellt, dass, wenn thread 1 nach 200µs nicht mit seiner Berechnung fertig ist, alles gesichert wird,
    dann erst die anderen 4 drankommen ?

  • und dann Thread 1, wenn er nach 1ms wieder dran kommt, genau da weitermacht, wo er unterbrochen wurde ?

  • und trotzdem die Sensordaten dazwischen ununterbrochen schnell und regelmäßig abgefragt werden, ohne den Navigator zu stören ?

  • und auch die Werte und Graphen zeitgleich “scheibchenweise” aufs Display geschrieben werden?

Werden die Daten und Register und Pointer und wasweißich alles von jedem Thread also irgendwie zwischendurch auf einem Stack gesichert, dass sie nach der Unterbrechung wieder komplett zurückgeschrieben werden und es dann reibungslos weitergehen kann?

Und wenn meine main loop() pro Durchlauf (wegen anderer Dinge) nur alle 5ms eine Runde dreht - wie kann sichergestellt werden, dass die Einzeltasks unabhängig dennoch im 200µs Takt laufen?

Hmm...

Offensichtlich suchst du preemptives Multitasking. Die Protothread Lib liefert dir aber nur kooperatives.

Also: Weiter suchen.

ja, genau, denn der Sinn muss sein, die Zeitscheiben immer gerecht auf alle Threads zu verteilen, damit keiner zu kurz kommt oder komplett geblockt wird. (edit: im Titel ergänzt)

als Anschauungsmaterial:
so funktioniert es mit pthread (POSIX libraries für Linux mit gpp C/C++), und so etwas suche ich für einen “größeren” Arduino (Mega, Due etc.):

// mt-demo.c
// 0.64
// Autor: Helmut Wunder aka HaWe
//
// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen !
// die Motoren werden automatisch angesteuert,
// die Encoderwerte werden simultan angezeigt,
// auch bei pass. Verdrehung bei stromlosen Motoren während der Pausen!

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_lcd.h"
#include "ev3_sound.h"
#include "ev3_output.h"

volatile int flag_threadrun = 0;


//------------------------------------------------------------------------------


void *DisplayValues(void *threadid) {
  char buf[120];
 
  while(flag_threadrun) {

    sprintf (buf, "Enc.A: %5d", MotorRotationCount(OUT_A)); LcdText(1, 0, 10, buf);
    sprintf (buf, "Enc.B: %5d", MotorRotationCount(OUT_B)); LcdText(1, 0, 20, buf);
    sprintf (buf, "Enc.C: %5d", MotorRotationCount(OUT_C)); LcdText(1, 0, 30, buf);

    Wait(40);
  }
 
  pthread_exit(0);
}


//------------------------------------------------------------------------------


inline unsigned long Random(unsigned long x) {
   return (rand() % x);
}


inline void Motor(int outputs, int pwr) {

  SetPower(outputs, pwr);
  On (outputs);

}



void *MotorControl(void *threadid) {
  int speed;

  while(flag_threadrun) {
    speed=Random(201) - 100; // ergibt eine Zufallszahl für die Motorleistung  zwischen -100 und +100
    Motor(OUT_A, speed);

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

    speed=Random(201) - 100;
    Motor(OUT_C, speed);

    Wait( 200+ (Random(2801)) ); // ergibt eine Zufallszahl für die Aktionsdauer von 200 - 3000 ms
  }
  pthread_exit(0);
}


//------------------------------------------------------------------------------


main()  {

  unsigned short btn;

  // initialize
  OutputInit();
  LcdInit();
  ButtonLedInit();
  ResetAllTachoCounts(OUT_ABCD);

  flag_threadrun=1;

  pthread_t f2_thread, f1_thread;

  pthread_create(&f1_thread,NULL,MotorControl,NULL);
  pthread_create(&f2_thread,NULL,DisplayValues,NULL);

  while(1) {
    if ( checkButtons() )  break;
    Wait(100);
  }

  flag_threadrun=0;

  pthread_join(f1_thread,NULL);
  pthread_join(f2_thread,NULL);

  OutputClose();
  OutputExit();
  ButtonLedClose();
  ButtonLedExit();
  LcdExit();

}

http://code.google.com/p/rtoslibs/

hallo, danke für die Infos! Sind die libs für Sketch verfügbar? Es steht da sehr viel, aber nichts speziell zu Sketch soweit ich sehe - oder habe ich was übersehen? Gibt es Beispiele für Sketch?

So in der Art:

include "lib.h"

und dann tasks deklarieren (wie Funktionen) tasks starten (wie Funktionsaufrufe mit Endlosschleifen und Abbruchbedingung) nach Ende: tasks aufräumen (s. pthread)

Ja.
Zumindet das freertos habe ich (an)getestet.

Es gibt auch noch ArdOS…(google wirds dir zeigen)

könntest du bitte mal einen Link zu freeRTOS Arduino-Sketch-Impementierungen schicken? Ich finde leider nichts. :(

HaWe: könntest du bitte mal einen Link zu freeRTOS Arduino-Sketch-Impementierungen schicken? Ich finde leider nichts. :(

Äääähhmmm ...... Habe ich doch..... Ein paar Postings vorher. Da gibts einen Downloadbereich.

Hier mal ein Direktlink: http://code.google.com/p/rtoslibs/downloads/detail?name=FreeRTOS20130714.zip&can=2&q=

aaahhh! danke! jetzt hab ich's, und schau es mir gleich an!

so, nochmal vielen Dank, ich habe mir die Beispiele angesehen.

Die Includierung scheint über die Sketch libs ja sehr leicht zu sein, auch die Implementierung scheint nicht übermäßig schwierig, wenngleich ich aus den einzelnen Beispielen auch zugegebenermaßen nicht schlau werde.

Ein Programmbeispiel, wo - ein Thread eine ewig lange Endlosberechnung macht - und ein zweiter eine led mit delay(100) blinken lässt - und ein dritter eine andere led mit einem delay(500) blinken lässt, - und ein vierter, der Zwischenergebnisse aus dem 1. Thread in Abständen von 1 sec per serial.print ausgibt ...

  • das wäre ein gutes Anschauungsmaterial! 8)

ps, edit: ein delay() darf übrigens in MT-Systemen keine Zeitscheibe bis zu ihrem regulären Ende blockieren, sond müsste wie "yield()", wie es oft auch genannt wird, sofort, vorzeitig, automatisch zur nächsten Zeitscheibe überspringen, bis insgesamt die delay-Zeit erreicht ist.