Go Down

Topic: Delay in millis() ändern (Read 474 times) previous topic - next topic

Tiff48317

#30
Aug 26, 2019, 12:18 am Last Edit: Aug 26, 2019, 12:19 am by Tiff48317
Hallo,

okay, Danke Microbahner, Verständnisproblem mit der Pinnummer geklärt.

Ich weiß aber immer noch nicht wer oder was den Interrupt im Nulldurchgang auslöst. Ich kann nur davon ausgehen das es sicher funktioniert. Mein theoretischer Ablauf müßte wie folgt sein. Nulldurchgang erzeugt den Interrupt, den Startvorgang. Danach erfolgt die umgekehrt proportional wirkende Dimmzeit, max. 10ms. Zwischen Ende der Dimmzeit und dem folgenden Nulldurchgang ist der Triac gezündet.

Würde laut meiner Logik bedeuten, man konfiguriert einen Timer für "Pulslänge" von max. 10ms. Dessen "Compare" Interrupt zündet den Triac. Ausschalten tut sich der Triac automatisch. Damit es keine zeitlichen Verschiebungen gibt, würde ich den Timer nach dem Zündvorgang ausschalten und immer wieder beim Einschaltinterrupt neu starten. Dafür sollte auch der Normal- oder CTC Modus reichen.

Gibt es zum Timerablauf Einwände?

Da du einen Mega hast und wenn du sonst bis jetzt keinen PWM Pins in Benutzung hast, kann man Timer 1 bis 5 frei verwenden.
Beim 8Bit Timer 2 hättest du eine Auflösung von 39µs. Im Grunde sollten die 255 Stufen ausreichend sein.
Bei einem 16Bit Timer hättest du eine Auflösung von 0,15µs.
Womit gibts du die Dimmzeit überhaupt vor? Poti am Analogeingang oder ... ?
Der Timerablauf scheint eben so zu sein.
Ist zwar PhasenANschnittsteuerung statt der gewünschten PhasenABschnittsteuerung, aber das ist jetzt egal, da ich auf Glühlampen umgeschwenkt habe.
Doch, ich habe PWM Pins in Benutzung. DC- Dimmen über einen IRLZ44N.
Die Dimmzeit wird über die IR- Fernbedienung eingespielt. [+ ] und [-], ganz grob erklärt.

Und...da ich jetzt auf die Bibliothek umgeschwenkt habe...versuche ich das momentan verzweifelt anders zu lösen.
Verstehe ich das richtig, dass die Dimmfunktion auf dem PWM Pin ein "Störfaktor" sein könnte?
__________________________________________________
Eine präzise gestellte Frage enthält meist bereits 80% der Antwort.
Die mit dem Postit vor der Stirn...
                                               ...und dem Brett vor dem Kopf.

combie

Quote
Verstehe ich das richtig, dass die Dimmfunktion auf dem PWM Pin ein "Störfaktor" sein könnte?
Natürlich!
Je nach Aufgabenstellung kann ein Timer keine 2 Dinge gleichzeitig.
Da muss man schon Aufmerksam sein, um das doch noch hin zu bekommen.


Lese doch bitte das Datenblatt zu deinem µC ...
Da steht drin, was mit den Timern geht, und wie es geht.
Der Arduino Code liegt auch auf deinem PC, da kannst du sehen, was sowieso schon mit den Timern angestellt wird.

Alle sagen: Das geht nicht!
Einer wusste das nicht und probierte es aus.
Und: Es ging nicht.

Doc_Arduino

#32
Aug 26, 2019, 02:23 am Last Edit: Aug 26, 2019, 02:29 am by Doc_Arduino
Hallo,

weil ich das interessant finde habe ich einen Simulator gebaut. Falls irgendein Pin schon belegt ist lässt sich das ändern. Nur Pin 11, 12 ,13 kannste nicht wie gewohnte PWM Pins verwenden, weil das im Timer 1 abgeschalten ist. Der "50Hz Pin" ist in seiner Timerfunktion direkt geschrieben. Kann man auch ändern nur das eine Pin 12 Änderung noch nicht alles ist was man ändern muss.

Zum warm werden mit den Timern kannste das durcharbeiten.
Funktioniert nur im Zusammenhang mit dem Datenblatt.
https://forum.arduino.cc/index.php?topic=519208.msg3538992#msg3538992

Die oben besagte Auflösung stimmt nicht mehr ganz wenn wir Timer 2 verwenden. Denn wir sind auf die Prescaler angewiesen. Mit Prescaler 1024 und maximalen Timercount von 155 kommen wir auf eine Zeit von 9,98ms. Unter Umständen muss man das noch verkürzen wegen der 50Hz Netztoleranz. Ich denke aber das ca. 150 Dimmabstufungen auch ausreichend sein werden.

Timer 1 dient mir nur als 50Hz Simulator.
Der Pin 12 simuliert damit den Nulldurchgang-Interrupt aller 10ms.

Dieser Pin 12 ist mit dem Eingangspin 2 verbunden welcher die zeroCross Interrupt-Funktion auslöst bzw. aufruft. Darin wird der Timer 2 Counter entsprechend gesetzt und der Timer selbst gestartet.

Hat Timer 2 sein Maximum (255) erreicht löst er einen Overflow-Interrupt aus. Darin wird der Timer 2 ausgeschalten und der Pin 28 für den Triac zum zünden kurz geschalten. Würde man Timer 2 nicht ausschalten driftet zur 50Hz Netzfrequenz irgendwann weg und das Timing liefe aus dem Ruder.

Für die Simulation wird ein Poti an A2 verwendet welches aller 20ms abgefragt wird.
Der gemappte Wert wird zum dimmen verwendet. Bei mir wie folgt.
Poti Linksanschlag, Triaczündung erfolgt kurz vorm nächsten Nulldurchgang, volle Leistung.
Poti Rechtsanschlag, Triaczündung erfolgt kurz nach Nulldurchgang, geringste Leistung.

Schau dir das erstmal am Oszi an. Wenn alles paßt kannste eigentlich alles außer Timer 1 übernehmen und praktisch testen. Ob die derzeitigen 4µs zum Triac zünden ausreichen weiß ich nicht.

Danach bleibt noch viel Platz für Besserungen übrig. Zum Bsp. die Dimmhelligkeitstufen ans menschliche Auge anzupassen für eine empfundene lineare Änderung. Oder je nach dem mit was du die Dimmung regelst Toleranzen einbauen das erst über einem Schwellwert überhaupt eingeschalten wird und darunter immer sicher aus ist.

Alles was mit dem Triac zu tun hat darfst du natürlich gern in einer Struktur zusammenfassen Man möchte ja kein Kaos stiften.

Viel Spass beim weiteren programmieren und mit den Timern.

Code: [Select]

/*
  Doc Arduino - german arduino.cc Forum
  Arduino Mega2560
  Arduino IDE 1.8.9
  avr-gcc 9.2.0
  26.08.2019

  https://forum.arduino.cc/index.php?topic=632819.0
*/

const byte pinInterrupt = 2;
const byte pinTriac = 28;
const byte pinDimPoti = A2;

const byte MaxCount = 155;              // entspricht 9,98ms
const byte OffsetCount = 255-MaxCount;
volatile byte dim;

// 50Hz Simulator mit Timer 1
const byte pin50Hz = 12;


void setup() {  
  pinMode(pinInterrupt, INPUT_PULLUP);
  pinMode(pinTriac, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(pinInterrupt), zeroCross, CHANGE); // 'CHANGE' wegen 50Hz Simulierung
  configTimer2();

  // 50Hz Simulator
  pinMode(pin50Hz, OUTPUT);
  setupTimer1();
}

void loop()
{
  dim = updatePoti();
}


byte updatePoti(void)
{
  static byte var = 0;
  static unsigned long last_ms = 0;
  
  if (millis() - last_ms > 20)
  {
    last_ms = millis();
    unsigned int tmp = analogRead(pinDimPoti);
    var = map(tmp, 0, 1023, 0, MaxCount);
  }
  return var;
}


void configTimer2(void)
{
  cli();
  TCCR2B = 0;   // Timer Rest und Normalmode
  TCCR2A = 0;   // Timer Rest und Normalmode
  TCNT2  = 0;
  TIMSK2 = (1<<TOIE2); // Overflow Interrupt aktiviert
  sei();
}

void startTimer2(void)
{
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // Timer starten mit Prescaler 1024
}

void stopTimer2 ()
{
  TCCR2B = 0;   // da sonst im Register nichts konfiguriert ist kann man eiskalt reseten
}

void zeroCross(void)
{
  // aktuelle Potieinstellung übernehmen und Timer2 starten
  // TCNT2 Count auf maximal 155 setzen, entspricht 9,98ms
  if (dim <= MaxCount)
  {
    TCNT2 = (dim + OffsetCount);
    startTimer2();
  }
}


ISR(TIMER2_OVF_vect) // Timer 2 Overflow Interruptroutine
{
  stopTimer2();
  digitalWrite(pinTriac, HIGH); // Triac zünden
  digitalWrite(pinTriac, LOW);
}


// 50 Hz Nulldurchgang-Simulator
void setupTimer1(void)            
{
  cli();                        
  TCCR1B = 0;                     // Timer Reset
  TCCR1A = 0;
  TCNT1  = 0;
  OCR1A  = 19999;                 // Compare 10ms
  TIMSK1 = (1<<OCIE1A);           // Compare Match Interrupt aktiviert
  TCCR1B = (1<<WGM12)|(1<<CS11);  // CTC Mode & Prescaler 8
  sei();                        
}

ISR(TIMER1_COMPA_vect)            // 50Hz simulieren
{
  PINB = _BV(PB6);                // toggle Pin 12 (PIN.Port.B Bit.6)
}
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Doc_Arduino

#33
Aug 26, 2019, 05:41 pm Last Edit: Aug 26, 2019, 05:44 pm by Doc_Arduino
Hallo,

neue Version ohne zusätzlichen Timer. Außer das ich für die 50Hz Simulation einen benötige (Timer 1).
Mit Hardwaretimer würde man ansonsten für jede einzeln steuerbare Lampe einen neuen Timer benötigen.
Es wird nur mit Differenzen vom micros() Zähler gerechnet.
OFFSET und MINIMUM sind erstmal Pauschalwerte.

OFFSET ist dafür gedacht um zeitliche Toleranzen der Netzfrequenz zu berücksichtigen.
Das heißt ich lasse es nicht zu die Dimmzeit bis an den nächsten Nulldurchgang heranzulassen. Schwankt die Netzfrequenz würde man den Zeitpunkt vom Nulldurchgang überschreiten. Das hätte unschönes Lampenflackern zur Folge und/oder ein ausschalten wäre temporär nicht mehr möglich. Der Wert begrenzt theoretisch die maximale Helligkeit. Praktisch sieht man das nicht.

MINIMUM ist dafür gedacht um ein sicheres Pulsfreies ausschalten zu ermöglichen. Ansonsten würde der Triac mit Null weiterhin vorm Nulldurchgang gezündet, was ein glimmen zur Folge hat.

Theoretisch ist die Anzahl der Lampen (Einheiten) erstmal unbegrenzt.

Auf dem Oszi sieht man schön wie der Zündimpuls innerhalb der halben Periode wandert je nachdem wie man am Poti dreht.

Für den Sketch ist es erforderlich Pin12 mit Pin 2 zuverbinden. Der Rest sollte selbsterklärend sein.

Code: [Select]

/*
  Doc Arduino - german arduino.cc Forum
  Arduino Mega2560
  Arduino IDE 1.8.9
  avr-gcc 9.2.0
  26.08.2019

  https://forum.arduino.cc/index.php?topic=632819.0
*/

const unsigned long HALFPERIODE = 10000;    // [µs] 1/50Hz/2*1000µs            
const unsigned long OFFSET = 200UL;         // [µs] zeitlicher Sicherheitsabstand zum nachfolgenden Nulldurchgang
const unsigned long MINIMUM = 50UL;         // [µs] Triac Mindesteinschaltdauer, ermöglicht aussschalten
unsigned long timeNullPhase;                // [µs] wird mit jedem Nulldurchgang aktualisiert
const byte pinNullPhase = 2;

struct Daten
{
  const byte pinPoti;
  const byte pinTriac;
  const char text[20];
  unsigned long timeTriacOn = 0;
  bool switchOn = 0;
  bool enableOn = 0;
  unsigned int dimWert = 0;
  byte dimProzent = 0;
};

Daten Einheit[] = {
  {A2, 28, "Lampe1 "},
  {A3, 29, "Lampe2 "},
};


const byte ANZAHL = sizeof(Einheit) / sizeof(Daten);

// 50Hz Simulator mit Timer 1
const byte pin50Hz = 12;

void setup()
{
  Serial.begin(250000);

  pinMode(pinNullPhase, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pinNullPhase), zeroCross, CHANGE); // 'CHANGE' wegen 50Hz Simulierung

  Serial.print(F("Anzahl Einheiten: "));
  Serial.println(ANZAHL);

  for (byte i=0; i<ANZAHL; i++) {
    pinMode(Einheit[i].pinTriac, OUTPUT);
  }

  for (byte i=0; i<ANZAHL; i++) {
    Serial.print(Einheit[i].pinPoti);
    Serial.print('\t');
    Serial.print(Einheit[i].pinTriac);
    Serial.print('\t');
    Serial.print(Einheit[i].text);
    Serial.println();
  }

  // 50Hz Simulator
  pinMode(pin50Hz, OUTPUT);
  setupTimer1();
}

void loop()
{
  updatePoti();

  update_Einheit();

  schalteTriac();

  updateDisplay();
}


void zeroCross(void)    // wird nur einmal aller 10ms aufgerufen
{
  timeNullPhase = micros();   // aktuelle Zeit merken für Zeitvergleich zum Triac zünden

  for (byte i=0; i<ANZAHL; i++) {
    Einheit[i].enableOn = true;     // Triac zünden für eine halbe Periode erlauben
  }
}


void updatePoti(void)
{
  static unsigned long last_ms = 0;

  if (millis() - last_ms > 50)
  {
    last_ms = millis();
    for (byte i = 0; i < ANZAHL; i++) {
      Einheit[i].dimWert = analogRead(Einheit[i].pinPoti);  // 0...1023
      // umgekehrt proportionales Periodendauer-Mapping inkl. Offsetanpassung
      Einheit[i].timeTriacOn = map(Einheit[i].dimWert, 0, 1023, (HALFPERIODE-OFFSET), 0); // 0...HALFPERIODE-OFFSET µs
      // Spielerrei, Prozentangabe der aktuell eingestellten Helligkeit
      Einheit[i].dimProzent = 100*Einheit[i].dimWert/1023;
    }
  }
}


void update_Einheit (void)
{
  for (byte i=0; i<ANZAHL; i++)
  {
    if (micros() - timeNullPhase < HALFPERIODE-OFFSET-MINIMUM) { // ermöglicht sicheres ausschalten des Triac
      if (Einheit[i].enableOn) {            // erneute Triaczündung erlaubt?
        if (micros() - timeNullPhase > Einheit[i].timeTriacOn) {
          Einheit[i].switchOn = true;       // Minimumzeit okay, aktivieren
        }
      }
    }  
  }
}


void schalteTriac()
{
  for (byte i=0; i<ANZAHL; i++)
  {
    if (Einheit[i].switchOn && Einheit[i].enableOn) { // Triac zünden wenn freigegeben und nicht schon gezündet wurde
      digitalWrite(Einheit[i].pinTriac, HIGH);        // Zündpuls
      digitalWrite(Einheit[i].pinTriac, LOW);
      Einheit[i].enableOn = false;    // erneutes einschalten bis zum nächsten Nulldurchgang verhindern,
                                      // ansonsten Dauerzündpulse auch kurz vor/während des Nulldurchgangs
      Einheit[i].switchOn = false;    // Zeitschalter deaktivieren
    }
  }
}


void updateDisplay()
{
  static unsigned long last_ms = 0;
  if (millis() - last_ms > 1000)           // gegen Anzeigeflimmern
  {
    last_ms = millis();
    for (byte i=0; i<ANZAHL; i++)
    {
      Serial.print(Einheit[i].text);
      Serial.print(": ");
      Serial.print(Einheit[i].dimWert);
      Serial.print('\t');
      Serial.print(Einheit[i].dimProzent);
      Serial.print('%');
      Serial.print('\t');
      Serial.print(Einheit[i].timeTriacOn);
      Serial.print('\t');
    }
    Serial.println();
  }
}


// 50Hz Nulldurchgang-Simulator
void setupTimer1(void)
{
  cli();
  TCCR1B = 0;                     // Timer Reset
  TCCR1A = 0;
  TCNT1  = 0;
  OCR1A  = 19999;                 // Compare 10ms
  TIMSK1 = (1<< OCIE1A);          // Compare Match Interrupt aktiviert
  TCCR1B = (1<< WGM12)|(1<<CS11); // CTC Mode & Prescaler 8
  sei();
}

ISR(TIMER1_COMPA_vect)            // 50Hz simulieren
{
  PINB = _BV(PB6);                // toggle Pin 12 (PIN.Port.B Bit.6)
}



Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Tiff48317

Soooo spannend.
Ich habe zwar erst am Mittwoch Zeit zum Probieren, aber dann stürze ich mich gleich drauf und berichte.
Jetzt wird erstmal geguckt, was struct ist.
 :)
__________________________________________________
Eine präzise gestellte Frage enthält meist bereits 80% der Antwort.
Die mit dem Postit vor der Stirn...
                                               ...und dem Brett vor dem Kopf.

Go Up