ich verstehe zwar mittlerweile die blink_without_delay-Logik, stehe nun aber vor einer komplexeren LED-Schalt-Aufgabe, bei der ich leider nicht weiterkomme.
Folgendes Problem:
Eine LED soll innerhalb der ersten 250ms einer Sekunde bis zu fünf Mal aufleuchten. Es soll also auch möglich sein die LED einmal, zweimal, dreimal und viermal über diese 250ms für jeweils 30ms pro "Aufleuchten" aufleuchten zu lassen.
Die Leuchtdauer pro Aufleuchten ist mit 30ms festgelegt und die Abstände zwischen den einzelnen Einschaltvorgängen müssen identisch sein.
Ein Beispiel (y-Achse = Zeit):
Die LED soll in diesem Fall dreimal während der ersten 250ms für jeweils 30ms aufleuchten:
0ms LED ein
30ms LED aus
125ms LED an
155 ms LED aus
250ms LED ein
280ms LED aus
Nach 1000ms soll sich das Ganze dann wiederholen..
Natürlich soll nebenher noch anderer Code abgearbeitet werden können (daher möchte ich mit millis() arbeiten) und Timer2, oder wie das heisst scheidet aus.
Bin leider scheinbar zu doof eine entspr. Logik zu entwickeln.
Vielleicht mag mich ja einer von Euch auf die richtige Fährte führen.
sschultewolters Code ist zwar Pseudocode, was vollkommen in Ordnung ist, berücksichtigt jedoch nicht die von mir geforderte Angabe mit den 250ms.
PhunThoms Code kompiliert bei mir zwar, führt aber zu Fehlverhalten der LED.
Hier mal mein Code, welcher leider ebenfalls (mir unerklärliche) Fehlverhalten der LED aufweist:
/************************************************** LED-Parameter */
const byte LED = 13; // Initialisierung der LED
byte LED_st = 11; // Blinkanzahl der LED
int ms_dur = 1000;
unsigned long blink_start_time = 0;
const byte short_LED_dur = 30; // Definiert die Leuchtdauer der LED
unsigned long LED_short_on_time = 0; // Definiert den Zeitpunkt, wann die LEDs zuletzt eingeschaltet wurde
const int LED_quarter_dur = 250; // Definiert die 250ms, in denen die LED blinken soll
void setup()
{
pinMode(LED, OUTPUT);
}
void loop()
{
if(millis() - blink_start_time >= ms_dur) // Jede Sekunde..
{
digitalWrite(LED,HIGH);
blink_start_time = millis();
}
if(millis() - blink_start_time <= LED_quarter_dur) // So lange noch keine 250ms einer angebrochenen Sekunde vergangen sind,
{
if(millis() - LED_short_on_time >= (LED_quarter_dur / (LED_st - 10))) // wenn das Intervall noch nicht überschritten wurde
{
if(digitalRead(LED == LOW))
{
digitalWrite(LED ,HIGH);
}
LED_short_on_time = millis();
}
}
if(millis() - LED_short_on_time >= short_LED_dur)
{
if(digitalRead(LED == HIGH))
{
digitalWrite(LED ,LOW);
}
}
}
Die LED blinkt nicht wie von mir erwartet regelmäßig, sondern manchmal etwas kürzer, oder nur einmal anstatt zweimal.
Chris72622:
sschultewolters Code ist zwar Pseudocode, was vollkommen in Ordnung ist, berücksichtigt jedoch nicht die von mir geforderte Angabe mit den 250ms.
Der Code müsste funktionieren. Du musst natürlich die If-abfragen passend ergänzen. Es ist nur der Anfang.
mein Versuch (geht davon aus, dass mit einer Pause angefangen wird und mit einem Blink aufgehört wird, demnach gleich viele Pausen wie Blinks vorhanden sind):
boolean newPeriod = true;
boolean pause = true;
unsigned long startPeriod;
unsigned long startPause;
unsigned long startBlink;
void blinkSequence (int blinks) {
int pauseTime = (250 - (blinks * 30)) / blinks; // legt die Pausenzeit bei Anzahl blinks fest
if (newPeriod) {
startPeriod = millis();
startPause = millis();
newPeriod = false;
}
if (millis() - startPeriod < 251) { // nur in ersten 250ms wird geblinkt
if (millis() - startPause >= pauseTime && pause) {
digitalWrite(led, HIGH);
pause = false;
startBlink = millis();
}
if (millis() - startBlink >= 30 && !pause) {
digitalWrite(led, LOW);
pause = true;
startPause = millis();
}
}
else if (millis() - startPeriod >= 1000 {
newPeriod = true;
}
}
Edit: Wie bekommt man eigentlich so große Tab-Schritte in den Code?
Du hast vergessen, die Funktion in der Loop aufzurufen. So müssts sein:
E: und den LED Pin als Output zu definieren.
const byte led = 13; // Initialisierung der LED
boolean newPeriod = true;
boolean pause = true;
unsigned long startPeriod;
unsigned long startPause;
unsigned long startBlink;
void setup()
{
pinMode(led, OUTPUT);
}
void loop()
{
blinkSequence(3);
}
void blinkSequence (int blinks)
{
int pauseTime = (250 - (blinks * 30)) / blinks; // legt die Pausenzeit bei Anzahl blinks fest
if (newPeriod)
{
startPeriod = millis();
startPause = millis();
newPeriod = false;
}
if (millis() - startPeriod < 251)
{ // nur in ersten 250ms wird geblinkt
if (millis() - startPause >= pauseTime && pause)
{
digitalWrite(led, HIGH);
pause = false;
startBlink = millis();
}
if (millis() - startBlink >= 30 && !pause)
{
digitalWrite(led, LOW);
pause = true;
startPause = millis();
}
}
else if (millis() - startPeriod >= 1000)
{
newPeriod = true;
}
}
Die Tabschritte sind aus dem Texteditor kopiert, wohl deshalb etwas zu gross…
E2: die an die Funktion übergebene Zahl sollte übrigens für die Anzahl Blinks stehen. In diesem Fall sollte also die LED 3mal innerhalb der ersten 250ms blinken. Wenn du die Zahl durch 1,2,4 oder 5 ersetzt, sollte es entsprechend diese Anzahl blinken… probiers doch bitte auch aus, wenn der andere Code funktioniert, würde mich wundernehmen und um es selbst auszuprobieren, habe ich gar keine Möglichkeit, so genaue Messungen vor zu nehmen. Abweichungen können übrigens entstehen, da beim Geteiltrechnen Kommazahlen entstehen würden, die hier einfach konsequent abgerundet werden.
Falls es noch komplexer werden sollte macht es Sinn, da du deine Aufgaben dann einfach miteinander Verschachteln kannst ohne dass zuviele If Abfragen benötigt werden.
Der Code würde dann wie folgt aussehen:
#include <simpleThread.h>
#define _sT_cnt _sT_cnt_2
simpleThread_init(_sT_cnt);
simpleThread_new_timebased_dynamic (_sT_P1 , _sT_millis, 30, _sT_start , LED_Blink);
simpleThread_new_timebased_dynamic (_sT_P2 , _sT_millis, 1000, _sT_start , Start_LED_Blink);
uint8_t g_blink_cnt = 0;
uint8_t g_led_status = 0;
// Thread LED Blink
void simpleThread_setup(LED_Blink)
{
g_blink_cnt = 0;
}
void simpleThread_stable(LED_Blink)
{
//Led ausschalten
digitalWrite(13, LOW);
}
boolean simpleThread_loop(LED_Blink)
{
if(g_blink_cnt < 8) { //8*30 = 240 ms
// LED Blink
g_led_status = !g_led_status;
digitalWrite(13, g_led_status);
g_blink_cnt++;
} else {
// Thread wird sich selbst deaktivieren
simpleThread_stopStable(LED_Blink);
}
return 0;
}
// Thread Start Led Blink
void simpleThread_setup(Start_LED_Blink)
{}
boolean simpleThread_loop(Start_LED_Blink)
{
simpleThread_restart(LED_Blink);
return 0;
}
// Setup
void setup()
{
pinMode(13, OUTPUT);
/* Threads initialisieren */
simpleThread_initSetup(_sT_cnt);
}
void loop() {
/* calls the threads */
simpleThread_run(_sT_priority);
// hier der andere Code, besser noch in einem eigenen Thread
}
Falls noch mehr Zeiten bei dir hinzukommen sollten, führt auf dauer kein Weg dran vorbei eine Lib zu verwenden die diese oder eine Ähnliche Funktionalität bietet.
Jedoch muss ich zugeben, dass mich die Komplexität der SimpleThreads-Geschichte erschlagen hat.
Ich habe zwar noch nicht aufgegeben, kann aber das aktuelle Projekt nicht komplett umstricken.
Sollte es mittlerweile anfängerfreundlichere Beispiele/ Einstiegsmöglichkeiten geben, würde ich mich sehr über eine Nachricht (gerne auch per PM) freuen.
Ehrlich gesagt sehe ich mich zum jetzigen Zeitpunkt manchmal kurz davor, dass ich mich verstricke und es einen Riesenschlag tut. :~
Gruß Chris
Edit: Dein Code lässt die LED lediglich sekündlich ganz kurz aufblitzen. SimpleThread-Library ist installiert und es wird anstandslos kompiliert. :~
Beim Projekt "Leuchtturm-Karte" ging es damals darum, einen ganzen Schwung Leuchttürme mit jeweils eigenem Leuchtfeuer-Timing blinken zu lassen. Die Timings hier sind zwar kürzer, aber prinzipiell unterscheidet es sich nicht.
BTW: Dass der millis()-Timer "unrund" läuft, ist Dir hoffenlich bekannt?
Innerhalb von 1000 Millisekunden tickt die millis() Funktion nur 977 mal um eins vor und 23 mal gleich um zwei.
Falls es also bei den 30 Millisekunden drauf ankommt, diese möglichst exakt einzuhalten, würde ich das Timing in dem Fall lieber über die micros() Funktion statt über die millis() Funktion ausstoppen.
jurs:
BTW: Dass der millis()-Timer "unrund" läuft, ist Dir hoffenlich bekannt?
Nein.
1000 Dank. Dieses mir bisher fehlende Wissen hat mich in der Vergangenheit schätzungsweise 24h meines Lebens gekostet, da ich mich beim Fehler suchen im Kreis drehte. Schätzungsweise drei von ihnen habe ich heute verbraten.
1000 Dank. Dieses mir bisher fehlende Wissen hat mich in der Vergangenheit schätzungsweise 24h meines Lebens gekostet, da ich mich beim Fehler suchen im Kreis drehte.
Dann passt es ja, dass ich das mal gepostet habe.
Wenn Du mal sehen möchtest, wo und wie genau der millis() Zähler im Programm springt, kannst Du das mit diesem Sketch selbst austesten:
// Messung des unrunden Laufs der millis()-Funktion
// Test-Sketch by 'jurs' for German Arduino forum
void setup() {
Serial.begin(57600);
Serial.println();
Serial.println("Start");
}
void loop()
{
static unsigned long count;
static unsigned long lastMillis;
unsigned long thisMillis=millis();
int diff=thisMillis-lastMillis;
lastMillis=thisMillis;
if (diff>1) // millis() ist um mehr als 1 gesprungen
{
count++;
Serial.print(count);
Serial.print('\t');
Serial.print(thisMillis);
Serial.print('\t');
Serial.println(diff);
}
if (millis()>10240) while(1); // Stop nach 10 Sekunden
}
Der Sketch gibt 10 Sekunden lang immer dann eine Zeile mit drei Werten aus, wenn der Zähler um mehr als 1 springt. Die angezeigten Werte sind:
Anzahl der falschen Zählungen
Stand des millis() Zählers beim Auftreten der falschen Zählung
um wieviel tatsächlich weitergezählt wird (ist immer "2")
Offenbar zählt millis() intern in 1/1024 Sekunden Schritten, und weil damit nach 1000 Zählungen erst 1000/1024 Sekunden vergangen wären, mogelt die Funktion von Zeit zu Zeit zum Ausgleich eine Millisekunde extra drauf, damit es zeitlich wieder zusammenpasst und millis() wenigstens im zeitlichen Durchschnitt pro Sekunde um 1000 Millisekunden weiterzählt.
Falls Du Englisch kannst, Dann schau Dir einfach die Beisspiele in meinem Blog an. Da sind jede Menge Effekte die Blinken ohne in loop irgendwas zu tun.