Dein erster Fehler
Das hier
1 | EIMSK |=0x02;// Aktiviert die Erkennung für die Flanke der Nulldurchgangserkennung (S.115 im Datenblatt)
|
2 | EICRA |=0x03;// Hier wird eingestellt das die positive Flanke erkannt wird (S.113/114 Datenblatt)
|
passt nicht.
Du aktivierst den INT1 setzt aber die Levelbits für den Interrupt 0.
Schreib dir Ding eum Himmels willen nicht als Hex-Zahlen! Da sieht man
genau gar nichts!
So
1 | EIMSK |= ( 1 << INT1 );
|
2 | EICRA |= ( 1 << ISC11 ) | ( 1 << ISC10 );
|
wär dir der Fehler nicht passiert. Überlass dem Compiler das
Zusammenstellen der Hex-Zahlen. Für dich ist interessant: In welchem
Register sitzt das Bit und wie heißt es. Welchen binären Wert es hat,
ist für dich als Programmierer uninteressant.
Selbiges für alle Timersachen. Benutze die symbolischen Bitnamen!
PS: Du wirst den Timer3 und den Timer4 gleichzeitig starten. Denn nach
den ersten 30°, beginnt sofort das 120° 'Intervall' zu laufen und nicht
erst, wenn der Triac abgestellt hat.
So
1 | PORTF |= (0<<PF3);//Hier wird Triac2 gezündet (Low)
|
wird ein Bit NICHT auf 0 gezogen.
1 | PORTF &= ~( 1 << PF3 );
|
Wenn du irgendwo ein "0 << Name" stehen hast, dann ist das meistens
falsch. Eine 0 kannst du nach links schieben sooft du willst, das bleibt
immer 0. Und mit einer 0 kannst du verodern sooft du willst, das ändert
nichts.
Anstelle dieser expliziten Port Bitangaben, mach folgendes
1 | #define TRIAC_1_PORT PORTF
|
2 | #define TRIAC_1_DDR DDRF
|
3 | #define TRIAC_1 PF1
|
4 |
|
5 | #define TRIAC_2_PORT PORTF
|
6 | #define TRIAC_2_DDR DDRF
|
7 | #define TRIAC_2 PF3
|
8 |
|
9 | #define TRIAC_3_PORT PORTA
|
10 | #define TRIAC_3_DDR DDRA
|
11 | #define TRIAC_3 PA2
|
12 |
|
13 |
|
14 | #define KILL_TRIAC(port,bit) ((port) |= ( 1 << (bit)))
|
15 | #define FIRE_TRIAC(port,bit) ((port) &= ~( 1 << (bit)))
|
16 |
|
17 | #define FIRE_TRIAC_1 FIRE_TRIAC( TRIAC_1_PORT, TRIAC_1 )
|
18 | #define KILL_TRIAC_1 KILL_TRIAC( TRIAC_1_PORT, TRIAC_1 )
|
19 |
|
20 | #define FIRE_TRIAC_2 FIRE_TRIAC( TRIAC_2_PORT, TRIAC_2 )
|
21 | #define KILL_TRIAC_2 KILL_TRIAC( TRIAC_2_PORT, TRIAC_2 )
|
22 |
|
23 | #define FIRE_TRIAC_3 FIRE_TRIAC( TRIAC_3_PORT, TRIAC_3 )
|
24 | #define KILL_TRIAC_3 KILL_TRIAC( TRIAC_3_PORT, TRIAC_3 )
|
25 |
|
26 | ....
|
27 | void PortInit(void)
|
28 | {
|
29 | DDRD = 0x00;// Eingang für die Nulldurchgangserkennung an PD1
|
30 |
|
31 | // Ausgänge für die Triacs schalten ...
|
32 | TRIAC_1_DDR |= ( 1 << TRIAC_1 );
|
33 | TRIAC_2_DDR |= ( 1 << TRIAC_2 );
|
34 | TRIAC_3_DDR |= ( 1 << TRIAC_3 );
|
35 |
|
36 | // ... und für Default sorgen
|
37 | KILL_TRIAC_1;
|
38 | KILL_TRIAC_2;
|
39 | KILL_TRIAC_3;
|
40 | }
|
41 |
|
42 | ....
|
43 |
|
44 | ISR (TIMER1_COMPA_vect)
|
45 | {
|
46 | TCCR1B = 0x00; //Timer1 stoppen
|
47 |
|
48 | FIRE_TRIAC_1;
|
49 |
|
50 | TCCR3B |= ( 1 << CS21 ) | ( 1 << CS20 );
|
51 | }
|
52 |
|
53 | ....
|
54 |
|
55 | int main()
|
56 | {
|
57 | ...
|
mit ein paar #define habe ich mir Hilfskonstrukte gebaut, so dass im
weiteren Programm aufgabenbezogene Bezeichnungen benutzt werden können.
Anstalle von PF1 oder PF3 ist dann vom TRIAC_1 oder TRIAC_2 die Rede und
ich muss bei PFx nicht mehr ständig überlegen welcher Triac ist denn
das. Es steht sirekt im Programmtext dort! Aus demselben Grund brauch
ich dann auch die meisten Kommentare nicht mehr. Was willst du an
FIRE_TRIAC_1;
groß kommentieren. Es steht alles im Quelltext, was ich wissen muss.
Kein weiterer Kommentar nötig, es sei denn ich will mir dazuschreiben,
WARUM hier gerade dieser Triac gezündet wird.
In diesem Sinne solltest du dir ansehen, wie du dein Programm noch
verändern kannst, so dass du die Kommentare loswerden kannst. In einem
Kommentar sollst du niemals das WIE beschreiben, sondern das WARUM!
i++; // i um 1 erhöhen
das i um 1 erhöht wird, das sehe ich im C-Text, das braucht man nicht
kommentieren. Die Frage ist: warum wird hier i um 1 erhöht, was bedeutet
das? Das sind die Dinge die man kommentieren soll.
Und natürlich: keine magischen Zahlen im Programm. Dinge wie die
konkreten Pins, an denen eine bestimmte Hardware angeschlossen ist,
gehören vorgezogen, sodass man im eigentlichen Programmtext mit
Begriffen aus der Aufgabenstellung operieren kann. Und zwar bereits im
Programmtext und nicht erst im begleitenden Kommentar. Denn Kommentare
haben die unangenehme Eigenschaft, mit fortschreitenden
Programmänderungen immer 'falscher' zu werden. Zb hier
1 | TIMSK3=0x03;// Hier werden die ersten 2-Bits im TIMSK1-Register gesetzt, somit wird bei einem Überlauf ein Timer Overflow Interrupt ausgelöst
|
Lies den Kommentar. Da wird nicht das TIMSK1 Register verändert!
Schreibs so
1 | TIMSK3 |= ( 1 << TOIE3 ) | ( 1 << OCIE3A );
|
und mit ein wenig Übung brauchst du noch nicht einmal einen Kommentar,
weil jeder weiß, dass TOIE3 für "Timer Overflow Interrupt Enable 3"
steht und OCIE3A für "Output Compare Interrupt Enable 3 kanal A" steht.
Wenn du kommentiert, dann spar dir das "Hier werden". No, na. Natürlich
hier. Du wirst nicht einen Kommentar an diese Stelle schreiben der sich
auf Code auf der übernächsten Seite bezieht
1 | TIMSK3 |= ( 1 << TOIE3 ) | // Overflow
|
2 | ( 1 << OCIE3A ); // Compare Match, Vergleichsregister A
|
Sieh zu dass du in Kommentaren die wichtigen Dinge beschreibst.
zb
1 | OCR3A=1000;// Vergleichswert für den Timer3. Entspricht 0,5 ms (sicheres Zünden der Triacs).
|
wo kommen die 1000 her? Wie ist die Berechnung dafür? Kann man diese
Berechnung nicht auch im Quelltext direkt durchführen, so dass
* der Compiler den Wert an die Taktfrequenz (F_CPU) anpasst und
der Compiler dir bei Veränderung der Taktfrequenz die Neuberechnung
abnimmt
* für jeden ersichtlich ist und kontrollierbar ist, wie der Wert
berechnet wird. Es soll ja schon vorgekommen sein, dass man sich
verrechnet hat.
Lass den Compiler für dich arbeiten! Der macht das zuverlässiger als du
das jemals könntest.
und die Variablen b und c hätten sich auch ein paar bessere Namen
verdient.