Forum: Compiler & IDEs Eine Funktion in zwei if-Bedingungen im Takt halten


von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich versuche eine Flugzeugbeleuchtung nach dem Aircraft Recognition 
Lighting System (ARLS) nachzubilden.

Die Antikollisionslichter leuchten im Sekundentakt von Triebwerkstart 
bis Triebwerkstop, d.h. wenn Schalter 1 'on' ist. Diese Lichter sind mit 
PWM über Timer1 realisiert, sodass die LEDS ein rotierendes Licht 
simulieren.

Sobald das Flugzeug in Bewegung kommt sind zusätzlich die 
Positionslichter und Strobelichter dazugeschaltet, d.h. bei Schalter 2 
'on'. Die Positionslichter (rot und grün) sind dauernd an und die 
Stobelichter blitzen nach Vorschrift. Die zeitliche Abfolge ist mit 
Timer0 realisiert.

Ich habe einen nahezu funktionierenden Code geschrieben. Das Problem 
ist, dass beim Zuschalten, bzw. Abschalten der zusätzlichen Lichter 
(über Schalter 2) der Sekundentakt der Antikollisionslichter nicht 
eingehalten wird. Die Funktion für die Antikollisionslichter wird an 2 
Orten aufgerufen, bei if-Bedingung 'Schalter 1 on' und bei if-Bedingung 
'Schalter 1 und 2 on'.

Versuchsaufbau läuft auf einem ATmega8.

Gibt es eine Möglichkeit die Antikollisionslichter bei wechselnder 
if-Bedingung im Takt zu halten?

Danke und Gruss
Firebird

von Nop (Gast)


Lesenswert?

Firebird schrieb:
> Die Funktion für die Antikollisionslichter wird an 2
> Orten aufgerufen, bei if-Bedingung 'Schalter 1 on' und bei if-Bedingung
> 'Schalter 1 und 2 on'.

Dann ruf sie eben nur an einer Stelle auf. Dazu mußt Du natürlich den 
Programmcode entsprechend umschreiben.

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Nop schrieb:
> Dann ruf sie eben nur an einer Stelle auf. Dazu mußt Du natürlich den
> Programmcode entsprechend umschreiben.

Danke für den Tipp, ich war in Gedanken bei den Timern festgefahren.

Den Programmcode habe ich umgeschrieben. Zudem noch die PWM Funktion 
invertiert (damit die LEDS auch dunkel sind) und noch die Werte 
angepasst.

Funktioniert wunderbar bis auf ein neues Problem.

Sobald ich den Reset-Knopf drücke leuchten die LEDS an PB1/OC1A und 
PB2/OC1B, auch nachdem ich den Versuchsaufbau vom Strom genommen und 
wieder angeschlossen habe. Nach Betätigung von Schalter 1 funktioniert 
alles wieder.

Woran kann das liegen?

von TM F. (p_richner)


Lesenswert?

Firebird schrieb:
> Gibt es eine Möglichkeit die Antikollisionslichter bei wechselnder
> if-Bedingung im Takt zu halten?

Setze den Timer auf den GgT-Wert der beiden Frequenzen und zähle danach 
entsprechend.

Bsp.: 800ms und 1000ms blinken: Timer auf 200ms einstellen.

Nebenbei:
Wenn du schon einen Timer verwendest, dann benutze bitte keine delays.
Dieser blockiert nur.

von Firebird (Gast)


Lesenswert?

TM F. schrieb:
> Wenn du schon einen Timer verwendest, dann benutze bitte keine delays.
> Dieser blockiert nur.

Das wäre natürlich super, diesen Leuchtturm-Effekt ohne delays zu 
realisieren. Leider habe ich im Forum und auch im Internet keine 
weiterführenden Informationen gefunden wie man das machen könnte.

Darum habe ich diese Funktion mal so stehen gelassen:
1
void BeaconLeds(void)
2
{
3
  int i = 255;
4
  
5
  for (i = 255; i >= 235; i--)
6
  {
7
     OCR1A = i;
8
     OCR1B = i;
9
      
10
     _delay_ms(4);
11
  }
12
  
13
  OCR1A = 0;
14
  OCR1B = 0;
15
  
16
  _delay_ms(80);
17
  
18
  for (i = 235; i <= 255; i++)
19
  {
20
     OCR1A = i;
21
     OCR1B = i;
22
      
23
      _delay_ms(4);
24
  }
25
}

Vielleicht hast du mir einen Tipp?

von Joachim B. (jar)


Lesenswert?


: Bearbeitet durch User
von Firebird (Gast)


Lesenswert?

Joachim B. schrieb:
> https://playground.arduino.cc/Learning/BlinkWithoutDelayDe
> https://www.google.com/search?q=without+delay+millis

Danke! Das muss ich mir genauer anschauen.

Zuerst möchte ich das Problem betreffend LEDs an PB1 und PB2 lösen:
Firebird schrieb:
> Sobald ich den Reset-Knopf drücke leuchten die LEDS an PB1/OC1A und
> PB2/OC1B, auch nachdem ich den Versuchsaufbau vom Strom genommen und
> wieder angeschlossen habe. Nach Betätigung von Schalter 1 funktioniert
> alles wieder.

Der Ursprung des Fehlers habe ich noch nicht gefunden und bin momentan 
Ratlos.

von Joachim B. (jar)


Lesenswert?

Firebird schrieb:
> Zuerst möchte ich das Problem betreffend LEDs an PB1 und PB2 lösen:
> Firebird schrieb:
>> Sobald ich den Reset-Knopf drücke leuchten die LEDS an PB1/OC1A

dann initialisierst du nicht richtig nach dem Reset!

Ports auf Ausgang und low/high?
PWM auf 0 oder 1 je nachdem wie deine LEDs angeschlossen sind.

LED nach GND oder nach +V mit Rv?

von Klaus (Gast)


Lesenswert?

Such mal Internet nach Multasking. Damit kommsr du ohne delay aus und 
kannst die verschiedensten Zeiten damit machen

von Klaus (Gast)


Lesenswert?

muus natürlich Multitasking sein

von Firebird (Gast)


Lesenswert?

Nach langem Suchen konnte ich den Fehler einkreisen. Der Input Compare 
Register Eintrag ICR1 war in der Funktion 'main' eingetragen:
1
int main(void)
2
{  
3
  unsigned char input;
4
  unsigned int time;
5
6
  ICR1 = 255;
7
...
Nachdem ich ICR1 zur Funktion 'BeaconLeds()' hinzugefügt habe ist das 
Problem behoben:
1
void BeaconLeds(void)
2
{
3
  int i = 255;
4
  ICR1 = 255;
5
...

Jetzt kann ich den anderen Tipps nachgehen.

Danke und Gruss
Firebird

von Peter D. (peda)


Lesenswert?

Firebird schrieb:
> Der Input Compare
> Register Eintrag ICR1 war in der Funktion 'main' eingetragen:

Das reicht doch. Ich sehe nirgends Code, der ICR1 auf einen anderen Wert 
setzt und von selbst kann ICR1 sich nicht ändern.
Unnütze Mehrfachzuweisungen sollte man vermeiden.

Es schadet auch nichts, bei jeder Änderung den neuen kompletten Code als 
Anhang zu posten. Nur mit Schnipseln lassen sich die Änderungen sonst 
kaum nachvollziehen.

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> Das reicht doch. Ich sehe nirgends Code, der ICR1 auf einen anderen Wert
> setzt und von selbst kann ICR1 sich nicht ändern.
> Unnütze Mehrfachzuweisungen sollte man vermeiden.

ICR1 habe ich aus der Funktion 'main' entfernt und zur Funktion 
'BeaconLeds()' hinzugefügt. Vermutlich ist mein vorangegangener Beitrag 
unglücklich formuliert.

Im Anhang ist der komplete Code.

von Nico W. (nico_w)


Lesenswert?

> Peter D. schrieb:
> Das reicht doch. Ich sehe nirgends Code, der ICR1 auf einen anderen Wert
> setzt und von selbst kann ICR1 sich nicht ändern.
> Unnütze Mehrfachzuweisungen sollte man vermeiden.


Firebird schrieb:
> ICR1 habe ich aus der Funktion 'main' entfernt und zur Funktion
> 'BeaconLeds()' hinzugefügt. Vermutlich ist mein vorangegangener Beitrag
> unglücklich formuliert.
>
> Im Anhang ist der komplete Code.

Das würde schon richtig verstanden.


Wenn ICR1 einmal gesetzt wurde, braucht man es nicht wieder neu setzen. 
Aber genau das tust du jetzt.

: Bearbeitet durch User
von Firebird (Gast)


Lesenswert?

Nico W. schrieb:
> Wenn ICR1 einmal gesetzt wurde, braucht man es nicht wieder neu setzen.
> Aber genau das tust du jetzt.

Ok, jetzt hab ich's verstanden.

Wenn ICR1 wieder im 'main' drin steht, dann ist das Problem wieder da. 
Die LEDs an PB1/OC1A und PB2/OC2A leuchten sobald der Versuchsaufbau an 
Strom angeschlossen wird und auch wenn der Reset-Knopf gedrückt wird. 
Sie sollten aus bleiben bis mit Schalter 1 eingeschaltet wird.

Ich habe echt keine Ahnung woran das liegt.

von Peter D. (peda)


Lesenswert?

Firebird schrieb:
> Wenn ICR1 wieder im 'main' drin steht, dann ist das Problem wieder da.

Vermutlich stimmt dann die Reihenfolge nicht.
ICR1 ist erst beschreibbar, wenn ein Mode gesetzt wurde, wo es als 
TOP-Wert verwendet wird.
In Mode 0 (nach Reset) übernimmt es bei einer Flanke an ICP1 den Wert 
des TCNT1.

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt OCR1A und OCR1B mit dem Top-Ladewert in die Funktion 
init_timer() eingetragen. Diese waren nicht definiert.
1
void init_timer1(void)
2
{  
3
  // Fast PWM invertiert mit ICR1 als TOP
4
  TCCR1A = (1 << COM1A0)|(1 << COM1B0)|(1 << COM1A1)|(1 << COM1B1)|(1 << WGM11);
5
  TCCR1B = (1 << WGM13)|(1 << WGM12)|(1 << CS12);
6
  
7
  OCR1A = 255;
8
  OCR1B = 255;
9
}

So funktioniert es.

von Reiner K. (reiner_k)


Lesenswert?

Firebird schrieb:
> Das wäre natürlich super, diesen Leuchtturm-Effekt ohne delays zu
> realisieren. Leider habe ich im Forum und auch im Internet keine
> weiterführenden Informationen gefunden wie man das machen könnte.

Such mal nach dem Stichwort "Statemachine"...
So etwas könnte hier hilfreich sein...

von Peter D. (peda)


Lesenswert?

Firebird schrieb:
> So funktioniert es.

ICR1 = 255; steht aber immer noch an der falschen Stelle.
Es muß nach Setzen von TCCR1A, TCCR1B stehen.

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> ICR1 = 255; steht aber immer noch an der falschen Stelle.
> Es muß nach Setzen von TCCR1A, TCCR1B stehen.

Danke für den Hinweis. Ich hoffe, dass jetzt alles passt.
1
void init_timer1(void)
2
{  
3
  // Fast PWM invertiert mit ICR1 als TOP
4
  TCCR1A = (1 << COM1A0)|(1 << COM1B0)|(1 << COM1A1)|(1 << COM1B1)|(1 << WGM11);
5
  TCCR1B = (1 << WGM13)|(1 << WGM12)|(1 << CS12);
6
  
7
  ICR1 = 255;
8
  
9
  OCR1A = 255;
10
  OCR1B = 255;
11
}

Im Anhang nochmals der komplette Code.

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

TM F. schrieb:
> Wenn du schon einen Timer verwendest, dann benutze bitte keine delays.
> Dieser blockiert nur.

Ich habe jetzt den Code umgeschrieben und die delays durch die Funktion 
delay_milliseconds() mit ATOMIC_BLOCK ersetzt. Der Timer0 ist auf 1ms 
eingestellt.
1
void delay_milliseconds(int8_t milliseconds)
2
{
3
    ATOMIC_BLOCK(ATOMIC_FORCEON)
4
  {
5
        current_time = counter1;
6
    } 
7
    target_time = current_time + milliseconds;
8
  do
9
  {
10
    ATOMIC_BLOCK(ATOMIC_FORCEON)
11
    {
12
      current_time = counter1;
13
    }
14
  }
15
  while (current_time <= target_time);
16
}
Die Software läuft soweit, d.h. visuell. Ich bin mir aber nicht sicher 
ob das so stimmt was ich gemacht habe.

Zudem suche ich eine Möglichkeit die Funktion BeaconLeds() unabhängig 
von den Tail- und StrobeLeds laufen zu lassen. Sobald ich die Zeiten in 
der Funktion BeaconLeds() verlängere (für einen besseren Effekt) werden 
die TailLed und StrobeLeds ausgebremmst, d.h. die TailLed leuchtet nicht 
mehr und die StrobeLeds blitzen nur einmal statt zweimal.

von Eric B. (beric)


Lesenswert?

Firebird schrieb:
>> Wenn du schon einen Timer verwendest, dann benutze bitte keine delays.
>> Dieser blockiert nur.
>
> Ich habe jetzt den Code umgeschrieben und die delays durch die Funktion
> delay_milliseconds() mit ATOMIC_BLOCK ersetzt. Der Timer0 ist auf 1ms
> eingestellt.

Also nur die vordefinierten delays durch selbstgeschriebene ersetzt. 
Auch die blockieren und führen also nicht zum Erfolg.

von Joachim B. (jar)


Lesenswert?

Firebird schrieb:
> Ich habe jetzt den Code umgeschrieben und die delays durch die Funktion
> delay_milliseconds() mit ATOMIC_BLOCK ersetzt.

und WARUM?
eine blockierende Funktion durch eine andere ersetzt?

Irgendwie ist dir Programmierung nicht blockierend noch nicht klar.

Wenn du Kuchen backst, sitzt du dann die ganze Zeit vor dem Herd oder 
merkst dir nur die Uhrzeit wann du ausschalten sollst um sinnvolleres zu 
machen als dem Kuchen beim Backen zuzusehen?

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Eric B. schrieb:
> Also nur die vordefinierten delays durch selbstgeschriebene ersetzt.
> Auch die blockieren und führen also nicht zum Erfolg.

Joachim B. schrieb:
> und WARUM?
> eine blockierende Funktion durch eine andere ersetzt?
>
> Irgendwie ist dir Programmierung nicht blockierend noch nicht klar.

Stimmt, das war mir nicht klar. Unterdessen habe ich viel gelesen und 
auch praktisch ausprobiert. Ich denke, ich komme der Sache langsam 
näher.

Zuerst habe ich timer2 mit Interrupt 1ms konfiguriert und dazu einen 
Timer Compare Match.
1
void init_timer2(void)
2
{
3
  /*
4
  Interrupt Aktion alle 1,0ms
5
  Ladewert = 3686400*0,001/64)=57,6 
6
  */
7
8
  TCCR2 |= (1 << WGM21)|(1 << CS22);    // CTC enabled, Prescaler 64
9
  OCR2 = 57;                            // Endwert 58 - 1 = 57
10
  TIMSK |= (1 << OCIE2);                // Compare Match Interrupt aktivieren
11
}
1
ISR (TIMER2_COMP_vect)
2
{  
3
  if(timeCount > 0)
4
  timeCount--;
5
}
Dann habe ich die Funktion BeaconLeds() so angepasst, dass die 
Zeitabfrage mit if-Anweisungen, ohne warten erfolgt. Bei den 
for-Schleifen mit if-else Anweisungen habe ich meine Kreativität walten 
lassen. Vielleicht gibt es bessere Lösungen.
1
void BeaconLeds(void)
2
{
3
  int i = 0;
4
  
5
  for (i = 255; i >= 235; i--)
6
  {
7
    if(timeCount == 0)
8
    {
9
      OCR1A = i;
10
      OCR1B = i;
11
      timeCount = 5;
12
    }
13
    else
14
    {
15
      i++;    // Wenn if Bedingung nicht erfüllt ...
16
    }         // dann Leerlauf i um 1 erhöhen
17
  }
18
19
  timeCount = 80;
20
  
21
  while(timeCount > 0)
22
    {
23
    OCR1A = 0;
24
    OCR1B = 0;
25
    }
26
27
  for (i = 235; i <= 255; i++)
28
  {
29
    if(timeCount == 0)
30
    {
31
      OCR1A = i;
32
      OCR1B = i;
33
      timeCount = 5;
34
    }
35
    else
36
    {
37
      i--;    // Wenn if Bedingung nicht erfüllt ...
38
    }         // dann Leerlauf i um 1 reduzieren
39
  }
Ich denke, dass ich einen grossen Schritt weiter gekommen bin. Auf Tipps 
und Tricks bin ich gespannt ;-)

von Joachim B. (jar)


Lesenswert?

du hattes mal delay(4) und delay(80)

also würde sich doch anbieten den IRQ auf 4ms zu setzen das gäbe einen 
Sprung zu delay(4) und einen 80/4 zu 20

weil 80 und 4 so schön ineinander passen, aber OK 1ms als Timer für 
anderes geht auch.

Damit du nicht in den Registern rechnen musst empfehle ich dir mal den 
Quellcode von IRMP anzusehen, dort wird einfach gerechnet aus F_CPU und 
gewünschte IRQ.

von Firebird (Gast)


Lesenswert?

Joachim B. schrieb:
> Damit du nicht in den Registern rechnen musst empfehle ich dir mal den
> Quellcode von IRMP anzusehen, dort wird einfach gerechnet aus F_CPU und
> gewünschte IRQ.

Meinst du das, z.B. für Timer2 ?
1
# define F_CPU 3686400UL
2
# define F_INTERRUPTS 1000
3
# define PRESCALE 64
4
5
OCR2   =  (F_CPU / F_INTERRUPTS / PRESCALE) - 1;  // Ergibt 56,6

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.