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.
_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
Triac1aus
4
Triac2aus
5
6
if(dimmer1>0){
7
dimmer1--;
8
if(dimmer1==0)
9
zündeTriac1
10
}
11
12
if(dimmer2>0){
13
dimmer2--;
14
if(dimmer2==0)
15
zündeTriac2
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.