Timer Setzen

Grüßt Euch!

Durch meine Projektarbeit bin ich an Arduino gestossen und es macht richtig Spaß, damit zu arbeiten.
Ich habe ein taktiles Ausgabegerät gebaut. Es hat mehrere Vibrationsmotoren, die dann an die Haut eine bestimmte Muster bilden.
Wie oft und wann die Motoren laufen habe ich schon programmiert. Mein Problem ist nur, dass ich einen Takt brauche. D.h. die Methoden werden alle 100 ms aufgerufen.
Ich habe dann die Methode millis() benutzt und mit Modulo gearbeitet.
In einem Array setze ich die Werte ein, wann ein Motor an(1) bzw. aus (1) ist. Mit einem Pointer gehe ich dann diesem Array entlang und prüfe die Werte.
Logisch sollte es eigentlich funktionieren, doch wenn ich mehrere Methoden aufrufe, komm ich ausserhalb von meinem vorgegeben Takt, und die Motoren laufen länger wie vorgegeben, bis der Zähler erhöht wird.

Kann mir einer von Euch sagen, wie ich das hinkriege, dass ich wirklich nur alle 100 ms die Motoren zum laufen bringe.

Den Code hänge ich mit an.

Danke im Voraus!
Lg,
Kadri
…CODE…

int motorPinB1 = 12;// defining the variable motorPinB1 as an integer and initialising it with the value 12. This will be the output 12 on Arduino-Board
int motorPinB2 = 8;
int motorPinS1 = 11;
int motorPinS2 = 10;
int motorPinS3 = 9;
int motorPinS4 = 6;
int motorPinS5 = 5;
int ledPinRed = 3;
int ledPinGreen = 2;

long schritt = 100; // Sampling every 100 ms. This means here one Positin on the array.

////////-----Pattern a1-----///////////
int arrayB1 ={1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // values of motor B1; if 1 then B1 is on, else off
int arrayB2 ={0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS1 ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS2 ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS3 ={0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0};
int arrayS4 ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS5 ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

////////-----Pattern a2-----/////////// comparing pattern
int arrayB1a ={1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayB2a ={0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS1a ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS2a ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS3a ={0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0};
int arrayS4a ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int arrayS5a ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// TODO: arraySize here
int arrayByte = sizeof(arrayB1); // Length of Bytes in an Array

int arraySize = 12;

void setup() {

pinMode(motorPinB1, OUTPUT); // difine motorPinB1 as an output. This means on Arduino-Board the digital output 13.
pinMode(motorPinB2, OUTPUT);
pinMode(motorPinS1, OUTPUT);
pinMode(motorPinS2, OUTPUT);
pinMode(motorPinS3, OUTPUT);
pinMode(motorPinS4, OUTPUT);
pinMode(motorPinS5, OUTPUT);
pinMode(ledPinRed, OUTPUT);
pinMode(ledPinGreen, OUTPUT);

Serial.begin(9600);
Serial.println(“Programm start …”);
delay(2000);// Give reader a chance to see the output.

//Serial.println(arrayLength);

///////////Begin control led green////////////////
digitalWrite(ledPinGreen, HIGH); //starting signal on motorPinB1
delay(1000);
digitalWrite(ledPinGreen, LOW);
delay(2000);
///////////End control led green////////////////

// Begin of pattern
playPattern( arrayB1, arrayB2, arrayS1, arrayS2, arrayS3, arrayS4, arrayS5 );
delay(2000); // wait for 2 seconds
playPattern( arrayB1a, arrayB2a, arrayS1a, arrayS2a, arrayS3a, arrayS4a, arrayS5a );

///////////Begin control led red////////////////
digitalWrite(ledPinRed, HIGH); //starting signal on motorPinB1
delay(1000);
digitalWrite(ledPinRed, LOW);
delay(2000);
///////////End control led red////////////////

} // ENDE setup()

void loop()
{
// void loop() is empty here, because we do not repeat anything. Nevertheless Arduino needs to have the loop() method otherwise it does´nt work.
}

int nextStep(int currentPointer){

long currTime = millis();

while((millis() - currTime) < 10000){ // stop if bigger than 10 s

if (millis()% 100 ==0) { // for every “schritt” seconds : here the duration of a period ex. 100 ms
++currentPointer;
Serial.println(millis());
return currentPointer;
} // ENDE if()
} // ENDE while()

} // ENDE nextStep()

void playPattern(int arrayMotorB1,
int arrayMotorB2,
int arrayMotorS1,
int arrayMotorS2,
int arrayMotorS3,
int arrayMotorS4,
int arrayMotorS5){

int pointer = nextStep(-1);
while (true){

// Break, if no more elements in an array
if(pointer == arraySize)
break;

motor(pointer, arrayMotorB1, motorPinB1);
motor(pointer, arrayMotorB2, motorPinB2);
motor(pointer, arrayMotorS1, motorPinS1);
motor(pointer, arrayMotorS2, motorPinS2);
motor(pointer, arrayMotorS3, motorPinS3);
motor(pointer, arrayMotorS4, motorPinS4);
motor(pointer, arrayMotorS5, motorPinS5);

pointer = nextStep(pointer);

} // ENDE while(pointer);

Serial.println("… ENDE des Programms!");

} // ENDE playPattern()

///////////////////////—B1—//////////////////////
void motor(int pointer, int valueArray, int motnr){

Serial.print(“Nun wird Motor am Ausgang:”); Serial.print(motnr); Serial.println(" aufgerufen");

if (valueArray[pointer] == 1)
{
digitalWrite(motnr, HIGH);
}
else
{
digitalWrite(motnr, LOW);
}
}
// ENDE Programm

Gibt’s fertig als Library, sogar in Schoko, Vanille und Erdbeer; ganz nach Gusto: Timer1, MsTimer2 und Timedaction sollten das sein, was du suchst.
http://www.arduino.cc/playground/Main/GeneralCodeLibrary#Timing_code

Und denk mal drüber nach deine int-arrays Bitweise auszudrücken.
Also: B10001010 als Binärzahl. Und dann via << bzw. >> auf die Bits zu zugreifen. (“bitmathe” in der referenz)
Bei deiner Größe wirds noch nich eng mitm Speicher, aber wenn es komplexer wird, wirds eng. Deine 10x20 integers brauchen ja schon 400Byte RAM. Bitweise wärs etwa nur nen Achtel davon.

Hallo Pumbaa, danke für deine schnelle Antwort und die Tipps.

Den Timer selber habe ich über millis() gebastelt. Ich werde jetzt den nochmal umschreiben und die TimedAction Library verwenden. Mein Problem ist immer noch nicht richtig gelöst. Das Ausführen jeder Methode kostet mir viel Zeit, sodass ich alle sieben Motoren nicht zur selben Zeit starten kann. Jeder Aufruf kostet mir ca 30 ms und wenn ich eine Funktion 7 mal aufrufe, dann komme ich erst nach 210 ms aus der Schleife raus. Das Ding sollte jedoch gleichzeitig die Motoren alle Hundert Sekunden aus bzw. anschalten. Ist es überhaupt machbar?

Gruß, Kadri

Aaah, ich dachte das genaue timing war dir wichtig, aber die Routine dauert einfach zu lange. Und das tut sie, da jedes Zeichen über die Serielle schnittstelle 1ms braucht :)

Lass mal den debug-schnickschnack raus, und es wird rennen!

Oder mach die Serielle mal doppelt so schnell. Das wirste auch schon merken.

Aber dein Programm verstehen tue ich dennoch nich... 8-)

Hi Pumbaa,

das Progamm an sich ist eigentlich recht einfach.
Ich habe eine Armmanschette gebaut. In der sind 7 verschieden Vibrationsmotoren angebracht. Diese Motoren will ich über Arduino steuern.
Ich erzeuge verschiedene Muster, deswegen habe ich ganz oben für jeden Array einen Motor definiert.
Diesen Array laufe ich dann durch, wenn es eine 1 ist, dann wird der Motor angeschaltet, bei 0 ausgeschaltet. Ein Pointer springt von einer Position im Array zum nächsten alle 100 ms. Dafür sorgt die Funktion nextStep.
Das Ganze soll eine Vorstufe eines vibrotaktilen Mp3-Players sein.

Wie gesagt, es dauert irgendwie zu lang, bis das gute Stück alle Aufrufe durch hat. Und bis zum nächsten Schritt dauert es dann nicht 100 ms sondern viel mehr…

Ich hoffe, ich habe das gut erklärt!

Danke nochmal für Deine Antwort und liebe Grüße,
Kadri

ok, klingt nun verständlich. Hatte den Ablauf in etwa verstanden, nur nicht was die Motoren machen sollten. Wenn sie nur vibrieren, ergibt's Sinn. Ich hab versucht, sowas wie eine Richtungsteuerung über H-Brücken drin zu lesen. Klappts ohne Serielle?

Hi,

welchem Zweck dient das Gerät?

Gruß Jürgen

Also mir fallen zwei Dinge ein wenn das zu langsam läuft:

1) Wegen

void motor(int pointer, int valueArray[], int motnr){

  Serial.print("Nun wird Motor am Ausgang:"); Serial.print(motnr); Serial.println(" aufgerufen");

wird motor sehr langsam sein. Serial.print kostet garantiert Zeit

2) Wenn das nicht reicht: digitalWrite ist unglaublich lahm. Direkt auf die Ports losgehen ist Welten schneller. http://news.jeelabs.org/2010/01/06/pin-io-performance/ http://www.instructables.com/id/Arduino-is-Slow-and-how-to-fix-it/

Danach sollte es keine Probleme mehr geben.

Gruß, Udo

Hallo Leute,

sorry dass ich mich nicht gemeldet habe. Musste für ein Paar Tage weg. Ich habe die ganzen Debugging-Sachen rausgenommen. Ein bisschen schneller ist es geworden. Das Problem ist aber immer noch vorhanden. Sobald ich das nur 2 Motoren verwende, funktioniert es wunderbar. Ab 3 Motoren bin ich nicht mehr unter 100 ms. Die Motoren sollten jedoch zuverlässig in einem 100 ms Takt laufen. Ich werde den Vorschlag von Udo ausprobieren. Das Ergebniss sag ich dann hier. @ Jürgen: Mit dem Gerät versuche ich den Tastsinn von Gehörlosen zu testen. Anhand der Motoren erzeuge ich abstrakte Vibrationsmuster. Diese können dann mit allerlei Informationen belegt werden. Wenn die Vibrationsmuster gut wahrnehmbar sind, kann man später Musikstücke in verschiedenen Frequenzbänder aufteilen. Dann jedem Motor einen dieser Bereiche zuordnen. Dadurch könnte man gewisse Rythmen vibrotaktil darstellen (indirekt: Entwicklung eines vibrotaktilen MP3-Players)

Danke für Eure Postings, Lg, Kadri

Also 100ms sind bei 16 MHz Takt 1 600 000 Taktzyklen. Das sollte ja wohl zu schaffen sein. Mein Teil hier läuft mit 10 kHz Interruptfrequenz, also alle 100us ein Interrupt und schaft dabei noch was sinnvolles zu tun. Da dürfte Dein Problem eigentlich noch nicht einmal in der Nähe der Leistungsgrenze des Arduino sein.

Wenn's nicht klappt liefer mal mehr Details. Das kriegen wir hin :)

Gruß, Udo

Hallo Udo,

ich wundere mich auch, dass es langsamer wird. Wie Du den Code oben gesehn hast, macht das Programm auch nicht viel. Es sind sieben Motoren die ich durch eine Matrix von Werten ansteuere. Jeder Motor wird alle 100 ms entw. aus oder angeschaltet (Dafür habe ich einen Array mit 0/1 gebaut) Ein Pointer berechnet die jetzige Position im Array. Mehr ist es echt nicht. Sobald ich mehr als 3 Motoren prüfe, wird das Programm langsamer. Bis der Pointer zur nächsten Stelle springt, dauert es länger als 100 ms und so bleibt z.B. ein Motor der an war, länger als 100 ms an. Damit verfälscht sich meine Ausgabe...

Ich habe den Vorschlag versucht, digitalWrite(); mit PORTB |= _BV(PB5); zu ersetzen aber mein Arduino gibt einen Fehler raus: error: ´PB5`was not declared in this scope http://www.instructables.com/id/Arduino-is-Slow-and-how-to-fix-it/step4/Overview/

Muss weiter experimentiern. Vielleicht habe ich einen Denkfehler im Programm, aber logisch ist es schon aufgebaut. Ich werde die Arrays noch als Bytes schreiben und nicht als int. Mein Problem ist auch noch, dass ich im Programmieren nicht sehr glänze! :( :-/ :-/

Lg, Kadri

Hast Du immer noch die Serial.print Anweisung in der playPattern Function drin? Die kostet doch sicher auch jede Menge Zeit. Schmeiß die mal raus.

Gruß, Udo

Grüß Dich Udo,

ich habe alle Serial.print... weggenommen. Wie gesagt, bis zwei Motoren läuft alles prima. Sobald ich alle Motoren laufen lasse, dann dauert es länger. Das sollte aber nicht der Fall sein. Auf dem Bord sollen einfach 7 Ausgänge an oder aus sein. Dafür sind die Arrays da, wenn ich an der Position x eine Null habe, dann soll Arduino für genau 100 ms an dem Port keinen Strom liefern, bei 1 umgekehrt. Dann soll er die Stelle x+1 in der Array nachschauen, usw.

Irgendwie komm ich nicht weiter, :'(. Lieben Gruß, Kadri

int nextStep(int currentPointer){
  
  long currTime = millis();
  
  while((millis() - currTime) < 10000){ // stop  if bigger than 10 s
    
    if (millis()% 100 ==0) { // for every "schritt" seconds : here the duration of a period ex. 100 ms
      ++currentPointer;
      Serial.println(millis());
      return currentPointer;
    } // ENDE if()
  } // ENDE while()
  
} // ENDE nextStep()

Ich glaube da passieren Dinge, die nicht sollten.
Denk dir den Teil mal genau durch. “”" 10 sekunden lang soll er, immer wenn millis() ne doppelnull (%100) liefert, “currentPointer” erhöhen “”"???
Ich glaube nicht!!

Ich glaube, schreib das alles nochmal :slight_smile:
Aus ner gelassenen Ruhe heraus. Das sieht böse gewachsen aus vom Code. Du weißt jetzt was es genau tun soll. Und das würde ich einfach mal vom grund neu schreiben. Ohne die verschachtelten Funktionsaufrufe.
Ich denke, einfacher als debuggen wäre das allemal :slight_smile:

Pseudocode

lastmillis=millis()

for i=0 bis "Anzahl der Zustände":
{
solange millis()-lastmillis<100 nix machen, und dann
setze motor1pin auf MOTORdaten1[i]
...
setze Motor12pin auf Motordaten12[i]
lastmillis=millis(); //damit wir gleich auch die richtige Zeit warten
}

Davor noch deinen Lampenkram und die Motordaten definieren.

Mein Tipp: einstelliger Millisekundenbereich… Also ruhig nach jedem Zustand noch die millis über die Serielle rausposaunen zum freuen :slight_smile:

Klasse Pumbaa,
Du hast mich auf eine sehr gute Idee gebracht.
Ich hoffe es klappt. Werde dann hier berichten.
Eigentlich kann ich ja auch die Arrays verkürzen, indem ich gar keinen Vergleich mache, sondern sofort HIGH oder LOW, statt 0/1 in der Array schreiben kann!
Dankeschön und liebe Grüße,
Kadri :slight_smile:

denk auch noch drüber nach, ob du die "Zustandsdaten" in nen byte (oder 2) packst. Dann kannste 8 (bei einem byte) Motoren direkt an/aus machen. Guck mal in der referenz nach "portmanipulation" nach. (guck mal Udos post..)

HIGH ist genau gleich 1, LOW ist 0. Hilft also nix.

die arduinosoftware ersetzt alle HIGH/LOWS darein, bevor er die auf den Arduino schiebt. Also hilfts nich

Aber du kannst digitalWrite(pin,1) genauso schreiben :)

Hi Pumbaa, wow, ich wusste das nicht. Ich brauch ja gar nicht nachzuprüfen, ob an Stelle array eine null oder eine eins ist, ich kann gleich digitalWrite(motor[motorNr], array*) schreiben. Cool. Danke vielmals. Ihr seit sehr hilfsbereit Leute. Danke* Cheers, Kadri

Nun guck dir endlich Portmanipulation an, und sei danach total glücklich!!

Dann kommste ohne deine Konstrukte aus!

In etwa so:

byte =zustand[]={B10010101,B11101011,B10010100,....}
for (i=0;i<laenge_des_arrays;i++)
{  PORTB=zustand[i];
   delay(100);
}

Du kannst den Port direkt mit den Zuständen beschreiben! Wenn an dem Port die 8 Motoren hängen, bekommen die ihren “Zustandsbefehl”!

bin schon dabei ;)