2560: 2 LEDS mit verschieden Taktzyklen realisieren OHNE DELAY!!! HILFE

Hallo, Ich möchte mit meinem Arduino folgendes erstellen:
2 LEDs sollen getrennt von einander mit unterschiedlichen Schaltzeiten
angesteuert werden. Hab mir die Finger schon wunggegoogelt, vielleicht
hab Ihr ein Paar Tipps.

Die erste LED soll 250ms an sein , dann 750 ms aus sein.

gleichzeitig soll dabei die 2. Led 500 ms aus , dann 250 ms dann wieder
250 ms aus sein.

Dann soll der Zyklus wieder beiginnen.

Diagramm siehe bild

Aufpassen, das sind Mikrosekunden, keine Millisekunden! Da wird es aus meiner Sicht schon komplexer, das Timing einzuhalten. Natürlich müsste man dazu klären, wie genau das Timing sein muss bzw. den genauen Verwendungszweck kennen.

ziemlich genau wenns geht. Ob milli oder micro ist erstmal egal, mir gehts es in erster Linier um die Grundfuktion, wie das zu realisieren ist?
Beide leds knallen abwechselnt auf ne Fotodiode die an einem Transimpedanzverstärker hängt. Über Filter gelangen die Signale auf nen Analogeingang. die Spannungen müssen bei led rot an bzw. ir an eingelesen werden.

also wenn du ms meinst (und nicht us), sollte das doch einfach sein:

void loop() {
blinkLed1();
blinkLed2();
}

void blinkLed1() {
if (millis()-timer1<250) digitalWrite(LED1, HIGH);
if ((millis()-timer1>250) && (millis()-timer1<1000)) digitalWrite(LED1, LOW);
if (millis()-timer1>1000) timer1=millis();
}

//und das dann noch für led2
//(globale) variablen definieren

Geht vielleicht auch einfacher, ist aber ein ansatz

Einen 250µs Takt erzeugen (mrsTimer oder ähnlich) und dann zälen wann welche LED an oder aus sein Soll.
Grß
Der Dani

ah ich nehms zurück es müssen µs sein.

Hallo,
mein Lösungsansatz wäre folgender: Du hast 4 Zustandsänderungen pro Millisekunde. Also machst Du Dir einen Timerinterrupt der 4000 mal pro Sekunde überläuft. In der Timerroutine zählst Du von eins bis vier. Bei 1 schaltest Du die erste LED an, bei 2 schaltest Du alle LEDs aus, bei 3 schaltest Du die andere LED an, bei 4 schaltest Du wieder alle LEDs aus und lässt den Zähler wieder von vorne starten.
Gruß,
Ralf

Dann muss ich mich erstmal in den Timern und Interrupts einlesen, da hab ich noch kein Plan davon. beeinflusst der Interrupt negativ mein Programm? Was bedeuted das Übelaufen bei Interrupt?

http://playground.arduino.cc/Code/Timer
http://playground.arduino.cc/Main/MsTimer2
Schau mal hier kannst du den Libaries alle 250µs eine eigne Funktion aufrufen.
Innerhalb der Funktion zählst du dann bis 4.
Bei 0 machst du die R LEDan
Bei 1 wieder aus
Bei 2 IR Led an
Bei 3 IR Led aus
Bei 4 setzt du den Zähler als erstes auf null und dann fängt es bei 0 wieder an.

Gruß
Der Dani

Das Problem an den verlinkten Timer-Libraries ist jedoch, dass diese soweit ich weiß noch den Millisekundenbereich abdecken, nicht aber die Mikrosekunden.

Schau Dir mal meinen Blog an. Am einfachsten wäre es eins der POV Sketche umzufrisieren. POV Reloaded | Blinkenlight

Problemchen ist, daß der Timer in einem 1ms Raster läuft, Du aber 250us brauchst. Dazu würde ich einfach die Takterzeugung aus dem Beispiel hier: Phase detection | Blinkenlight

ISR(TIMER2_COMPA_vect) {
    process_one_sample();
}
 
void initTimer2() {
    // Timer 2 CTC mode, prescaler 64
    TCCR2B = (1<<WGM22) | (1<<CS22);
    TCCR2A = (1<<WGM21);
     
    // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
    OCR2A = 249;
     
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}
 
void stopTimer0() {
    // ensure that the standard timer interrupts will not
    // mess with msTimer2 (that is: avoid undesirable jitter)
    // implies that delay and millis will not work anymore
    TIMSK0 = 0;
}

umfrisieren. Die entscheidende Stelle ist

void initTimer2() {
    // Timer 2 CTC mode, prescaler 64
    TCCR2B = (1<<WGM22) | (1<<CS22);
    TCCR2A = (1<<WGM21);
     
    // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
    OCR2A = 249;
     
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}

Was Du brauchst ist Prescaler 32 und OCR2A = 125-1 also:

void initTimer2() {
    // Timer 2 CTC mode, prescaler 64
    TCCR2B = (1<<WGM22) | (1<<CS22);
    TCCR2A = (1<<WGM21);
     
    // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
    OCR2A = 249;
     
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}
 
void stopTimer0() {
    // ensure that the standard timer interrupts will not
    // mess with msTimer2
    TIMSK0 = 0;
}

Äh ich hab da leider nur Bahnhof verstanden, sorry. Zur Info bin eher hier Neuling. Also anhand dieses Codes wird jetzt aus 1ms 250µs?

Schau dir mal das Datenblatt des Prozessors an. Und ließ da den Abschnitt zu den Timern. Da ist erklärt was die Register wie TCCR2B und OCR2A bedeuten und was die Bits darin (wie WGM22) genau machen.

Da geht auch das Atmega328 Datenblatt ab Seite 94 (der Mega funktioniert genauso, aber hat mehr Timer):

Ab Seite 158 werden die Register des Timer2 beschrieben.

Zu Code wie diesem hier:
(1<<WGM21)

WGM21 (Waveform Generation Mode Bit 1 Timer2) ist Bit 1 im TCCR2A (Timer/Counter Control Register A, Timer 2). Diese Konstante im Code hat daher den Wert 1. Man schiebt also "1" einmal nach Links, wodurch Bit 1 gesetzt wird. Das verordert man mit allen anderen Bits die auch gesetzt werden sollen und weißt den Wert dem Register zu.
WGM21 schaltet den Timer in den CTC Modus (Clear Timer on Compare Match).

Hier ist mir aber nicht klar wozu da noch WGM22 gesetzt wird. WGM21 + 22 sind laut Datenblatt Seite 160 "reserved". Wobei Udo da auch vergessen hat die Prescaler Bits anzupassen. Prescaler 32 ist laut Seite 162 CS21 + CS20. Also:
TCCR2B = (1<<CS21) | (1<<CS20);

Für die Länge des Takts sind der Prescaler wichtig (die CS = Clock Select Bits). Das ist der Wert durch den der Prozessortakt geteilt wird und das Output Compare Register OCRA2. Im CTC Modus wird der Wert des Timers mit dem Compare Register verglichen und bei Übereinstimmung ein Interrupt ausgelöst. Je niedriger dieser Wert, desto schneller wird also der Interrupt ausgelöst.

TIMSK2 ist das Timer Interrupt Mask Register. Da werden einfach die Interrupts aktiviert. Und OCIE2A steht für Output Compare Match A Interrupt Enable Timer 2. Siehe Seite 163

danke für den Tip!!

ISR(TIMER2_COMPA_vect) {
process_one_sample();
}

void initTimer2() {
// Timer 2 CTC mode, prescaler 64
TCCR2B = (1<<WGM22) | (1<<CS21) | (1<<CS20)
TCCR2A = (1<<WGM21);

// 249 + 1 == 250 == 250 000 / 1000 = (16 000 000 / 64) / 1000
OCR2A = 249;

// enable Timer 2 interrupts
TIMSK2 = (1<<OCIE2A);
}

void stopTimer0() {
// ensure that the standard timer interrupts will not
// mess with msTimer2 (that is: avoid undesirable jitter)
// implies that delay and millis will not work anymore
TIMSK0 = 0;
}

also wäre jetzt der Prescale für 250 µs gesetzt.
Allerdings kann ich nichts mit "process_one_sample();" anfangen

Im Anschluss müsste ich dann immer einen Interrupt mit meinen 250µs auslösen , zählen und die ledzustände zuweisen?

Im Anschluss müsste ich dann immer einen Interrupt mit meinen 250µs auslösen

Der Interrupt wird automatisch ausgelöst wenn das Timer Counter Register den Wert des Compare Registers erreicht. Deshalb wird ja OCIE2A gesetzt. Gleichzeitig wird das Counter Register auf Null zurückgesetzt.

Das müsste eigentlich so aussehen:

void initTimer2()
 {
    // Timer 2 CTC mode, prescaler 32
    TCCR2B = (1<<CS21) | (1<<CS22);
    TCCR2A = (1<<WGM21);
     
    OCR2A = 124;
     
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}

Der 124 Wert wurde oben auch genannt, aber im Code auch nicht angepasst. Deshalb solltest du nicht nur abschreiben sondern auch verstehen was hier gemacht wird :slight_smile:

Statt process_one_sample() machst du einfach deine eigene ISR. Du kannst testweise z.B. mal einen Ausgangspin toggeln und dann die Frequenz messen.

Wenn du wie vorgeschlagen, eine Variable zählst und auswertest muss das so ähnlich aussehen. Normale if-else-Abfragen gehen natürlich auch:

volatile byte counter = 0;

ISR(TIMER2_COMPA_vect) 
{
    counter = counter + 1 % 4;

    switch(counter)
    {
       case 0: ...
                  break;
       case 1: ...
                  break;
       case 2: ...
                  break;
       case 3: ...
                  break;
    }
}

#include <MsTimer2.h>
int led1 = 43;
int led2 =44;
volatile byte counter = 0;

void setup()
{
pinMode (led1,OUTPUT);
pinMode (led2,OUTPUT);}

void initTimer2()
{
// Timer 2 CTC mode, prescaler 32
TCCR2B = (1<<CS21) | (1<<CS22);
TCCR2A = (1<<WGM21);
OCR2A = 124;
// enable Timer 2 interrupts
TIMSK2 = (1<<OCIE2A);
}
void loop()
{
volatile byte counter = 0;

attachInterrupt(0,TIMSK2,RISING);
{
counter = counter + 1 % 4;

switch(counter)
{
case 0: digitalWrite(led1,HIGH);
digitalWrite(led2,LOW);
break;
case 1:digitalWrite(led1,LOW);
digitalWrite(led2,LOW);
break;
case 2: digitalWrite(led1,LOW);
digitalWrite(led2,HIGH);
break;
case 3: digitalWrite(led1,LOW);
digitalWrite(led2,LOW);
break;
}
}

}

hmm irgendwo ist noch was falsch ich bekomm mit dem Oszi nix zu messen

Du hast da einen fehler in:

void loop()
{
 volatile byte counter = 0;

attachInterrupt(0,TIMSK2,RISING);
{
    counter = counter + 1 % 4;

    switch(counter)
    {
       case 0: digitalWrite(led1,HIGH);
               digitalWrite(led2,LOW);
                  break;
       case 1:digitalWrite(led1,LOW);
               digitalWrite(led2,LOW);
                  break;
       case 2: digitalWrite(led1,LOW);
               digitalWrite(led2,HIGH);
                  break;
       case 3: digitalWrite(led1,LOW);
               digitalWrite(led2,LOW);
                  break;
    }
}

}

Müsste so aussehen

#include <MsTimer2.h>
int led1 = 43;
int led2 =44;
volatile byte counter = 0;

void setup()
{
  pinMode (led1,OUTPUT);
  pinMode (led2,OUTPUT);
  initTimer2();
}
 
void loop(){
//hier mal nix
}


ISR(TIMER2_COMPA_vect) {  // das ist der Richtige aufruf des "Timer Interrupt" ISR Interupt Service Routine
    counter = counter + 1 % 4;

    switch(counter)
    {
       case 0: digitalWrite(led1,HIGH);
               digitalWrite(led2,LOW);
                  break;
       case 1:digitalWrite(led1,LOW);
               digitalWrite(led2,LOW);
                  break;
       case 2: digitalWrite(led1,LOW);
               digitalWrite(led2,HIGH);
                  break;
       case 3: digitalWrite(led1,LOW);
               digitalWrite(led2,LOW);
                  break;
    }
}

void initTimer2()
 {
    // Timer 2 CTC mode, prescaler 32
    TCCR2B = (1<<CS21) | (1<<CS22);
    TCCR2A = (1<<WGM21);
    OCR2A = 124;
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}

Kann sein das bei meiner Copy Pasta Klammern als Nachtisch fehlen

:smiley:

Gruß
Der Dani

Dankeschon mal für eure Hilfe, es passiert schon mal ansatz weise was :
Die Frequenz liegt aber bei ca 2 hz , und die Impulsdauer bei ca 2ms, was muss denn noch geändert werden?


Da vermute ich einen OrderOfOperations-Fehler. Der avr-gcc Compiler macht Punkt vor Strich (wie alle C/C++ Derivate), hier eine ausführliche Erläuterung

counter = counter + 1 % 4; //rechnet zuerst 1%4, das ist immer 1 und addiert das zu counter.

Die Pulse entstehen dann nur dadurch, dass du counter als byte deklariert hast, dadurch gibts dann alle paar Interrupts einen Überlauf.

Setz ne Klammer und es sollte funktionieren:

counter = (counter + 1) % 4;

Gruß,
Marv

Sorry. Das wäre dann mein Fehler :slight_smile:

Mach für den Anfang mal nur das hier in der ISR bis die Frequenz stimmt:

digitalWrite(led1, !digitalRead(led1));

Das sollte den LED Pin immer toggeln. Wobei beide Funktionen recht langsam sind. Aber so langsam sollten sie auch nicht sein. Die brauchen glaube ich 50-60 Taktzyklen.

attachInterrupt ist eine Arduino Funktion die man hier nicht braucht. Außerdem hattest du die falsch verwendet. Korrekterweise gibt man da nur einen Function-Pointer auf die ISR an. Hier machen wir das aber mit AVR-Befehlen direkt.

Den Wert für OCRn habe ich nicht nachgerechnet. Hier ist das erklärt (aber nicht für den Arduino, also nicht den Code 1:1 kopieren!!):
https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328

Die Formel ist:
OCRn = [ (clock_speed / Prescaler_value) * Desired_time_in_Seconds ] - 1

Also OCRn = (16.000.000 / 32 * 250µs) - 1 =
= 500.000 * 250µs - 1 =
= 124

Das sollte also stimmen

Da ist ganz am Ende auch mal ein Beispiel für 250µs. Das verwendet Prescaler 64 und OCRn = 62. Was aufs Gleiche rauskommt.