Ich hoffe ihr könnt mir ein bischen Detailwissen vermitteln mit folgendem Problem. Schritt1: Ich habe einen Lichtdimmer als Phasenanschnitt mit einem Atmega8 und Triaczündung über Optodiac. Alles inkl. Netznulldurchgang wird zuverlässig erkannt und der Dimmalgorhytmus funktioniert eigentlich tadellos. Schritt2: Nun habe ich den Leistungsteil doppelt ausgeführt, also 2 Optodiacs und Triacs, um unabhängig 2 Lampen zu schalten und zu dimmen. Das Problem ist nun die Zündzeitpunkte der beiden Triacs, die unabhängig geschaltet werden sollen. Die vorherige Variante mit nur 1 Triac habe ich über Timer1 Overflow Interrupt gelöst, in dem ich bei Erkennung des Nulldurchgangs in der INT0 ISR den Timer1 vorgeladen habe und bei dessen Überlauf das Triac gezündet hat. Nun habe ich aber 2 verschiedene Zündzeitpunkte und kann den Timer1 OVL nicht mehr benutzen. Nun habe ich folgendes gemacht: 1.) Nulldurchgang wird erkannt -> INT0 Service Routine wird ausgeführt. 2.) In der INT0 ISR warte ich mit der Funktion _delay_us nun eine Zeitspanne und danach setze ich den entspr. Ausgang, zünde also das Triac. Somit könnte ich 2 Triacs zu verschiedenen Zeitpunkten zünden. Getestet habe ich das und es funktionert mit fest vorgegebenen werten. 3.) Ich möchte aber in der MAIN Schleife eine globale Variable (volatile) verändern, quasi den Helligkeitswert für das Licht. In der INT0 ISR soll dann dieser globale Wert als Vorgabe für die _delay_us Funktion genutzt werden. -> Dies klappt leider nicht. Setze ich z.B. in der ISR einen festen Wert, z.B. _delay_us(8000), dann habe ich ein schön konstant gedimmtes Licht. Gehe ich den Weg über eine globale Variable, z.B. "volatile int16_t Test=8000" und schreibe in der ISR dann _delay_us(Test); dann klappt es nicht mehr. Das Licht flackert wie verrückt, weil das Timing nicht mehr stimmt. D.h. die Übergabe einer globalen Variable an die ISR scheint sehr viel Zeit zu benötigen. Welche Möglichkeiten gibt es, die beschriebene Funktion umzusetzen, also einen gewünschten Wert an die ISR zu übergeben, aber ohne dass es dabei zu zeitlichen Verzögerungen kommt. Eine statische Variable ohne Volatile habe ich auch getestet, dann klappt es wieder ohne Flackern, nur kann ich dann im Main Programm die Variable nicht verändern und das bringt mich nicht weiter. P.s. Verwendet wird ein Atmega8 mit internem Oszillator 1 MHz.
Hallo In fast jedem Beitrag mit Wartezeiten steht, benutze _delay_us IMMER mit einem konstanten Wert als Parameter, benutze NIEMALS eine Variable, weil das nicht funktioniert. Das Ergebnis siehst du. Gruß
Oliver Dewald schrieb: > P.s. Verwendet wird ein Atmega8 mit internem Oszillator 1 MHz. Wenn du den Takt auf 8Mhz hochdrehen würdest, gäbe es auch noch eine andere Möglichkeit Grund: delays willst du eigentlich nicht haben, schon gar nicht innerhalb einer ISR Der Weg geht so Du benutzt den Timer 0 als interne Uhr. Wenn du den 8 Bit Timer benutzt, dann generiert dir der bei 8Mhz Takt und einem Vorteiler von 1 exakt 31250 Interrupts in der Sekunde oder anders ausgedrückt: Alle 0.000032 Sekunden einen. Das ist dein Basistakt Kommt dein INT0 daher, so setzt du eine gloable Variable für jede Lampe auf einen entsprechenden Wert zwischen 0 und 625 ( == 31250 / 50) In der ISR werden nacheinander die jeweiligen Zähler wieder heruntergezählt und wenn 0 erreicht wird, wird der Triac gezündet. -> Du kannst nahzu beliebig viele Dimmstufen bauen ohne einen einzigen delay zu benötigen. Einzige Einschränkung: Deine ISR muss innerhalb von 256 Taktzyklen ihre Berechnungen fertig haben, weil dann schon der nächste Timer Interrupt kommt.
1 | uint16_t dimmer1; |
2 | uint16_t dimmer2; |
3 | volatile uint16_t vorladeWert1; |
4 | vaoltile uint16_t vorladeWert2; |
5 | |
6 | ISR( TIMER0_OVF_vect ) |
7 | {
|
8 | if( dimmer1 > 0 ) { |
9 | dimmer1--; |
10 | if( dimmer1 == 0 ) |
11 | zünde Triac 1 |
12 | }
|
13 | |
14 | if( dimmer2 > 0 ) { |
15 | dimmer2--; |
16 | if( dimmer2 == 0 ) |
17 | zünde Triac 2 |
18 | }
|
19 | }
|
20 | |
21 | ISR( INT0_vect ) |
22 | {
|
23 | dimmer1 = vorladeWert1; |
24 | dimmer2 = vorladeWert2; |
25 | }
|
26 | |
27 | int main() |
28 | {
|
29 | ...
|
30 | Timer initialisieren auf Vorteiler 1 / Overflow Interrupt |
31 | |
32 | sei(); |
33 | |
34 | while( 1 ) { |
35 | ...
|
36 | vorladeWert1 = .... berechne Wert für Lampe 1 ( 0..625) |
37 | ...
|
38 | vorladeWert2 = .... berechne Wert für Lampe 2 ( 0..625) |
39 | ...
|
40 | }
|
41 | }
|
_delay_us immer mit festen Werten benutzen mache ich normalerweise auch, ich habe nur nicht verstanden, warum eine lokale Variable, die ich an die Funktion _delay_us übergebe einen Unterschied hat zur einer globalen Variable, die ich dort übergebe. Ich könnte mir zwar denken, dass der Compiler das sieht und intern dann gleich einen festen Wert einträgt, es sich deswegen genauso verhält wie eine Konstante, aber es ist ja trotzdem eine lokalte VARIABLE, die ihren Wert zumindest in der ISR ändern könnte. Naja ... Dann zum Beitrag von kbuchegg: Vielen Dank für die ausführliche Antwort. Ich muss zugeben, dass sich der Ansatz garnicht schlecht anhört. Ich muss damit mal ein bischen experimentieren, evtl. kann man es wirklich auf diese Art angehen. Ich muss schauen, wie ich das mit der Triac Zündung dann mache, da ich das Signal auch wieder wegnehmen muss, damit der Triac sich im Nulldurchgang auch selbst löscht und nicht sofort wieder leitet. Es sind auch nicht 50 sondern 100 INT0 Interrupts pro Sekunde wegen der Sinuswelle. Ich denke ich probiere es so wie beschrieben und lege noch eine 3. Variable an, z.B. Dimmer1, Dimmer2, Loeschzeit. Diese wird dann auf den max. spätesten Löschzeitpunkt vor dem Nulldurchgang eingestellt und setzt bei Erreichen die Ausgänge zurück. Ich muss dann die beiden anderen, Dimmer1 und Dimmer2 bei ==1 abfragen und anschließend nochmal um 1 erniedrigen, damit nur bei ==1 das Triac zündet. Ich werde mal nach dem Wochenende berichten, ob ich damit zum Erfolg komme. Falls es noch andere Vorschläge gibt, ich bin für alles gerne offen. Rein Interesse halber interessiert mich aber dennoch, warum die _delay_us Funktion solche Schwierigkeiten bei globalen Variablen hat ?
Oliver Dewald schrieb: > Vielen Dank für die ausführliche Antwort. Ich muss zugeben, dass sich > der Ansatz garnicht schlecht anhört. Alles, mit dem man einen delay eliminieren kann, hört sich nicht nur gut an, sondern ist eine ausgesprochen gute Idee > Ich muss damit mal ein bischen > experimentieren, evtl. kann man es wirklich auf diese Art angehen. Ich > muss schauen, wie ich das mit der Triac Zündung dann mache, da ich das > Signal auch wieder wegnehmen muss, damit der Triac sich im Nulldurchgang > auch selbst löscht und nicht sofort wieder leitet. Gib das Signal bei einem Zählerstand von 1 auf den Triac und bei 0 nimmst du es wieder weg. Oder bei 0 ein, so wie im Pseudocode angegeben, und bei jedem ISR Aufruf am Anfang aus.
1 | ISR( TIMER0_OVF_vect ) |
2 | {
|
3 | Triac1 aus |
4 | Triac2 aus |
5 | |
6 | if( dimmer1 > 0 ) { |
7 | dimmer1--; |
8 | if( dimmer1 == 0 ) |
9 | zünde Triac 1 |
10 | }
|
11 | |
12 | if( dimmer2 > 0 ) { |
13 | dimmer2--; |
14 | if( dimmer2 == 0 ) |
15 | zünde Triac 2 |
16 | }
|
17 | }
|
auch dann haben die Triacs den Zündimpuls nur über den Zeitraum von einem ISR Aufruf bis zum nächsten anliegen und danach ist er wieder weg. > Es sind auch nicht 50 > sondern 100 INT0 Interrupts pro Sekunde wegen der Sinuswelle. OK. Wusste nicht, ob du den Interrupt auf aufsteigende/absteigende oder beide Flanken hast. Ist ja kein Problem. Dann hast du anstelle von 6-hundertirgendwas dimmstufen nur noch 3-hundertirgendwas. Sollte kein Beinbruch sein :-) > ich probiere es so wie beschrieben und lege noch eine 3. Variable an, > z.B. Dimmer1, Dimmer2, Loeschzeit. Kannst du machen. Ist aber nicht notwendig. Wenn du den Ausgang bei 1 ein und bei 0 wieder ausschaltest, tuts das auch. > Rein Interesse halber interessiert mich aber dennoch, warum die > _delay_us Funktion solche Schwierigkeiten bei globalen Variablen hat ? Hat sie nicht. Du kannst ganz einfach keine Variablen benutzen. Weder lokale noch globale. Die delay Funktionen sind darauf angewiesen, dass der Compiler alles wegoptimieren kann. Kann er das nicht, dann stimmt das alles hinten und vorne nicht. Je nachdem wieviel der Optimizer noch optimieren konnte, kriegst du dann unterschiedliche Ergebnisse. Und nein: 'Sieht so als als ob' ist noch kein Nachweis für 'funktioniert zuverlässig'
Karl heinz Buchegger schrieb: >> Ich muss damit mal ein bischen >> experimentieren, evtl. kann man es wirklich auf diese Art angehen. Ich >> muss schauen, wie ich das mit der Triac Zündung dann mache, da ich das >> Signal auch wieder wegnehmen muss, damit der Triac sich im Nulldurchgang >> auch selbst löscht und nicht sofort wieder leitet. > > Gib das Signal bei einem Zählerstand von 1 auf den Triac und bei 0 > nimmst du es wieder weg. Mmmh, erstmal vielen Dank für die Anwort mit dem _delay_us, werde mir das dann für spätere Projekte im Hinterkopf behalten. Das mit dem Triacsignal zurücknehmen bei Wert ==0, ist das lange genug für das Triac zum Zünden ? Nach obiger Berechnung und einem Takt von 8 MHz wären dann zwischen Zündzeitpunkt und Löschzeitpunkt ohne Verarbeitungszeit des Optodiacs nur knapp 30 us. Ich weiß nicht ob das ausreicht, das Triac definiert zu zünden. Alternativ verzichte ich aber vielleicht einfach noch auf 50 Dimmstufen und schalte das Triac beim Wert == 50 und bei == 0 nehme ich den Ausgang zurück, dann reicht es auf jeden Fall und mit gut 250 Dimmstufen komme ich auch noch ausreichend genug hin um fließende Dimmverläufe zu erzeugen. Bei meiner momentanen 1 Triac Lösung und Timer1 OVF habe ich etwa 1000 Dimmstufen bei einem 1 MHz Takt.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.