Hallo zusammen,
ich möchte mit dem ATmega 2560 einen Triac-Dimmer ansteuern. Die
Sinus-Welle, mit der man den 230V Wechselstrom (50Hz) darstellen kann,
überquert alle 10ms (halbe Sinus-Welle) den Nullpunkt auf der X-Achse
(Zero-crossing). Nun möchte ich per Knopfdruck auf einen angeschlossenen
Schalter ein Dim-Level einstellen. Um das Problem zunächst zu
vereinfachen lasse ich nur zwei Dim-Level zu, nämlich MIN für aus und
MAX für an. Um das Dim-Level auf die zu einem bestimmten Zeitpunkt auf
der Sinus-Kurve einzustellen, lasse ich einen Timer-Interrupt alle 75
Mikrosekunden feuern und habe so 128 diskrete Schritte (clocktick),
wobei 128 MIN (LED aus) und 0 MAX (Led an) darstellt. Den clocktick
setze ich bei jede Zero-crossing auf 0 und jedes Mal wenn der clock-tick
dem eingestellten Dim-Level entspricht, setze ich den Pin, an dem der
Triac angeschlossen ist auf high, warte 10µs und setze ihn dann wieder
auf low. Das ganze lässt sich durch folgenden Code darstellen:
1 | #include <time.h>
|
2 | #include <TimerOne.h>
|
3 | #include <TimerThree.h>
|
4 | #include <DS3232RTC.h>
|
5 | #include <sunMoon.h>
|
6 | #include <DigitalIO.h>
|
7 |
|
8 | #define MIN 127 // LED full off value, not 128 to avoid flicker
|
9 | #define MAX 1 // LED full on value, not 0 -"-
|
10 |
|
11 | volatile byte clock_tick;
|
12 | volatile byte dim_level = MIN; // init DL (LEDs off)
|
13 | volatile byte buttonState1 = LOW;
|
14 |
|
15 |
|
16 | void setup() {
|
17 | DDRC |= 0b01000000; //init pin for Triac as Output
|
18 | pinMode(18, INPUT); // init switch active/passive
|
19 | attachInterrupt(0, zero_cross_int, RISING); // fire interrupt when signal is rising
|
20 | Timer1.initialize(75); // every 75 us
|
21 | Timer1.attachInterrupt(timerIsr); // attach Interrupt Service Routine to Timer Interrupt
|
22 | }
|
23 |
|
24 | void timerIsr() // checks every 10us if triac needs to fired at the expected dim level DL
|
25 | {
|
26 | clock_tick++;
|
27 | if (dim_level == clock_tick)
|
28 | {
|
29 | PORTC |= 0b01000000; //Triac high-
|
30 | delayMicroseconds(10);
|
31 | PORTC &= ~0b01000000; //Triac low
|
32 | }
|
33 | }
|
34 |
|
35 |
|
36 | void zero_cross_int() // reset if sinus wave travels through zero point
|
37 | {
|
38 | clock_tick=0; // every 10
|
39 | }
|
40 |
|
41 | void loop(){
|
42 | if (buttonState1 = digitalRead(channel_1_sw) == HIGH) //Button press
|
43 | {
|
44 | delay(150); // Prellzeit Schalter
|
45 | if (buttonState1 = digitalRead(channel_1_sw) == LOW) // If MAX then MIN, else MAX
|
46 | {
|
47 | if(dim_level == MAX){dim_level = MIN;} else {dim_level = MAX;}
|
48 | }
|
49 | }
|
50 | }
|
Mein Problem ist hier nun, dass ich mit dem hochfrequenten Interrupt
nicht richtig umgehen kann. Meistens passiert nämlich gar nichts, wenn
ich den Schalter betätige. Ab und zu hat man Glück und es funktioniert.
Benutze ich nun aber Serial.print um mir irgendwelchen Text ausgeben zu
lassen, funktioniert das Programm um einiges stabiler. Hier passiert nur
ab und zu nichts bei einem Knopfdruck. Dieses komische Verhalten kann
ich mir nur dadurch erklären, dass die Serial Bibliothek ebenfalls
Interrupts verwendet und dadurch irgendwie das Verhalten meines
Programms beeinflusst wird. Allerdings ist das natürlich nicht
wünschenswert. Ich gehe davon aus, dass ich irgendwie kritische
Sektionen verwenden muss, wenn die Triacs gefeuert werden oder so
ähnlich, komme aber irgendwie auf keinen grünen Zweig. Wo könnte mein
Fehler liegen?
Hoffe ihr könnt mir helfen und sorry für den Roman.
Gruss mikroluk
PS: Die Art den Knopfdruck zu prüfen, habe ich nur deswegen so gewählt,
um die Menge an Interrupts zu reduzieren. Hatte vorher einen
Hardware-Interrupt für den Knopfdruck, das Problem war aber das gleiche.