Forum: Mikrocontroller und Digitale Elektronik Programm arbeitet nicht richtig


von Sven (Gast)


Lesenswert?

Hallo Leute.
Ich habe das folgende Programm geschrieben aber leider funktioniert es 
nicht so wie ich mir das vorstelle. Die Triacs werden leider alle nicht 
auf High und Low gesetzt sondern Triac1 und Triac2 sind immer High und 
Triac3 immer Low. Ich habe keine Idee mehr wieso :(.
Ich verwende den ATmega 2561.
1
//**************************Bibliotheken****************************************************
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
int b=0;//Deklaration der Hilfsvariablen b
5
int c=0;//Deklaration der Hilfsvariablen c
6
7
//**************************Initalisierung***************************************************
8
9
void PortInit(void)
10
{
11
DDRD=0x00;// Eingang für die Nulldurchgangserkennung an PD1
12
DDRF=0xFF;// Ausgang für die 2 Triacs PF1/PF3 
13
DDRA=0xFF;// Ausgang für den Triac PA2
14
PORTC=0xFF;//Port wird auf 1 gesetzt, damit die Triacs bei der Initalisierung nicht zünden, sondern nur bei 0 zünden
15
PORTA=0xFF;//"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
16
}
17
18
void Timer1Init(void)
19
{
20
TCCR1B=0x00;//  Der Timer1 wird hier gestoppt
21
TCCR1A=0x02;//  Der Timmer1 arbeitet so im CTC-Mode ( S.131 im Datenblatt)
22
TIMSK1=0x03;//  Hier werden die ersten 2-Bits im TIMSK1-Register gesetzt, somit wird bei einem Überlauf ein Timer Overflow Interrupt ausgelöst (S.166 Datenblatt)
23
EIMSK |=0x02;// Aktiviert die Erkennung für die Flanke der Nulldurchgangserkennung (S.115 im Datenblatt)
24
EICRA |=0x03;// Hier wird eingestellt das die positive Flanke erkannt wird (S.113/114 Datenblatt)
25
OCR1A=3333;//   Vergleichswert für Alpha=30°
26
TCCR1B=0x02; // Timer1 zum zählen der 3333 Takte zählen. (1,67ms) 
27
}
28
29
void Timer3Init(void)
30
{
31
TCCR3B=0x00;// Der Timer3 wird hier gestoppt
32
TCCR3A=0x02;// Der Timer3 arbeitet so im CTC-Mode
33
TIMSK3=0x03;// Hier werden die ersten 2-Bits im TIMSK1-Register gesetzt, somit wird bei einem Überlauf ein Timer Overflow Interrupt ausgelöst (S.166 Datenblatt)
34
OCR3A=1000;//  Vergleichswert für den Timer3. Entspricht 0,5 ms (sicheres Zünden der Triacs).
35
}
36
37
void Timer4Init(void)
38
{
39
TCCR4B=0x00;// Der Timer4 wird hier gestoppt
40
TCCR4A=0x02;// Der Timer4 arbeitet so im CTC-Mode
41
TIMSK4=0x03;// Hier werden die ersten 2-Bits im TIMSK1-Register gesetzt, somit wird bei einem Überlauf ein Timer Overflow Interrupt ausgelöst (S.166 Datenblatt)
42
OCR4A=13333;// Vergleichswert für den Timer4. Entspricht 6,7 ms (120° Phasenverschiebung).
43
}
44
45
//*****************************Timer1-Compare********************************************************************************************************************
46
47
ISR (TIMER1_COMPA_vect) 
48
{
49
TCCR1B=0x00;//Timer1 stoppen
50
PORTF |= (0<<PF1);// Hier wird Triac1 gezündet (Low)
51
TCCR3B=0x02;//Timer3 zum zählen der 1000 Takte starten
52
}
53
54
//****************************Timer3-Compare*********************************************************************************************************************
55
ISR (TIMER3_COMPA_vect) 
56
{
57
TCCR3B=0x00;//Timer3 stoppen
58
b++;//Hilfsvariable hochzählen
59
if (b==1)
60
{
61
  PORTF |= (1<<PF1);//Triac1 wieder gespert
62
  TCCR4B=0x02;//Timer4 starten
63
}
64
else
65
if (b==2)
66
{
67
  PORTF |= (1<<PF1);//Triac1 sperren (High) 
68
  PORTF |= (1<<PF3);//Triac2 sperren (High)
69
  TCCR4B=0x02;//Timer4 starten
70
}
71
else
72
if (b==3)
73
{
74
  PORTF |= (1<<PF1);//Triac1 sperren (High) 
75
  PORTF |= (1<<PF3);//Triac2 sperren (High) 
76
  PORTA |= (1<<PA2);//Triac3 sperren (High)
77
  b=0;// Zurücksetzten der Hilfsvariable auf null
78
  c=0;// Zurücksetzten der Hilfsvariable auf null
79
}
80
}
81
82
//****************************Timer4-Compare**********************************************************************************************************************
83
ISR (TIMER4_COMPA_vect) 
84
{
85
TCCR4B=0x00;//Timer4 wieder stoppen
86
c++;// Hilfsvariable c hochzählen
87
if (c==1)
88
{
89
  PORTF |= (0<<PF3);//Hier wird Triac2 gezündet (Low)
90
  TCCR3B=0x02;//Timer3 wieder zum zählen der 1000 Takte starten
91
}
92
else
93
94
if (c==2)
95
{
96
  PORTA |= (0<<PA2);// Hier wird Triac 3 gezündet
97
  TCCR3B=0x02;//Timer3 wieder zum zählen der 1000 Takte starten
98
}
99
100
}
101
102
//***************************Hauptprogramm****************************************
103
void Init() 
104
{
105
PortInit(); // Aufruf der Funktion "PortInit"
106
Timer1Init();//Aufruf der Funktion "Timer1Init"
107
Timer3Init();//Aufruf der Funktion "Timer3Init"
108
Timer4Init();//Aufruf der Funktion "Timer4Init"
109
}
110
111
int main() 
112
{
113
cli (); // Interrupts deaktivieren
114
Init(); // Aufruf der Funktion "Init()"
115
sei (); // Interrupts aktivieren
116
}

von Sven (Gast)


Lesenswert?

Jetzt wo es so groß ist hab ich den ersten Fehler schonmal selber 
entdeckt ;). Es muss bei der Port-Initalisierung natürlich PORTF=0xFF 
heißen und nicht PORTC=0xFF.

von No Name Today (Gast)


Lesenswert?

Mit

PortF |= (0<<PF3)

kannst Du das entsprechende Bit nicht wieder auf 0 setzen.
Denn wenn PortF.PF3 = 1 ist, dann folgt PortF.PF3|0 = 1.

Ich denke zum Rücksetzen müsste es

PortF &= ~(1 << PF3)

heißen.

von Sven (Gast)


Lesenswert?

Autsch danke. Habe es auch gerade im Tutorial unter Bitmanipulation 
gesehen danke.

von Karl H. (kbuchegg)


Lesenswert?

In main() fehlt dir ausserdem die Hauptschleife.

Und deine Kommentare sind zu 90% komplett sinnlos. Denk lieber darüber 
nach, wie du deinen Code gestalten kannst, so dass du die Kommentare 
überhaupt nicht brauchst, weil der Code sein eigener Kommentar ist. Die 
Tippenergie deiner Kommentare hättest du besser in den Quellcode 
gesteckt.

Deine Formatierung ist grauenhaft bis nicht vorhanden.


-> seltsame Programmfehler haben nicht selten damit zu tun, dass der 
Programmierer den Code hinschludert.

von Sven (Gast)


Lesenswert?

Okay, dann gib mir doch ein Bsp. für sinnvolle Formatierung.  Dann kann 
ich es ja verbessern.

von Sven (Gast)


Lesenswert?

Noch eine Frage. Ist es notwendig die beiden Hilfsvariablen mit 
"volatile" zu versehen??

von Uwe (de0508)


Lesenswert?

Hallo Sven,

wenn die a und b nur in der ISR verändert werden dann nicht.

sonst müsste man, falls keine volatile Deklaration vorhanden, auch solch 
ein Macro im Hauptprogramm verwenden:
1
#define vu8(x)  (*(volatile uint8_t*)&(x))
2
  // 8-Bit
3
#define vu16(x)  (*(volatile uint16_t*)&(x)) // 16-Bit

von Sven (Gast)


Angehängte Dateien:

Lesenswert?

Okay danke.
Ich habe das Programm nochmal überarbeitet und angehängt. Mir ist 
aufgefallen, dass wenn ich das Programm mit AVR Stück für Stück 
durchgehe, mein Programm nicht über meinen "INT1_vect" hinausgeht, bzw. 
der kleine gelbe Pfeil an der Seite der mir ja zeigt wo ich im Programm 
bin.
MFG

von Sven (Gast)


Lesenswert?

Also das Problem ist, dass mein Programm nicht in die while-Schleife 
springt. Und wenn ich an Pin PD1 ein High vorgebe, fängt das Programm an 
alles wieder von vorne zu initalisieren und springt nicht in die ISR.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Und noch was.
Wenn du eine 1:1 Beziehung zwischen deinem Code und dem was dir der 
Debugger anzeigt brauchst, dann musst du in den Project Options den 
Optimizer ausschalten (-Od). Ansonsten stimmt das alles nicht mehr 
richtig zusammen und die Debugger-Anzeige stimmt nur noch so ungefähr 
mit dem tatsächlich laufenden Code überein.

von Sven (Gast)


Lesenswert?

Okay. Gut ich werde mich nochmal dransetzen und alles vernüftig 
schreiben und mir das für die Zukunft merken.
Danke für die Hilfe und die ganzen Tipps. Das Wochenende gehört dem 
Programm ;).

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
Noch kein Account? Hier anmelden.