Delay in millis() ändern

Dieser Thread stammt ursprünglich HIERHER, hat aber mit dem Ausgangsproblem nicht mehr viel zu tun, der Ordnung halber nun ein neuer.

Zwei Dinge, die man mir beigebracht hat sind sehr prägend hängen geblieben:

  1. Gelber Schnee ist baba!
  2. Finger weg von Delay

Und tatsächlich habe ich ein Problem mit einem Code, der mit Delay nicht funktionert, den ich aber einfach nicht in millis() umgeschreiben bekomme.

Könnt ihr mir da helfen?

DIESER Code funktioniert:

int R1 = 29;
int DimmPin1 = 4;    
int DimmWert1 = 90;  //einfach ein Wert zum ausprobieren. 
int EinschaltZeit1 = 0;

void setup()
{
  pinMode(R1, OUTPUT);    //Hier ist ein Relais für die Leuchte angesteuert
  digitalWrite(R1, HIGH); //Relais für Leuchte ein
  pinMode(DimmPin1, OUTPUT);
  attachInterrupt(0, zero_crosss_int, RISING);  // PIN 2 am Mega- Clone
}
void zero_crosss_int()  
{
  EinschaltZeit1 = (75*DimmWert1);    // For 60Hz =>65    
  delayMicroseconds(EinschaltZeit1);    // Wait till firing the TRIAC    
  digitalWrite(DimmPin1, HIGH);   // Fire the TRIAC
  delayMicroseconds(10);         // triac On propogation delay 
         // (for 60Hz use 8.33) Some Triacs need a longer period
  digitalWrite(DimmPin1, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
}
void loop()  
  {
    /* 
   Das rauf- und runter Dimmen wird erst später benötigt
   for (int i=50; i <= 95; i++){
    DimmWert1=i;
    delay(100); */
   }

Mein Versuch das in millis() (micros) umzuschreiben sieht so aus und funtioniert NICHT:

int R1 = 29;
int DimmPin1 = 4;    
int DimmWert1 = 90;  
int EinschaltZeit1 = 0;
bool DimmMerker1 = LOW;
unsigned long Dimmstart1 = 0;
unsigned long DimmPause1 = 0;

void setup()
{
  pinMode(R1, OUTPUT);
  digitalWrite(R1, HIGH); //Relais für Leuchte ein
  pinMode(DimmPin1 , OUTPUT);
  attachInterrupt(0, zero_crosss_int, RISING); 
}
  
void zero_crosss_int()  
{
  EinschaltZeit1 = (75*DimmWert1);    
  Dimmstart1 = micros();    
  if((micros()- Dimmstart1 >=  EinschaltZeit1) && DimmMerker1 == LOW)
    { digitalWrite(DimmPin1, HIGH);
      DimmMerker1 = HIGH;
      DimmPause1 = micros();
      }
  
  if(micros()- DimmPause1 >= 10 && DimmMerker1 == HIGH)
    { digitalWrite(DimmPin1, LOW);
      DimmMerker1 = LOW;}
}

void loop()  { 
 /*
  for (int i=50; i <= 95; i++){
    DimmWert1=i;
    delay(100);
   }*/
   }

Könnt ihr mir helfen, dieses Problem zu lösen?

und funtioniert NICHT

Eins der Probleme ist, dass delay(), micros() und auch millis() selber Interrupts benötigen.
bzw. wird deren Zeitbasis in einer ISR vorbereitet

Das kann/wird dann in deiner ISR versagen, da Interrupts üblicher Weise in ISRs gesperrt sind.

Auch müssen alle Variablen, welche von ISR und Hauptprogramm benötigt werden, volatile sein und atomar ausgelesen werden.

Das heißt, das Problem ist für mich unlösbar.

Oder...ich nehme einen zweiten Arduino, der nur für das Dimmproblem zuständig ist.
Und der kann dann so viel Delay benutzen wie er lustig ist... :o

Das stimmt nicht so ganz. Man kann die Zeiten in einer ISR schon abfragen. Aber sie werden nicht inkrementiert. Und auch das muss man eingrenzen:

unsigned long micros() {
	unsigned long m;
	
	cli();
	m = timer0_overflow_count;
	t = TCNT0;
	
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

Das ist gekürzt auf das relevante. Wie man sieht fließt der aktuelle Zählerstand immer ein. Aber der Überlauf (der ca. alle 1ms kommt) wird bei abgeschalteten Interrupts nicht höher

Das ist schonmal falsch:

 Dimmstart1 = micros();    
  if(micros()- Dimmstart1 >=  EinschaltZeit1 && DimmMerker1 == LOW)

Du machst da praktisch micros() - micros()

Außerdem solltest du micros() nicht immer wieder neu auslesen. Funktionsaufrufe kosten viel Zeit. Wobei das hier nicht so schlimm ist, da du dich beim Dimmen im Millisekunden Bereich bewegst

Und schon die nächste Frage:
Wenn ich einen Arduino Mega (Clon) und einen Nano (Clon) miteinander kommunizieren lassen will, und nur EINEN Wert übertregen möchte, was wäre da die einfachste Vorgehensweise für jemanden, der davon (noch) keine Ahnung hat?

  1. Ein Signal über PWM ausgeben und analog einlesen?
  2. Mehr über die serielle (oder was auch immer) Kommunikation zwischen zwei Arduinos lernen?

Hi,

was soll denn bei einer Verknüpfung INT und BOOL ohne Klammern rauskommen ?

EinschaltZeit1 && DimmMerker1 == LOW)

I2C oder Seriell
Bei der seriellen Schnittstelle hat der Mega mehrere. Auf dem Nano sollte man Software Serial verwenden und nicht die Hardware-Schnittstelle.

olmuk:
Hi,

was soll denn bei einer Verknüpfung INT und BOOL ohne Klammern rauskommen ?

EinschaltZeit1 && DimmMerker1 == LOW)

Da war es wieder, das Brett vor dem Kopf.
Fehler habe ich (denke ich) korrigiert.

Danke!

Mal darüber nachgedacht. Das geht sowieso in der ISR so nicht.

Ja, man kann micros() abfragen. Aber du bist nur einmal beim Nulldurchgang in der ISR. Du kannst also nicht rechzeitig abfragen ob die Zeit abgelaufen ist.
Du musst in der ISR ein Flag setzen und darauf in loop() abfragen. Und dann ständig abfragen ob die Zeit vorbei ist. Außerhalb könnte man dann auch delay() verwenden. Oder es nicht-blockierend machen was natürlich besser ist, wenn du noch andere Dinge wie Kommunikation erledigen willst.

Auch mit delay() muss der Rest des Codes nicht-blockierend sein, damit man das Flag in der Zeit zuverlässig abfrägt

Tiff48317:
Das heißt, das Problem ist für mich unlösbar.

Oder...ich nehme einen zweiten Arduino, der nur für das Dimmproblem zuständig ist.
Und der kann dann so viel Delay benutzen wie er lustig ist... :o

Och...
Das könnte auch für dich lösbar sein....
Wenn du dich in die ISR Thematik einliest.
Oder auch das Datenblatt des ATMega2560 auswendig lernst.
Denn da steht viel über die eingebauten Timer und ihre Möglichkeiten.

Das wäre natürlich am zuverlässigsten. Die ISR wirft einen Hardware Timer an und der hat seine eigene ISR

Aber es geht auch per Hand wenn man es richtig macht. Wie gesagt kann man auch sowas machen:

volatile bool trigger = false;

void isr()
{
   trigger = true;
}

void loop()
{
   if(trigger)
   {
      trigger = false;
      ...
   }
}

Man muss aber trotzdem etwas sorgfältig sein damit man die if-Abfrage nicht verpasst

Serenifly:
Mal darüber nachgedacht. Das geht sowieso in der ISR so nicht.

Ja, man kann micros() abfragen. Aber du bist nur einmal beim Nulldurchgang in der ISR. Du kannst also nicht rechzeitig abfragen ob die Zeit abgelaufen ist.
Du musst in der ISR ein Flag setzen und darauf in loop() abfragen. Und dann ständig abfragen ob die Zeit vorbei ist. Außerhalb könnte man dann auch delay() verwenden. Oder es nicht-blockierend machen was natürlich besser ist, wenn du noch andere Dinge wie Kommunikation erledigen willst.

Auch mit delay() muss der Rest des Codes nicht-blockierend sein, damit man das Flag in der Zeit zuverlässig abfrägt

Nicht blockierend sollte es sein.
Es ist noch einges anderes dabei (z.B. IR Fernbedienung und später Blauzahn), was auch dann funktionieren soll.

Wenn Du das mit einem Prozessor sauber machen willst, wirst Du nicht umhin kommen, dich mit den Timern zu beschäftigen. Man muss das Datenblatt ja nicht gleich auswendig lernen. Aber man sollte wissen, was die Timer können, und wo steht, wie man das nutzt.
Du brauchst dann 2 Interrupts:
im zero_crosss_int erkennst Du den Nulldurchgang, berechnest, wann Du den Triac zünden willst, und ziehst den Timer mit dieser Zeit auf. Wenn dann der Timer seine ISR feuert, wird da nur noch der Triac gezündet - fertig.

Der loop ist dann völlig frei für andere Aufgaben und gibt letztendlich nur die Zeit zwischen Nulldurchgang und Zünden vor.

Dann werde ich mich mal mit timern beschäftigen. Uff...

Bis dahin (auch auf die Gefahr, völlig falsch zu liegen) ginge es vielleicht so?

int R1 = 29;
int DimmPin1 = 4;    
int DimmWert1 = 50;  //einfach ein Wert zum ausprobieren. 
int EinschaltZeit1 = 0;
volatile bool trigger = false;

void setup()
{
 pinMode(R1, OUTPUT);    //Hier ist ein Relais für die Leuchte angesteuert
 digitalWrite(R1, HIGH); //Relais für Leuchte ein
 pinMode(DimmPin1, OUTPUT);
 attachInterrupt(0, zero_crosss_int, RISING);  // PIN 2 am Mega- Clone
}
void zero_crosss_int() { IAC }

void isr(){ trigger = true;}

void loop()
{
  if(trigger)
  {
     trigger = false;
     EinschaltZeit1 = (75*DimmWert1);    // For 60Hz =>65    
     delayMicroseconds(EinschaltZeit1);    // Wait till firing the TRIAC    
     digitalWrite(DimmPin1, HIGH);   // Fire the TRIAC
     delayMicroseconds(10);         // triac On propogation delay 
     digitalWrite(DimmPin1, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TR
  }
}

Es gibt Denke ich auch fertigen Code dazu. Wobei man das schon verstehen muss, da man es eventuell anpassen muss wenn vielleicht andere Bibliotheken noch einen Timer verwenden wollen

wenn vielleicht andere Bibliotheken noch einen Timer verwenden wollen

Timer0 ist belegt, für den ganzen Arduino Zeitkram.

Allerdings sind dessen Compare Interrupts eher nie in Benutzung. Also frei.
Und wenn man die PWM Funktionen des Timer0 auch nicht nutzt, dann kann man sich da schon was draus basteln.

Denke ich mal...

Nachteil beim Timer 0 ist die Durchlaufzeit von ca. 1ms, an der man nichts ändern darf, da sonst die millis nicht mehr stimmen. Da muss man dann die Zünd-ISR mehrfach auslösen und mitzählen bis es 'feuert', denn Zeiten über 1ms wird man wohl brauchen.

Da muss man dann die Zünd-ISR mehrfach auslösen und mitzählen bis es 'feuert', denn Zeiten über 1ms wird man wohl brauchen.

Wohl wahr!

Aber da eine Halbwelle nur 10ms hat, muss man auch nur maximal bis 9 zählen.
8) Das soll doch ein AVR wohl hin bekommen. 8)

Immerhin wirds damit recht exakt...
256 Zählerstände pro ms
Macht dann ca. 2560 mögliche Zündzeitpunkte pro Halbwelle.
OK, die Enden, da fehlen ein paar Prozent, wegen den ISR Laufzeiten.

combie:
Das soll doch ein AVR wohl hin bekommen.

Der AVR ist dabei sicher das kleinste Problem :wink: :slight_smile:

Ich hab es nun doch mit der Bibliothek versucht.
Wenn "nur" das Testprogramm auf dem Mega (Clon) ist, funktioniert es auch.
Es flackert zwar (zum Probieren ist eine gute alte 40W Glühlampe angeschlossen), aber es funktioniert.
Die Bibliothek frisst eine Menge Speicher, aber es reicht (hoffentlich), um meine Gäste morgen im Garten nicht im Dunkeln sitzen zu lassen.

Jetzt mal sehen, ob sich das in meinen Sketch einbauen läßt....