Programm läuft von alleine los - LED glimmt - geht es schneller ?

Kann mir jemand mal helfen und bitte über meinen Quellcode schauen,
ich bin total neu in der Materie und kenn mich noch kaum aus. Hab einen Arduino Mega 2560.

Manchmal Funktioniert es manchmal nicht. Aber nach einem reset/oder upload läuft es immer von alleine los.

Die Funktion des Programms sollte sein:
Nach betätigen eines Tasters und einer Wartezeit werden 4 Schritte durchlaufen die jeweils den Ausgang für eine bestimmt Zeit ein- oder Ausschalten.
Währenddessen sollen 3 Analogeingänge und ein Digitaler eingelesen werden und der Taster nicht betätigt werden können.

Probleme:

  • nach Aufspielen des Programms läuft es los ohne das ich auf den Taster drücke.
  • manchmal kann ich mit dem Taster starten, manchmal nicht.
  • wenn der Zyklus einmal durchlaufen ist glimmt die LED noch und schaltet nicht komplett ab. (Habs auch mit der Internen an PIN13 versucht, selbes Phänomen)

Zusatz Fragen:

  • warum hab ich eine steigende Analoge Spannung von 1-2V obwohl ich noch keine Spannung angelegt habe? oder ist genau das der Grund?
  • Die Programmlaufzeit ist laut dem Logfile ca 30ms, geht das nicht schneller?

Als Anlage hab ich mein Programm, den Aufbau und das Logfile hinzugefügt.

Wär schön wenn sich jemand findet der mir helfen könnte.
gruß Jens

Jens_Analogwerte_lesen.ino (2.21 KB)

LOG.txt (34.6 KB)

Läuft das Programm denn ansonsten normal ab? Wäre zur Sicherheit noch interessant, auch mal den "Zustand" zu loggen und natürlich den Ausgang an dem die LED angeschlossen ist.

Dass du an den analogen Eingängen Blödsinn misst, wenn nichts angeschlossen ist, ist normal.

Ein vorprogrammiertes Problem wirst du früher oder später mit der Zeitsteuerung haben. Integer gehen nur bis 32'768, dann überlaufen sie, das sind in ms also ca. 30 Sekunden. Mit unsigned long hast du ein bisschen mehr Luft.

Du hast break; in den einzelnen case-Abfragen vergessen. Bei dir werden alle "cases" schon beim ersten Programmdurchlauf abgearbeitet.

 switch (Zustand)
  {
  case 0:  //waiting for start
    digitalWrite(FGL1, LOW); 
    if (WertTaster == HIGH)
    {
      Starttime=millis(); 
      Zustand = 1;
    }
    break;
  case 1:  //Startdelay
    if ((millis()-Starttime)>=Startdelay)
    {
      digitalWrite(FGL1, HIGH); 
      Zustand = 2; 
    }  
    break;
...

Ein freier/unbeschalteter Analogeingang wir immer irgendetwas anzeigen, weil das Potential nicht definiert ist, d.h. Es wird zwischen 0 und 5V liegen.

Die Variable Starttime muss unsigned long sein, wie t3d_et schon erwähnt hat.

Grüße,
J3RE

Ok Danke, hat prima funktioniert.

Die breaks hab ich total vergessen^^ aber nachdem sie gesetzt wurden hat auch das glimmen der led aufgehört und es läuft auch nicht mehr von selber an.
Das mit den Int, unsigned Long wusst ich nicht.

was mich noch interessieren würde:

  • die Zykluszeit der Messungen sind laut logfile immer noch ca 40ms, kann man das Programm noch irgendwie schneller machen? was schluckt soviel Zeit?

Baudrate der seriellen Ausgabe auf Maximum bringt noch was.

Was ist denn maximal möglich ?

es wird doch vorgeschlagen mit 9600 zu arbeiten, kann da nichts kaputt gehen?

hi,

nein, mit der baudrate kannst Du nichts kaputtmachen, da wird kein strom zu hoch oder so...

es kann nur passieren, daß bei zu langem kabel oder schlechtem kabel die kommunikation nicht mehr zuverlässig ist.

gruß stefan

ok, hab sie jetzt mal auf 115200 gesetzt, da gehts schon anders ab.
Zykluszeit 4ms.
Der Serialmonitor in dem Arduino Programm lässt sich nicht höher einstellen.

Geht noch mehr? wenn ja wie?
Sehr geil wär es wenn eine kontinuierliche Zykluszeit von 100ns oder so machbar wäre.

100ns sind 10-7 Sekunden oder 0,0000001 Sekunden. Der Takt deines Arduinos ist maximal 16Mhz also 106 Hertz oder 16.000.000 Zyklen pro sekunde... also 62.5ns pro Zyklus. In den 100ns Bereich wirst du fast unmöglich vordringen, da das Programm auch noch Laufzeit benötigt.

hm ok, klingt eindeutig.
Aber ist es nicht trotzdem irgendwie möglich das Programm schneller zu machen oder noch abzuspecken?

Ich hab auch was von nem prescaler gelesen, habs aber nicht ganz geschnallt, was macht dieser?

Wärs möglich die Werte permanent über nen Timer interrupt auszulesen? Das Programm kann ja "langsamer" laufen

welchen Teil genau willst du denn schneller haben?

Man kann Programmteile, die nur seltener gebraucht werden in Zeitscheiben auslagern, dann bleibt die eigentliche loop() schlanker und schneller.

if (millis()> Zeitscheibe_1sek )    // Zeitscheibe wird alle 1000ms aufgerufen
  { Zeitscheibe_1sek  = millis()+1000;  
   // mach was, was nur jede Sekunde erledigt werden muß
} // end Zeitscheibe_1sek

Allerdings wird die Ausführung der Loop bei der Zeitscheibe jedesmal kurz unterbrochen.

die Zeile float VoltageA0 = WertA0*(5.0/1023.0); könntest du ändern in float VoltageA0 = WertA0 / 204.6

das spart eine floating point multiplikation.

Um zu sehen, wie schnell dein Code wirklich ist, ohne Serial.print, kannst du an Anfang der loop() einen befehl setzen Timestamp0 = micros() dann, vor Beginn der Ausgabe Timestamp1 = micros() und in der Ausgabe dann Serial.print(Timestamp1 - Timestamp0).

Gunther

Merci gunther für den Tip, hab ich gleich mal gemacht.
Wenn ich nur die Loopschleife messe hab ich 8-16 µs.
Kommt aber die Serielle Ausgabe hinzu sind es 2000-4000µs je Zyklus.

d.h. die Ausgabe bremst aus.

Ist es denn nicht möglich mit einem serial.print Befehl mehrere Variablen zu schicken. Evtl als Array oder so?
Oder kann ich die Baudrate noch mehr erhöhen als 115200? Wie kann ich es dann lesen, der Serial Monitor kann ja nur bis 115200.

Gruß Jens

@Gunther:

das spart eine floating point multiplikation.

Mein (und dein) avr-gcc Compiler ist so schlau, dass er das selber merkt:

void setup() {
float VoltageA0 = analogRead(A0)*(5.0/1023.0);
//float VoltageA0 = analogRead(A0) / 204.6;
Serial.begin(115200);
Serial.print(VoltageA0);
}
void loop() {}

Übersetzen für einen Uno liefert:
Binäre Sketchgröße: 3.932 Bytes (von einem Maximum von 32.256 Bytes)
Wenn ich den Kommentar in die obere Zeile setze, kommt der gleiche Sketch von 3.932 Bytes raus.
Aber ganz auf float verzichten bringt was...

Es gibt andere Terminal-Programme, die evtl. auf mehr als 115200 eingestellt werden können.
Ein 16MHz Arduino sollte auch noch 230400, 250000 oder noch mehr können, schau dir mal diesen Thread an.

Aber, als Mensch mitlesen geht etwa bis 9600, darüber brauchst du auf der PC-Seite sowieso ein Empfangsprogramm, dass die vielen Daten weiterverarbeitet (wie z.B. das erwähnte "Oszilloskop")

Mitlesen brauch ich es ja nicht, meine Messung startet und wird einfach mitgeloggt.

Das Ergebniss lass ich mir nacher irgendwo Grafisch darstellen zum Auswerten.
Aber es wär halt schöner, wenn es noch nen ticken schneller gienge als derzeit ~3ms/Zyklus. kontinuierlich (Beziehungsweise für einen variablen Messbereich zwischen 1 und 30 Sekunden).

Wenn es eh kein Mensch direkt lesen soll:

Eine Zahl im Bereich 0 .. 256255 kann man mit 4 Buchstaben, z.B. "255," oder binär als 1 Zeichen übertragen.
Dadurch kriegst du statt 3 ms z.B. 1 ms hin.

Mit anderen Daten bringt Binär statt Text nicht ganz so große, aber auch deutliche Verbesserungen.

Edit: 0...256 wären 257 verschiedene Werte, also einer zuviel. Sorry

michael_x:
Eine Zahl im Bereich 0 .. 256 kann man mit 4 Buchstaben oder binär als 1 Zeichen übertragen.

Da hast du was verwechselt oder!?
0-256 0-255 entspricht ja einem Byte, also 8 Bit, dafür kann man char oder uint8_t (unsigned Integer mit 8 statt 16 Bit) verwenden.

Grüße,
J3RE

DrThunderhell:
Ist es denn nicht möglich mit einem serial.print Befehl mehrere Variablen zu schicken. Evtl als Array oder so?

du kannst natürlich eine char-Array füllen und das dann auf einmal ausgeben.

Vor allem würde ich die ganzen Texte "VoltageA0" usw. nur einmal an Anfang ausgeben, als Tabellenüberschrift, und später nur noch die Zahlen.
wenn du dann anstelle der leerzeichen ein char(9) (tab) ausgiebst, dann lässt sich das leichter einlesen, später.

z.B. so

String dataString = "";

      dataString += "VoltageA0"; 
      dataString += (char(9));
      dataString += "VoltageA1"; 	
      dataString += (char(9));
      dataString += "VoltageA2";	
      Serial.println(dataString);

Schonmal an SD-Karten gedacht?

Ein Modul für SD Karten steht mir hier nicht zur verfügung.

Das mit dem Array hört sich gut an, funktioniert aber leider nicht so richtig.
+= als operator kennt er nicht, außerdem hab ich ja verschiedene Variablentypen.

so war es vorher:
Serial.print(millis()-Starttime);
Serial.print("ms ");
Serial.print(analogRead(A0)*0.0048875855327468);
Serial.print("V ");
Serial.print(analogRead(A1)*0.0048875855327468);
Serial.print("V ");
Serial.print(analogRead(A2)*0.0048875855327468);
Serial.print("V ");

kann man denn den Typ konvertieren oder so, das alles Char oder String ist?

Oh, das habe ich übersehen, das ich hier ja strings verkettet habe!

ich habe es gerade mal nachgemessen: um 3 Floatwerte auf SD zu schreiben, brauche ich 18ms. ist also auch nicht schneller.
Allerdings soll es Optimierungen geben um die SD-Lib schneller zu machen.
(ein SD-Card reader kostet 2€ beim Chinesen

Zum konvertieren: hier fällt mir nur "sprintf" ein, ich weiß allerdings nicht, wie schnell das ist.

und noch was: wenn du die daten sowieso später am PC weiterverarbeiten willst, dann schreibe doch nur die gemessenen AD-Werte als integer.
Umrechnen kann der PC besser...

ah ja funktioniert doch.

wenn ich die Voltage nicht als Float übernehme (gleich ausgerechnet in richtige Volt) sondern nur den eingelesenen Int Wert

unsigned int Zeit=(millis()-Starttime);
int Voltage1= (analogRead(A0));
int Voltage2= (analogRead(A1));
int Voltage3= (analogRead(A2));
int Eingang= (digitalRead(Taster));
int LED=(digitalRead(Ausgang));

String dataString = "";
dataString += Zeit;
dataString +="ms ";
dataString +=Voltage1;
dataString +=" ";
dataString += Voltage2;
dataString +=" ";
dataString += Voltage3;
dataString +=" ";
dataString += Eingang;
dataString +="/";
dataString += Zustand;
dataString +="/";
dataString += LED;
dataString +=" - ";
Serial.print(dataString);

Programmzykluszeit ~2,2ms