mikrocontroller.net

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


Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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?

Autor: TM F. (p_richner)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Firebird (Gast)
Datum:

Bewertung
-1 lesenswert
nicht 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:
void BeaconLeds(void)
{
  int i = 255;
  
  for (i = 255; i >= 235; i--)
  {
     OCR1A = i;
     OCR1B = i;
      
     _delay_ms(4);
  }
  
  OCR1A = 0;
  OCR1B = 0;
  
  _delay_ms(80);
  
  for (i = 235; i <= 255; i++)
  {
     OCR1A = i;
     OCR1B = i;
      
      _delay_ms(4);
  }
}

Vielleicht hast du mir einen Tipp?

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

: Bearbeitet durch User
Autor: Firebird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Klaus (Gast)
Datum:

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

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
muus natürlich Multitasking sein

Autor: Firebird (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nach langem Suchen konnte ich den Fehler einkreisen. Der Input Compare 
Register Eintrag ICR1 war in der Funktion 'main' eingetragen:
int main(void)
{  
  unsigned char input;
  unsigned int time;

  ICR1 = 255;
...
Nachdem ich ICR1 zur Funktion 'BeaconLeds()' hinzugefügt habe ist das 
Problem behoben:
void BeaconLeds(void)
{
  int i = 255;
  ICR1 = 255;
...

Jetzt kann ich den anderen Tipps nachgehen.

Danke und Gruss
Firebird

Autor: Peter D. (peda)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Nico W. (nico_w)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Firebird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

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

So funktioniert es.

Autor: Reiner K. (reiner_k)
Datum:

Bewertung
1 lesenswert
nicht 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...

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Firebird schrieb:
> So funktioniert es.

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

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
void init_timer1(void)
{  
  // Fast PWM invertiert mit ICR1 als TOP
  TCCR1A = (1 << COM1A0)|(1 << COM1B0)|(1 << COM1A1)|(1 << COM1B1)|(1 << WGM11);
  TCCR1B = (1 << WGM13)|(1 << WGM12)|(1 << CS12);
  
  ICR1 = 255;
  
  OCR1A = 255;
  OCR1B = 255;
}

Im Anhang nochmals der komplette Code.

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
void delay_milliseconds(int8_t milliseconds)
{
    ATOMIC_BLOCK(ATOMIC_FORCEON)
  {
        current_time = counter1;
    } 
    target_time = current_time + milliseconds;
  do
  {
    ATOMIC_BLOCK(ATOMIC_FORCEON)
    {
      current_time = counter1;
    }
  }
  while (current_time <= target_time);
}
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.

Autor: Eric B. (beric)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Firebird (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
void init_timer2(void)
{
  /*
  Interrupt Aktion alle 1,0ms
  Ladewert = 3686400*0,001/64)=57,6 
  */

  TCCR2 |= (1 << WGM21)|(1 << CS22);    // CTC enabled, Prescaler 64
  OCR2 = 57;                            // Endwert 58 - 1 = 57
  TIMSK |= (1 << OCIE2);                // Compare Match Interrupt aktivieren
}

ISR (TIMER2_COMP_vect)
{  
  if(timeCount > 0)
  timeCount--;
}
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.
void BeaconLeds(void)
{
  int i = 0;
  
  for (i = 255; i >= 235; i--)
  {
    if(timeCount == 0)
    {
      OCR1A = i;
      OCR1B = i;
      timeCount = 5;
    }
    else
    {
      i++;    // Wenn if Bedingung nicht erfüllt ...
    }         // dann Leerlauf i um 1 erhöhen
  }

  timeCount = 80;
  
  while(timeCount > 0)
    {
    OCR1A = 0;
    OCR1B = 0;
    }

  for (i = 235; i <= 255; i++)
  {
    if(timeCount == 0)
    {
      OCR1A = i;
      OCR1B = i;
      timeCount = 5;
    }
    else
    {
      i--;    // Wenn if Bedingung nicht erfüllt ...
    }         // dann Leerlauf i um 1 reduzieren
  }
Ich denke, dass ich einen grossen Schritt weiter gekommen bin. Auf Tipps 
und Tricks bin ich gespannt ;-)

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Firebird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?
# define F_CPU 3686400UL
# define F_INTERRUPTS 1000
# define PRESCALE 64

OCR2   =  (F_CPU / F_INTERRUPTS / PRESCALE) - 1;  // Ergibt 56,6

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.