Blinken nach vorgegebenen Hz, mit Funktionen.

Da die Hardwarebastelei aufgrung von fehlendem Material gerade ruht, habe ich versucht mit Funktionen zu arbeiten. Mann kann ja üben solange. Meine Idee war es, eine Funktion zu machen, die mir eine vorgegebene Frequenz in ms für Puls und Pause umwandelt. Dann eine eigene Funktion, die diese ms in einen Blinker umwandelt, also x ms an, x ms aus. Im Loop dann alles Funktionen passend aufrufen. Irgendwo ist aber der Hund drin, die LED blinkt gar nicht. Sieht irgendjemand den Fehler? Ich verstehe gerade nicht was ich falsch mache.

/////////////////// GLOBALE VARIABLEN //////////////////////
int LED = 13;
unsigned long PreviousMillis = 0;
int Zeit = 0;
boolean lamp = false;

////// Funktion um Hertz in ms für den Blinker umzurechnen //////
int ms(int Hz){
  int v = 1000 / Hz / 2;
  return v;
}
////////// Funktion die nach vorgegebenen ms pulst ///////////
int blinkms(int Millis){
  unsigned long CurrentMillis = millis();
  if(CurrentMillis - PreviousMillis >= Millis) {
    // save the last time you blinked the LED
    PreviousMillis = CurrentMillis;   
    if (lamp == true)
      lamp = false;
    else
      lamp = true;
    return lamp;
  }
}

////////////////////// SETUP ////////////////////////////////
void setup()
{
  pinMode(LED, OUTPUT);
}

////////////////////// Hauptprogramm ////////////////////////
void loop()
{
  ///// Wandle Hertz in Ms
  int Zeit = ms(1);
  ///// Blinke erhaltene ms
  boolean Lampe = blinkms(Zeit);
  if (Lampe == true)
    digitalWrite(LED, HIGH);
  else
    digitalWrite(LED, LOW);
}

Danke schonmal.

Sven

Hast du mal versucht, den Serial.print zum debuggen zu nutzen? Ich änder dir mal eben das Programm passend ab. PreviousMillis hat nichts in den Globalen Variablen zu suchen, wenn du es nur einmal brauchst. Geht zwar, ist aber bisschen unschön.

/////////////////// GLOBALE VARIABLEN //////////////////////
int LED = 13;

////////// Funktion die nach vorgegebenen ms pulst ///////////
void blinkms(int Millis){
  static unsigned long PreviousMillis;
  unsigned long CurrentMillis = millis();

  if(CurrentMillis - PreviousMillis >= Millis) {
    // save the last time you blinked the LED
    PreviousMillis = CurrentMillis;
    int ledStatus = digitalRead(LED);
    if(ledStatus == HIGH)   digitalWrite(LED, LOW);
    else digitalWrite(LED, HIGH);

    Serial.print("LED Status : ");
    Serial.print(ledStatus);
    Serial.print("(");
    Serial.print(millis());
    Serial.println(")");      
  }
}

////////////////////// SETUP ////////////////////////////////
void setup()
{
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
  Serial.println("Hallo");
}

////////////////////// Hauptprogramm ////////////////////////
void loop()
{
  ///// Wandle Hertz in Ms
  float Zeit = ms(1);
  ///// Blinke erhaltene ms
  blinkms(Zeit);
}

////// Funktion um Hertz in ms für den Blinker umzurechnen //////
float ms(float Hz){
  float v = 1000 / Hz / 2;
  return v;
}

Mit dem Serial.println bin ich schon dran. Wenn der dauerhaft schreibt, dann blinkt auch die Led 13 im Halbsekundentakt, aber nur ganz kurz immer, bei der Zeit kommt noch das richtige raus, bei der boolean kommt immer eine zahl zwischen 255 und 0, da werde ich was bauen, dass true oder false als Text kommen.

sschultewolter:
PreviousMillis hat nichts in den Globalen Variablen zu suchen, wenn du es nur einmal brauchst.

Muss aber global oder static sein...
Also in diesem Sinne ist das schon ok.

Das Progamm sieht ansonsten ok aus.
Ich denke das Problem ist die globale "Zeit" variable. Die muss weg, da anonsten ein "Division durch 0" Fehler auftreten kann.

1.00 hz -> 500.00 ms
10.00 hz -> 50.00 ms
11.00 hz -> 45.45 ms
25.00 hz -> 20.00 ms
100.00 hz -> 5.00 ms
50.00 hz -> 10.00 ms

Sollte so funktionieren! Serial.print kannste dann auch wieder rausnehmen :wink:
Nochmal ein kleines Update gemacht. Serial.print meldet sich nur, wenn sich der Wert ändert.

int LED = 13;
float wert;


void setup()
{
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
  wert = 1.0;
}


void loop()
{
  // Änderung der Frequzen (hz) im Serial Monitor.
  if(Serial.available()) wert = Serial.parseInt();
  
  blink(wert); // wert = float hz
}


float blink(float hz) {
  static unsigned long vorherigeZeit;
  unsigned long aktuelleZeit = millis();
  float ms = 1000 / hz / 2;
  static float tempMs;
  
  // Vergleich ob sich der Wert ms geändert hat aufgrund einer neuen Frequenz (hz). 
  // Wenn ja, neuen Wert ausgeben, ansonsten nichts.
  if(ms != tempMs) {
    Serial.print(hz);
    Serial.print(" hz -> ");
    Serial.print(ms);
    Serial.println(" ms");
    tempMs = ms;
  }

  if(aktuelleZeit - vorherigeZeit >= ms) {
    int ledStatus = digitalRead(LED);
    if(ledStatus == HIGH) digitalWrite(LED, LOW);
    else digitalWrite(LED, HIGH);
    vorherigeZeit = aktuelleZeit;
  }
}

Stimmt so funktioniert es. Und es sind echt ein paar schöne Sachen drinn, die ich so noch nicht kannte. Wie zum Beispiel den Zustand des Ausganges mit digtalread abzufragen. Geht beim fertigen Projekt dann zwar so nicht für mich, die LED 13 ist gerade nur zum testen da. Auch die Hz in float anzulegen, damit auch 2,38Hz blinken können ist schön. Ich werde mir das jetzt alles nochmal genau zerdrösseln, was Du da gemacht hast. Danke. Verstehen was bei mir genau fehlschlug tu ich leider immer noch nicht, aber das werde ich mir morgen früh, wenn ich ausgeschlafen bin nochmal anschauen.

Hab es etwas kommentiert. Sollte eigentlich dann nicht so schwer zu verstehen sein :wink:
Ansonsten fragen.

Eine Sache war z.B. dass deine Methode einen int als return Wert hat, du das aber als boolean verwendest. Das ist zwar theoretisch zueinander kompatibel, aber man kann es auch gleich richtig machen.

Dann gibt die Methode nur true zurück wenn die Sekunde vergangen ist. Ansonsten fehlt im anderen Zweig der return-Wert. Da kann es sein, dass default-mäßig 0 zurückgegeben wird, aber das wäre funktional immer noch falsch. Die LED blinkt nur einmal kurz auf (was man gar nicht sieht) und ist für den Rest des Intervalls Low.

Im anderen Programm kann man kann das auch übrigens auch in einer Zeile schreiben: digitalWrite(LED, !digitalRead(LED));

Auch an Gutem gibts was zu Meckern:
blink sollte schon als

[b]void[/b] blink (float f);

definiert sein, wenn es keinen Wert zurückliefert.

Und wo ich schonmal dran bin:
Ich fande es schöner, wenn es

[b]boolean[/b] blink (float f);

wäre, dann könnest du es in loop als

digitalWrite(LED, blink(2.5));

verwenden. ( Echte Funktionen machen nur was aus ihren Parametern, sagen Puristen. )

boolean blink(float hz) {
  static boolean Zustand;
  static unsigned long vorherigeZeit;
  if ( hz <= 0. ) hz = 0.1;  // Fehlerschutz 
  unsigned long aktuelleZeit = millis();
  unsigned long ms = (unsigned long)(1000. / hz / 2);
  if(aktuelleZeit - vorherigeZeit >= ms)
  {
    Zustand = ! Zustand;
    vorherigeZeit = aktuelleZeit;
    // Serial.print(hz); (" Hz --> ms : ");
    // Serial.print(ms);
    // Serial.print(" "); Serial.println(Zustand);
  }
  return Zustand;
}

Jurs ( oder Sven, wenn er richtig c++ üben will ) kann da noch eine class Blink draus machen, damit man mehrere davon unabhängig blinken lassen kann :wink:

michael_x:
Auch an Gutem gibts was zu Meckern:
blink sollte schon als

[b]void[/b] blink (float f);

definiert sein, wenn es keinen Wert zurückliefert.

Es sollte eigentlich ein void sein :wink: Hatte das Programm mehr oder weniger auseinander gezogen und dabei ist das unbemerkt stehen geblieben :wink:

An digitalWrite(LED, blink(2.5)); habe ich nicht mal so schnell gedacht. Defentiv eine gute Lösung :wink:

An dieser Stelle ist der Compiler wirklich nicht schön (oder die Flags sind schlecht gesetzt). In Visual C++ bekommt man einen Fehler wenn der return-Wert fehlt und eine Warnung wenn nicht alle Zweige was zurückgeben.

Wahnsinn, wenn diese Ideen jetzt in meinem Hirn bleiben, habe ich heute echt einiges gelernt. Wirklich allen die sich beteiligt haben ein dickes Danke! Ich kann bisher keine Hochsprache, und da kann ich echt noch viel lernen. Danke sschultewolter für die Mühe die Du Dir gemacht hast. Danke Serenifly für die Hinweise auf meine Fehler, jetzt verstehe ich warum es nicht ging, und danke michael_x fürs weiter verbesser, das boolean blink kann ich bestimmt genau so brauchen. Und auch danke an int2str, static kannte ich für Variablen auch noch nicht. Mogen sitze ich vier Stunden im Zug, da wird an dem Ding weiter gemacht, gleich mit all Euren Hinweisen.

Sowas ähnliches habe ich schon mal hier Flexible Sweep | Blinkenlight breitgetreten. Anders als die Lösungen mit den Millisekunden bildet meine Lösung die gewünschte Frequenz so exakt wie möglich ab. Allerdings war in meinem Fall der Output eine Art "Rampe" bzw. wenn man nur auf eine LED schaut ein assymetrisches Rechteckt. Wenn Du symmetrischen Output brauchst müsstest Du "advance_phase()" entsprechend anpassen.

Danke Udo. In Deinem Blog habe ich übrigens schon öfters interessante Dinge gefunden. Deine Lösung wäre für meine Fälle aber übertrieben. Ich muss visuell Frequenzen zwischen 14 und 3 Hz darstellen. Am Ende des Tages muss das ganze Projekt dann auf einen ATTiny45 zusammen mit einer SPI Library. Da muss ich Platz sparen. Für spätere Projekte aber definitiv interessant, da ich mehrer POV Ideen im Kopf habe, die da auch bald mal heraus drängen werden.

Der Platzbedarf meiner Lösung ist nicht viel höher als der mit den Millis. Der Code ist nur länger. Der Compiler schiebt das schon zusammen. Den Parser kannst Du auch weglassen, dann wird es nochmal kürzer.

Das ist interessant. Ich habe bisher nur AWL programmiert, und da ist der geschriebene Text mehr oder weniger äquivalent zur Größe im Speicher.

In C hat die Souregröße nur sehr sehr wenig mit dem Speicherverbrauch zu tun. Du kannst Programme schreiben die Megabyte große Sourcen haben und Stunden kompilieren aber am Ende weniger als 1 kByte Speicher belegen (sieht man zugegeben in der Praxis so gut wie nie). Umgekehrt kannst Du Programme schreiben die nur sehr kleine Sourcen haben die dann aber aufgehen wie eine Hefekuchen und dann riesig Speicher verpulvern.

Weiterhin kann man je nach Libraries und/oder Compilereinstellungen viel Speicher oder weniger brauchen.

Die einzig wahre Methode um herauszufinden wieviel Speicher wirklich belegt wird ist zu compilieren und zu schauen was hinten rauskommt.