AVR: Komischer Fehler nach dem Ein- / und Ausschalten

Gast #3497494
Lesenswert?

Hallo zusammen,

ich habe ein Problem, das ich mir so langsam gar nicht mehr Erklären 
kann. Also ich verwende einen Mikrocontroller der Familie ATmega 2561, 
der eine Linearisierungsfunktion für ein W1 Stellglied berechnet. Soweit 
so gut, er macht „eigentlich“ was er soll.
Mein Problem ist, das es gelegentlich zu Fehlern kommt.

Beispiel: alles Funktioniert, Spannungsversorgung wird abgeschaltet und 
nach einer Zeit wieder eingeschaltet und siehe da, der Mikrocontroller 
berechnet die Funktion falsch. Nach einem hin und her aus Aus-/ und 
Einschalten geht’s dann wieder, wie durch Zauberhand.
Hat jemand schonmahl Ähnliche Probleme gehabt?

Viele Grüße und schonmahl Danke im Voraus für Anregungen und Tipps
Tobias
#3497502
Lesenswert?

> und siehe da, der Mikrocontroller berechnet die Funktion falsch.
> Nach einem hin und her aus Aus-/ und Einschalten geht’s dann wieder,
> wie durch Zauberhand.

Klingt nach nicht initialisierten Variablen, die mit zufälligen Werten 
lostraben. Ein paar mal ist der Zufall dann eben auch gerade richtig, so 
dass sich das Problem von selbst regelt. Ein ander mal dann eben wieder 
nicht.
Gast #3497518
Lesenswert?

Hallo Karl Heinz,
besten Dank für den Tipp. Ich hatte in meiner Funktion Variablen gleich 
mit einem Ergebniss Initialisiert.
z.B. float Bruch = a / b;

jetzt habe ich das so gemacht

float Bruch = 0;
Bruch = a/b;

Es hat denn anschein, als würde es jetzt besser laufen :-)

grüße Tobias
Gast #3497634
Lesenswert?

tja so ganz wars das wohl doch nicht....jetzt ist der fehler wieder da

hier ist der quellcode
1
/************************LÜFTERSTEUERUNG*****************************
2

3

4
***********************************************************************/
5

6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
#include "TrigonometrischeFunktionen.h"
10

11

12

13
/*****************************Variablen-Deklaration****************/
14

15
int ALPHA = 17900;
16
volatile int ALPHAnutz = 17900;
17

18
volatile int gezuendet_A = 0;
19
volatile int gezuendet_B = 0;
20
volatile int ausschaltsicherung = 0;
21
volatile int netzErkannt = 0;
22
volatile int drehrichtung = 0;
23
volatile int frequenz = 0;
24

25
volatile int fallendeFlanke = 1; //W1
26
volatile int zweiteHalbwelle = 0;
27
volatile int weiterZuenden = 0;
28
volatile int leitdauerErkennung = 0;
29

30
volatile int LAMBDA = 0;
31

32
float u = 0;
33
const float uMin = 1;
34
const float uMax = 1023;
35
const float pi = 20000;
36

37

38

39
/****************************Funktionen-Deklaration****************/
40
void PortInit(void);
41

42
void Timer1Init(void);  //Zündverzögerung
43
void Timer3Init(void);  //Leitdauererfassung
44
void Timer4Init(void);  //Ermittlung der Periodendauer
45

46
void AdcInit(void);
47
void Init(void);
48
int ADC_Lesen(void);
49

50
int Linearisierung(volatile int LAMBDA, volatile float u);
51

52

53
/****************************Port W1 Initialisierung*****************/
54

55
void PortInit(void) {
56

57
  DDRD=0x00;  //Eingang LDE;NDG
58

59
  DDRC=0xFF;  //Ausgang W1-Zündung
60
  PORTC=0xFF; //Anfangszustand; lowactic
61

62
  DDRF=0x00;  //Eingang AD-Konverter
63

64
  DDRB=0xFF;  //Ausgang 
65
  PORTB=0x00;
66
}
67

68
/****************************TIMER1-Initialisierung*****************/
69
 
70
void Timer1Init(void)  //Zündverzögerung
71
{
72
  TCCR1B = 0x00;          //stop Timer
73
  TCCR1A = 0x00;          //normaler Modus = inkrementale Aufzählung
74

75
  TIMSK1 |= (1<<OCIE1A) | (1<<OCIE1B) | (1<<OCIE1C); //Interrupts aktivieren für: compare Register A B C
76

77
  EIMSK |= (1<<INT0);        //aktiviere INT0
78
  EICRA |= (1<<ISC01);       //INT0 reagiert auf fallende Flanke
79

80
  TCCR1B = 0x02;          //Vorteiler 8; start Timer1 bei 0
81
}
82

83
/****************************TIMER3-Initialisierung*****************/
84

85
void Timer3Init(void)  //Leitdauererfassung
86
{
87
  TCCR3B = 0x00;          //stop Timer
88
  TCCR3A = 0x00;          //normaler Modus = inkrementale Aufzählung
89

90
  EIMSK |= (1<<INT1);        //aktiviere INT1
91
  EICRA |= (1<<ISC10);       //INT1 reagiert auf fallende und steigende Flanke
92

93
  TCCR3B = 0x02;          //Vorteiler 8; start Timer3 bei 0
94
}
95

96

97

98
/******************************EXTERNER Interrupt*****************/
99

100
/*Netzsynchronisierung*/
101
ISR (INT0_vect)        //NDG; 240us Verzögerung
102
{
103
  leitdauerErkennung = 1;
104
  
105

106
  TCNT1 = 0;
107
  ALPHAnutz = ALPHA;
108
  OCR1A = ALPHAnutz;
109
  gezuendet_A=1;
110
  gezuendet_B=0;
111

112
  
113
}
114

115

116
ISR (INT1_vect)        //Leitdauer; 304us Verzögerung
117
{
118

119
    static int LAMBDAmess = 4000;
120

121
      if(fallendeFlanke)
122
      {
123
        fallendeFlanke = 0;
124
        TCNT3 = 0;
125
      }
126
        else //(fallendeFlanke) 
127
        {
128
          fallendeFlanke = 1;
129
          LAMBDAmess=TCNT3;
130
        }
131

132
          if(LAMBDAmess >= 1000)      //mindestens >= 1000 um den fehlerhaften Zündimpuls nicht zu berücksichtigen!!!
133
          {
134
            LAMBDA = LAMBDAmess;
135
          }
136
      
137
}
138

139
/******************************SOFTWARE Interrupts****************/
140

141
/*Timer1 Zünden des Thyristorsn*/
142
ISR (TIMER1_COMPA_vect)          //Zündung erste Halbwelle
143
{
144

145
  if (gezuendet_A=1)
146
  {      
147
    
148
      PORTC &= ~(1<<PC0);    //T1 AN W1 Low Aktiv 
149
      OCR1B = ALPHAnutz + 400;  //ALPHAnutz + LAMBDA;
150

151
      zweiteHalbwelle = 1;
152
      weiterZuenden = 1;
153

154
  }
155
}
156

157
ISR (TIMER1_COMPB_vect)          //Löschen aller gezündeten Pins
158
{
159
  if(gezuendet_A=1)
160
  {
161
    if(weiterZuenden)
162
    {
163

164
      PORTC |= (1<<PC0);    //T1 aus W1 Low Aktiv
165
      gezuendet_A=0;
166
      gezuendet_B=1;
167
        
168
    
169
      if(zweiteHalbwelle)
170
      {
171
        //gezuendet=1;
172
        zweiteHalbwelle = 0;
173
        OCR1C = ALPHAnutz + 20000;
174
      }
175
      else
176
      {
177
        gezuendet_A=0;
178
        weiterZuenden = 0;
179
      }
180
    }
181
  }                    
182
}
183
ISR (TIMER1_COMPC_vect)          //Zündung zweite Halbwelle
184
{
185
  
186
  if(gezuendet_B=1)
187
  {
188
    if(weiterZuenden)
189
    {
190

191
          PORTC &= ~(1<<PINC0);    //T1 an W1 Low Aktiv
192
        
193
          OCR1B = ALPHAnutz + 20400;
194

195
          gezuendet_B=0;
196
          gezuendet_A=1;
197
    }
198

199
  }
200

201
}  
202

203
/****************************Funktionen************************/
204

205
/*Linearisierung*/
206
int Linearisierung(volatile int LAMBDA, volatile float u)  
207
{
208
int bruch = 0;
209
int zaehler =0;
210
int nenner = 0;
211
int result = 0;
212
float K = 0;
213
float Ua = 0;
214
int Ul = 0;
215
int Pi = 20000;
216
float UaDurchUl = 0;
217
float UaDurchUlQuadrat =0;
218
int UaDurchUlQuadratMalPi = 0;
219

220
int  sinusVonLambda = 0;
221

222
int arcCosVonBruch = 0;
223

224
int arcCosVonBruchInTakte = 0;
225
int arcCosVonBruchInTakteminusLAMBDA = 0;
226

227
K = 1023/230;
228
Ua = u/K; 
229
Ul = 230;
230
UaDurchUl = Ua/Ul;
231
UaDurchUlQuadrat = UaDurchUl*UaDurchUl;
232
UaDurchUlQuadratMalPi = UaDurchUlQuadrat*Pi;
233

234

235

236
 zaehler = (UaDurchUlQuadratMalPi) - LAMBDA;
237
 nenner = (-(float)sinusVonLambda);
238

239
 bruch = zaehler / nenner;
240

241

242
arcCosVonBruch = ((float)ArcCosinus(bruch)/10000);
243

244
arcCosVonBruchInTakte = (float)ArcCosinus(bruch)/10000*(180/3.14/0.009) ;
245

246
arcCosVonBruchInTakteminusLAMBDA = arcCosVonBruchInTakte - LAMBDA;
247
  
248
result =13000 - (arcCosVonBruchInTakteminusLAMBDA /2);
249

250

251
if(result > 17900)      //maximaler ALPHA = 161,1° (17900 Takte)
252
  {
253
    result = 17900;
254
  }
255

256
  if(result < 5000)      //minimaler ALPHA = 58,5° (6500 Takte)
257
  {
258
    result = 5000;
259
  } 
260

261

262
  return result; 
263

264

265

266
}
267

268

269

270
/***************************************ADC W1**********************/
271
void AdcInit (void)
272
{
273
   ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2); //ADC0  
274
   ADMUX |= (1<<REFS0); //externe Referenzspannung nutzen 5 V
275
  ADCSRA |=(1<<ADEN); //ADC überhaupt zu aktivieren.
276
   ADMUX |= (0<< ADLAR);   // Die 8 bit kommen in das Low-Register 
277
  ADCSRA |=(1<<ADSC); //Messvorgang startet.
278
   ADCSRA |=(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//Vorteiler 128
279
}
280

281
int ADC_Lesen(void)
282
{
283

284
  ADCSRA |=(1<<ADEN);         //ADC aktivieren.
285
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
286
  while (ADCSRA & (1<<ADSC) );    // auf Abschluss der Konvertierung warten
287
 
288
  ADCSRA &= ~(1<<ADEN);        // ADC wieder deaktivieren
289
  
290
  return ADCW;                  // ADC auslesen und zurückgeben
291
}
292

293

294

295

296
//********************************INITIALISIERUNG*******************
297
void Init() {
298
  
299
  PortInit();
300

301
  Timer1Init();
302
  Timer3Init();
303
  PORTB |= (1<<PINB3);  //rote LED aus
304
  PORTB |=(1<<PINB3);  //rote LED aus
305
  AdcInit();
306
}
307

308

309
//*****************************HAUPT-PROGRAMM********************/
310
int main() 
311
{
312
  
313
  int linearisierungsBeschraenkung = 20;
314
  
315

316
  cli();  //disable Interrupts
317
  Init();
318
  sei();  //alles initialisiert
319
      //enable Interrupts  
320

321
  while(1)
322
  {
323
    u = (((uMax - uMin) / (1023)) * (ADC_Lesen()) + uMin);
324

325
    if(linearisierungsBeschraenkung >= 20)    //alle 20 main-Durchläufe wird hier rein gesprungen
326
    {
327
      linearisierungsBeschraenkung = 0;
328
      ALPHA = Linearisierung(LAMBDA, u);//+13000;// - 480;  //ACHTUNG! Dieser Korrekturfaktor ist nur für die ALTE Schaltung!
329
    }
330
    
331
    linearisierungsBeschraenkung++;  
332
  }    
333
}
#3497687
Lesenswert?

Kann mal jemand meine Mathe kontrollieren?

Im Code steht
1
K = 1023/230;
2
Ua = u/K; 
3
Ul = 230;
4
UaDurchUl = Ua/Ul;

da wird ziemlich viel rumgerechnet. Aber was wird (sollte) eigentlich 
gerechnet werden.

Da steht

  UaDurchUL = Ua / Ul

Ul einsetzen

  UaDurchUL = Ua / 230

Ua einsetzen

  UaDurchUl = ( u / k ) / 230

Auf Doppelbruch erweitern

  UaDurchUl = ( u / k ) / ( 230 / 1 )

Doppelbruch auflösen

  UaDurchUL = u / ( k * 230 )

k einsetzen

  UaDurchUL = u / ( 1023 / 230 * 230 )

kürzen

  UaDurchUL = u / 1023


Da wird als eine Menge rumgerechnet, nur um u durch 1023 zu dividieren. 
Bei jeder Floating Point Operation hat man aber entsprechende Rundungs 
bzw. Darstellungsfehler. Die 'einfachere Version'

 UaDurchUL = u / 1023.0;

ist also nicht nur schneller, sondern auch höchst wahrscheinlich näher 
an der Wahrheit.
#3497689
Lesenswert?

Tobias schrieb:
> hallo karl Heinz,
>
> ähmm ist mir nicht klar.


Was mir nicht klar ist: Wwarum du das nicht weißt, wie sich die Sache 
mit den Datentypen in C (bzw. in den meisten Programmiersprachen) 
verhält?

> warum sollte den 1023/230 nicht 4.447 sein?

weil 1023 ein int ist und 230 ein int ist.
Damit ist das eine Integer Division. Und die erzeugt nun mal keine 
Nachkommastellen.

Das du das Divisionsergebnis einem float zuweist, ist zwar nett, aber 
völlig irrelevant. Entscheidend sind die Datentypen der beteilgten 
Operanden. Und die sind nun mal beide int.
Moderator (Firma: Titel) Persönliche Seite #3497715
Lesenswert?

Das verwirrt mich ein wenig:
1
      if(fallendeFlanke)
2
      {
3
        fallendeFlanke = 0;
4
        TCNT3 = 0;
5
      }
6
        else //(fallendeFlanke) 
7
        {
8
          fallendeFlanke = 1;
Seltsamer Name, der sich selber nicht verdient.

Ich vermute übrigens, dass du genau an dieser Stelle ein Problem mit der 
Flankenerkennung hast. Denn dich interessiert das, was tatsächlich am 
Pin ist keinen Deut, sondern du fabulierst dir selber eine Flanke her...

Was, wenn nach dem Einschalten der Eingangspin nicht den Pegel hat, 
den deine Software erwartet?
Gast #3497724
Lesenswert?

Hallo  Lothar,

das kommt daher, das ich um meine Funktion durchführen zu können, die 
Leitdauer an meinem Triac messen muss. Da die Leitdauererfassung aber 
immer im Nulldurchgang der Wechselspannung eine kurze Leitdauer misst, 
obwohl keine vorhanden ist, habe ich das so abgestellt.
#3497732
Lesenswert?

Tobias schrieb:
> Also bis jetzt hat das alles keinen großen Einfluss.

Noch sind wir ja nicht fertig.
Bis jetzt hat mich ja die Logik noch überhaupt nicht interessiert, 
sondern ich such erst mal den Code nach formalen Dingen ab, die seltsam 
aussehen und mehr verwirren, als sie zum Verständnis des Codes 
beitragen. Zb das hier
1
ISR (TIMER1_COMPB_vect)          //Löschen aller gezündeten Pins
2
{
3
  if(gezuendet_A=1)
4
  {
5
    if(weiterZuenden)
6
    {
7

8
      PORTC |= (1<<PC0);    //T1 aus W1 Low Aktiv
9
      gezuendet_A=0;
10
      gezuendet_B=1;
11
        
12
    
13
      if(zweiteHalbwelle)
14
      {
15
        //gezuendet=1;
16
        zweiteHalbwelle = 0;
17
        OCR1C = ALPHAnutz + 20000;
18
      }
19
      else
20
      {
21
        gezuendet_A=0;
22
        weiterZuenden = 0;
23
      }
24
    }
25
  }                    
26
}

nach der Erkennung von weiterZuenden wird gezuendet_A auf jeden Fall 
erst mal auf 0 gesetzt. D.h. das innere if suggeriert hier
1
      if(zweiteHalbwelle)
2
      {
3
        //gezuendet=1;
4
        zweiteHalbwelle = 0;
5
        OCR1C = ALPHAnutz + 20000;
6
      }
7
      else
8
      {
9
        gezuendet_A=0;
10
        weiterZuenden = 0;
11
      }

dass dies nur im Falle einer zweiten Halbwelle der Fall wäre. 
Tatsächlich stimmt das aber gar nicht. gezuendet_A ist auf jeden Fall 0, 
egal wie dieser if ausgeht. Betrachtet man nur das if, kommt man also zu 
falschen Schlussfolgerungen, bzw. in weiterem Kontext dann eben zu: was 
stimmt denn jetzt eigentlich? Immer 0, oder nur im Falle 
zweiteHalbwelle.

Das sind so die Dinge, die einem bei rein formaler Durchsicht schon mal 
auffallen. Genauso wie du für meinen Geschmack im Programm da ein wenig 
sehr freizügig rumrechnest. Hast du die Funktion 'Linearisierung' schon 
mal in einem PC Programm getestet, indem du sie systematisch in einer 
Schleife mit variierenden Werten durchgejagt und das Ergebnis 
kontrolliert hast?
Moderator (Firma: Titel) Persönliche Seite #3497733
Lesenswert?

Tobias schrieb:
> habe ich das so abgestellt.
Die Flankenerkennung funktioniert nicht, wenn du nicht mit der 
Aussenschaltung garantiert immmer mit dem selben Pegel am Pin beginnst.

Nur mal angenommen, du erwartest zuerst ein "low" am Eingang, der ist 
aber gerade "high": dann erkennst du immer dort eine fallende Flanke, wo 
in der Realität eine steigende Flanke ist...

> Was halt komisch ist, ist das ich ein anderes Verhalten habe, wenn ich
> im Betrieb den Programmer rausziehe oder reinstecke, das hat doch
> eigentlich nichts mit der Funktion zu tun, oder?
Hast du zusätzlich noch EMV-Sauereien auf dem Interrupt-Pin?
#3497746
Lesenswert?

Tobias schrieb:
> Hallo  Lothar,
>
> das kommt daher, das ich um meine Funktion durchführen zu können, die
> Leitdauer an meinem Triac messen muss. Da die Leitdauererfassung aber
> immer im Nulldurchgang der Wechselspannung eine kurze Leitdauer misst,
> obwohl keine vorhanden ist, habe ich das so abgestellt.

Dann ist aber 'fallendeFlanke' ein ganz schlechter Name.
Der suggeriert nämlich etwas, was offensichtlich nicht stimmt bzw. auch 
gar nicht erwartet war.

Sieht trotzdem seltsam aus, der ganze Interrupt. Offenbar wird da 
abwechselnd TCNT auf 0 gesetzt bzw. der TCNT geholt. Da wird also was 
ausgemessen.
Der Interrupt ist eingestellt auf
1
  EIMSK |= (1<<INT0);        //aktiviere INT0
2
  EICRA |= (1<<ISC01);       //INT0 reagiert auf fallende Flanke
ergo, wird die Zeitdauer von einer fallenden Flanke zur nächsten 
ausgemessen, aber nur bei jedem 2.ten mal.

Was das mit deiner Erklärung über die Leitdauer zu tun hat, ist mir 
allerdings nicht klar. Eine irgendwie erkennbare Erkennung eines 
'Fehlerzustands' (Zitat: "obwohl keine vorhanden ist") kann ich keine 
erkennen. Da wird sich halt drauf verlassen, dass der jeweils erste 
Interrupt schon passen wird.
Gast #3497748
Lesenswert?

Ok, also da das mit dem if(gezuendet_A=1) habe ich rausgeschmissen.
Funktion funktioniert immer noch. Mittlerweile auch so, dass ich 
Spannung An- und Aus machen kann und es geht dann auch immer noch
#3497752
Lesenswert?

Karl Heinz schrieb:

> Der Interrupt ist eingestellt auf
>
1
>   EIMSK |= (1<<INT0);        //aktiviere INT0
2
>   EICRA |= (1<<ISC01);       //INT0 reagiert auf fallende Flanke
3
>


Ach entschuldigung. Das ist ja gar nicht der INT0, über dessen ISR wir 
reden.
Dessen initialisierung ist ja hier
1
  EIMSK |= (1<<INT1);        //aktiviere INT1
2
  EICRA |= (1<<ISC10);       //INT1 reagiert auf fallende und steigende Flanke

ok. Selbes Problem - wie von Lothar schon angemerkt. Da wird drauf 
vertraut, dass die erste Flanke, die einen Interrupt auslöst, schon eine 
fallende sein wird.
GUt.
Aber: was wenn dem nicht so ist?
#3497766
Lesenswert?

Tobias schrieb:
> Ok, also da das mit dem if(gezuendet_A=1) habe ich rausgeschmissen.

Na wenn das mal gut geht.

Da war offenbar eine Verriegelung der einzelnen Compare Interrupts 
vorgesehen, so dass der ganze COmpare-Interrupt Tanz nur dann zu 
Veränderungen am Pin führt, wenn hier
1
ISR (INT0_vect)        //NDG; 240us Verzögerung
2
{
3
  leitdauerErkennung = 1;
4
  
5

6
  TCNT1 = 0;
7
  ALPHAnutz = ALPHA;
8
  OCR1A = ALPHAnutz;
9
  gezuendet_A=1;
10
  gezuendet_B=0;
11

12
  
13
}
der ganze Vorgang angestossen wird. Sprich: bleiben die INT0 Interrupts 
aus, dann laufen die Compare-Interrupts ins Leere und der Pin PC0 wird 
nicht mehr angesteuert.

Ist dieses Verhalten jetzt noch gewährleistet (ich habs nicht 
durchdacht, was dann passiert)
Gast #3499022
Lesenswert?

Ok, nach einigem nachdenken und einer kleineren Überarbeitung des 
Quellcodes, (Dank eurer Anregungen) ist der momentane Stand, das es beim 
Einschalten erst gar nicht funktioniert, dann falsch und zu guter Letzt 
richtig. Neu ist, dass das Verhalten jetzt immer gleich ist.

Neu hin zu gekommen ist, das wenn ich am Potentiometer
drehe (also einen anderen ADC Wert „u“ vorgebe) es scheinbar keinen 
Einfluss auf meine Funktion hat. Die Funktion spielt also die ganze Zeit 
mit dem gleichen wert rum.
Gast #3499027
Lesenswert?

1
/************************LÜFTERSTEUERUNG*****************************
2

3

4
***********************************************************************/
5

6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
#include "TrigonometrischeFunktionen.h"
10

11

12

13
/*****************************Variablen-Deklaration****************/
14

15
int ALPHA = 17900;
16
volatile int ALPHAnutz = 17900;
17

18
int gezuendet = 0;
19
 int gezuendet_A = 0;
20
  int gezuendet_B = 0;
21

22
volatile int ausschaltsicherung = 0;
23
volatile int netzErkannt = 0;
24
volatile int drehrichtung = 0;
25
volatile int frequenz = 0;
26

27
volatile int fallendeFlanke = 1; //W1
28
volatile int zweiteHalbwelle = 0;
29
volatile int weiterZuenden = 0;
30
volatile int leitdauerErkennung = 0;
31

32
volatile int LAMBDA = 0;
33

34
float u = 0;
35
const float uMin = 1;
36
const float uMax = 1024;
37
const float pi = 20000;
38

39

40

41
/****************************Funktionen-Deklaration****************/
42
void PortInit(void);
43

44
void Timer1Init(void);  //Zündverzögerung
45
void Timer3Init(void);  //Leitdauererfassung
46
void Timer4Init(void);  //Ermittlung der Periodendauer
47

48
void AdcInit(void);
49
void Init(void);
50
int ADC_Lesen(void);
51

52
int Linearisierung(volatile int LAMBDA, volatile float u);
53

54

55
/****************************Port W1 Initialisierung*****************/
56

57
void PortInit(void) {
58

59
  DDRD=0x00;  //Eingang LDE;NDG
60

61
  DDRC=0xFF;  //Ausgang W1-Zündung
62
  PORTC=0xFF; //Anfangszustand; lowactic
63

64
  DDRF=0x00;  //Eingang AD-Konverter
65

66
  DDRB=0xFF;  //Ausgang 
67
  PORTB=0x00;
68
}
69

70
/****************************TIMER1-Initialisierung*****************/
71
 
72
void Timer1Init(void)  //Zündverzögerung
73
{
74
  TCCR1B = 0x00;          //stop Timer
75
  TCCR1A = 0x00;          //normaler Modus = inkrementale Aufzählung
76

77
  TIMSK1 |= (1<<OCIE1A) | (1<<OCIE1B) | (1<<OCIE1C); //Interrupts aktivieren für: compare Register A B C
78

79
  EIMSK |= (1<<INT0);        //aktiviere INT0
80
  EICRA |= (1<<ISC01);       //INT0 reagiert auf fallende Flanke
81

82
  TCCR1B = 0x02;          //Vorteiler 8; start Timer1 bei 0
83
}
84

85
/****************************TIMER3-Initialisierung*****************/
86

87
void Timer3Init(void)  //Leitdauererfassung
88
{
89
  TCCR3B = 0x00;          //stop Timer
90
  TCCR3A = 0x00;          //normaler Modus = inkrementale Aufzählung
91

92
  EIMSK |= (1<<INT1);        //aktiviere INT1
93
  EICRA |= (1<<ISC10);       //INT1 reagiert auf fallende und steigende Flanke
94

95
  TCCR3B = 0x02;          //Vorteiler 8; start Timer3 bei 0
96
}
97

98
/****************************TIMER4-Initialisierung*****************/
99

100
void Timer4Init(void)  //Ermittlung der Periodendauer
101
{
102
  TCCR4B = 0x00;          //stop Timer
103
  TCCR4A = 0x00;          //normaler Modus = inkrementale Aufzählung
104
  TIMSK4 |= (1<<OCIE4A) | (1<<OCIE4B) | (1<<OCIE4C); //Interrupt aktivieren für: compare Register A B C
105
  OCR4A = 20000;          //Vergleichswert für die Zulassung der Leitdauererfassung in der zweiten Halbwelle
106
  TCCR4B = 0x02;          //Vorteiler 8; start Timer4 bei 0
107
}
108

109
/******************************EXTERNER Interrupt*****************/
110

111
/*Netzsynchronisierung*/
112
ISR (INT0_vect)        //NDG; 240us Verzögerung
113
{
114
  leitdauerErkennung = 1;
115
  TCNT1 = 0;
116
  TCNT4 = 0;
117
  ALPHAnutz = ALPHA;
118
  OCR1A = ALPHAnutz;
119
  gezuendet_A =1;  
120
  gezuendet_B =0;  
121
}
122

123

124
ISR (INT1_vect)        //Leitdauer; 304us Verzögerung
125
{
126

127
    static int LAMBDAmess = 4000;
128
    if(leitdauerErkennung)
129
    {
130
      if(fallendeFlanke)
131
      {
132
        fallendeFlanke = 0;
133
        TCNT3 = 0;
134
      }
135
        else //(fallendeFlanke) 
136
        {
137
          fallendeFlanke = 1;
138
          LAMBDAmess=TCNT3;
139
          leitdauerErkennung = 0;
140
        
141

142
          if((LAMBDAmess >= 2000)  & (LAMBDAmess < 12000))    //mindestens >= 1000 um den fehlerhaften Zündimpuls nicht zu berücksichtigen!!!
143
          {
144
            LAMBDA = (20000 - LAMBDAmess);
145
          }
146
        }
147
    }    
148
}
149

150
/******************************SOFTWARE Interrupts****************/
151

152

153
/*Timer1 Zünden des Thyristors*/
154
ISR (TIMER1_COMPA_vect)          //Zündung erste Halbwelle
155
{
156
  if(gezuendet_A==1)
157
  {
158
      PORTC &= ~(1<<PC0);    //T1 AN W1 Low Aktiv 
159
      OCR1B = ALPHAnutz + 400;  //ALPHAnutz + LAMBDA;
160

161
      zweiteHalbwelle = 1;
162
      weiterZuenden = 1;
163
    //  gezuendet=1;
164
      gezuendet_A=1;
165
      gezuendet_B=0;
166
  }
167
}
168

169
ISR (TIMER1_COMPB_vect)          //Löschen aller gezündeten Pins
170
{
171
  if(gezuendet_A==1)
172
  {
173
    if(weiterZuenden)
174
    {
175

176
      PORTC |= (1<<PC0);    //T1 aus W1 Low Aktiv
177

178
      gezuendet_B=1;
179
      gezuendet_A=0;
180

181
      if(zweiteHalbwelle)
182
      {
183
        //gezuendet=1;
184
        zweiteHalbwelle = 0;
185
        OCR1C = ALPHAnutz + 20000;
186
        gezuendet_B=1;
187
        gezuendet_A=0;
188
        
189
      }
190
      else
191
      {
192
        weiterZuenden = 0;
193
        
194
      }
195
    }
196
  }
197
                      
198
}
199
ISR (TIMER1_COMPC_vect)          //Zündung zweite Halbwelle
200
{
201
  if(gezuendet_B==1)
202
  {
203
    if(weiterZuenden)
204
    {
205

206
      PORTC &= ~(1<<PINC0);    //T1 an W1 Low Aktiv
207
      OCR1B = ALPHAnutz + 20400;
208
      gezuendet_B=0;
209
      gezuendet_A=1;
210

211
      
212
    }
213
  }
214
}  
215
/*******************************Timer4 : Leitdauer in der zweiten Haklbwelle**************/
216
ISR (TIMER4_COMPA_vect)    //lässt die Leitdauererkennung einmal in der zweiten Halbwelle zu!
217
{
218
  leitdauerErkennung = 1;
219
}
220
/****************************Funktionen************************/
221

222
/*Linearisierung*/
223
int Linearisierung(volatile int LAMBDA, volatile float u)  
224
{
225
  float relativU = 0;
226
  int bruch = 0;
227
  int zaehler =0;
228
  int nenner = 0;
229
  int result = 0;
230
  float K = 0;
231
  float Ua = 0;
232
  float Ul = 0;
233
  float Pi = 20000;
234
  float UaDurchUl = 0;
235
  float UaDurchUlQuadrat =0;
236
  int UaDurchUlQuadratMalPi = 0;
237

238
  int  sinusVonLambda = 0;
239

240
  int arcCosVonBruch = 0;
241

242
  int arcCosVonBruchInTakte = 0;
243
  int arcCosVonBruchInTakteminusLAMBDA = 0;
244

245
  relativU = u / uMax;
246

247
  //K = 5/230;
248
  K = uMax / 230;
249
  Ua = u/K; 
250
  Ul = 230; 
251
  UaDurchUl = Ua / Ul;
252
  //UaDurchUl = u / 1024;
253
  UaDurchUlQuadrat = UaDurchUl*UaDurchUl;
254
  UaDurchUlQuadratMalPi = UaDurchUlQuadrat*Pi;
255

256

257

258
 zaehler = (UaDurchUlQuadratMalPi) - LAMBDA;
259
 nenner = (-(float)sinusVonLambda);
260

261
 bruch = zaehler / nenner;
262

263

264
  arcCosVonBruch = ((float)ArcCosinus(bruch)/10000);
265

266
  arcCosVonBruchInTakte = (float)ArcCosinus(bruch)/10000*(180/3.14/0.009) ;
267

268
  arcCosVonBruchInTakteminusLAMBDA = arcCosVonBruchInTakte - LAMBDA;
269
  
270
  result =13000 - (arcCosVonBruchInTakteminusLAMBDA /2);
271

272

273
  if(result > 17900)      //maximaler ALPHA = 161,1° (17900 Takte)
274
    {
275
      result = 17900;
276
    }
277

278
    if(result < 5000)      //minimaler ALPHA = 58,5° (6500 Takte)
279
    {
280
      result = 5000;
281
    } 
282

283

284
    return result; 
285

286

287

288
}
289

290

291

292
/***************************************ADC W1**********************/
293
void AdcInit (void)
294
{
295
   ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2); //ADC0  
296
   ADMUX |= (1<<REFS0); //externe Referenzspannung nutzen 5 V
297
  ADCSRA |=(1<<ADEN); //ADC überhaupt zu aktivieren.
298
   ADMUX |= (0<< ADLAR);   // Die 8 bit kommen in das Low-Register 
299
  ADCSRA |=(1<<ADSC); //Messvorgang startet.
300
   ADCSRA |=(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//Vorteiler 128
301
}
302

303
int ADC_Lesen(void)
304
{
305

306
  ADCSRA |=(1<<ADEN);         //ADC aktivieren.
307
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
308
  while (ADCSRA & (1<<ADSC) );    // auf Abschluss der Konvertierung warten
309
 
310
  ADCSRA &= ~(1<<ADEN);        // ADC wieder deaktivieren
311
  
312
  return ADCW;                  // ADC auslesen und zurückgeben
313
}
314

315

316

317

318
//********************************INITIALISIERUNG*******************
319
void Init() 
320
{
321
  
322
  PortInit();
323

324
  Timer1Init();
325
  Timer3Init();
326
  Timer4Init();
327
  PORTB |= (1<<PINB3);  //rote LED aus
328
  PORTB |=(1<<PINB3);  //rote LED aus
329
  AdcInit();
330
}
331

332

333
//*****************************HAUPT-PROGRAMM********************/
334
int main() 
335
{
336
  
337
  int linearisierungsBeschraenkung = 20;
338
  
339

340
  cli();  //disable Interrupts
341
  Init();
342
  sei();  //alles initialisiert
343
      //enable Interrupts  
344

345
  while(1)
346
  {
347
    u = (((uMax - uMin) / (1023)) * (ADC_Lesen()) + uMin);
348

349
    if(linearisierungsBeschraenkung >= 20)    //alle 20 main-Durchläufe wird hier rein gesprungen
350
    {
351
      linearisierungsBeschraenkung = 0;
352
      ALPHA = Linearisierung(LAMBDA, u);//+13000;// - 480;  //ACHTUNG! Dieser Korrekturfaktor ist nur für die ALTE Schaltung!
353
    
354

355
    }
356
    linearisierungsBeschraenkung++;  
357
  }
358
}
#3499057
Lesenswert?

In deinem Programm taucht jetzt plötzlich ein Timer 4 auf.
Von dem gibst du 3 Compare Interrupts frei, hast aber nur für einen 
davon eine ISR -> Prozessorreset.

Dein Programm fängt dauernd wieder von vorne an.


Es ist keine gute Idee, an einem Programm zu viel auf einmal zu 
verändern (bzw. dann auch gleich noch Erweiterungen einzubauen). Eine 
Änderung, testen was sich verändert hat. Dann die nächste Änderung.
Gast #3499112
Lesenswert?

Mal vereinfacht ausgedrückt:
Im Speicher liegt eine Tabelle mit den Adressen aller InterruptRoutinen.
Tritt ein Ereignis ein das einen Interrupt auslöst (bspw. TimerCompare), 
so wird in dieser Tabelle nach der Adresse gesucht und dorthin 
gesprungen.

Hast du allerdings für deinen Compare-Interrupt keine Routine angelegt, 
so kann in der Tabelle natürlich auch keine Service Routine hinterlegt 
sein.
Stattdessen liegt dort ein Sprung nach _reset.
#3499125
Lesenswert?

Es ist wirklich ganz einfach.
Wenn du hier
1
  TIMSK4 |= (1<<OCIE4A) | (1<<OCIE4B) | (1<<OCIE4C);

die Interrupts für Output Compare 4 A, Output Compare 4 B und Output 
Compare 4 C freigibst, dann MUSST du auch für jeden davon eine ISR 
haben:
1
ISR (TIMER4_COMPA_vect)
2
{
3
  ...
4
}
5

6
ISR (TIMER4_COMPB_vect)
7
{
8
  ...
9
}
10

11
ISR (TIMER4_COMPC_vect)
12
{
13
  ...
14
}

Ob die Funktionen etwas machen oder nicht, spielt keine Rolle. Sie 
müssen nur in deinem Programm existieren.
Jede ISR, die NICHT in deinem Programm existiert, ist standardmässig so 
'verdrahtet', dass sie zu einem Prozessorreset führt. Tritt der 
entsprechende Interrupt auf, dann kommt es daher zu einem 
Prozessorreset.
Und bei dir treten diese Interrupts auf. Denn der Timer läuft, die 
COmpare Register haben einen Wert (auch wenn der 0 ist) und der 
Verglichsmechanismus samt versuchtem Auslösen eines Interrupts läuft 
ständig, wenn der Timer läuft. Und irgendwann ist dann eben der 
Timerwert wieder 0 und damit hast du einen Compare Match zb mit OCR4B. 
Der entsprechende Interrupt ist freigegeben, also wird die ISR 
angesprungen, die du aber nicht geschrieben hast. Statt dessen geht es 
in die 'Default-ISR', die einen Reset auslöst.

Du kannst es dir also aussuchen:
Entweder du baust die Funktionen rein oder du gibst die entsprechenden 
Interrupts (die du nicht benötigst), nicht frei.
Das sind deine 2 Optionen. Such dir die aus, die dir am sinnvollsten 
erscheint.
Gast #3499198
Lesenswert?

Tobias schrieb:
> Ok, danke für den Tipp.
> Ich habe die leeren Vergleichswerte rausgenommen. Funktioniert aber
> immer noch nciht so ganz wie ich es gerne hätte :(

Sag mal, hast du die letzten 3 Beiträge mit Absicht überlesen?

Ganz ehrlich, du hast von Tuten und Blasen keine Ahnung. Du doktorst 
irgendwo rum, solange bis es zufällig funktioniert.
Oder aber, es zufällig "annähernd" das macht was du willst.

Ganz ehrlich, was du vorhast ist noch zwei Nummern zu hoch für dich.

Mach das avr-gcc Tutorium hier durch, und zwar von Anfang bis Ende.
#3499213
Lesenswert?

Masl hat es zwar etwas krass ausgedrückt, aber so unrecht hat er nicht.

Das Problem ist halt, dass du einen Haufen Code hast, der nicht wirklich 
funktioniert und keine Ahnung wo man mit Fehlersuche anfangen soll. 
Gerade am Anfang seiner Karriere, kann ein Neuling nichts dümmeres 
machen, als haufenweise ungetesteten Code schreiben. Die beste Methode 
ist immer noch schrittweise vorgehen. Und auch ganz wichtig: von Anfang 
an Testmöglichkeiten vorsehen. Ob das eine UART ist oder ein LCD, oder 
ob man sich mit ein paar LED an ein paar Portpins behelfen muss, wichtig 
ist, dass man nachvollziehen kann, was das Programm macht und vor allen 
Dingen warum es das tut (Variablenwerte!).

Auch wenn die gegenseitige Steuerung der 3 Compare Interrupts vom Timer 
1 ein wenig unübersichtlich ist durch die vielen Flags, die sich 
gegenseitig setzen bzw. freigeben, ... ich denke, der Teil funktioniert. 
Mit einer Statusvariablen, die die Werte von 0 bis 4 durchläuft und in 
den ISR weitergzählt bzw. auf den richtigen Wert geprüft wird, wäre das 
alles wahrscheinlich einfacher nachzuvollziehen gewesen.

Ich hab ja immer noch die Monsterfunktion mit der Berechnung in 
Verdacht. Ob da unter allen Umständen das richtige rauskommt, dafür 
würde ich meine Hand nicht ins Feuer legen, ehe ich nicht auf dem PC die 
Funktion mal in einer Schleife mit wechselnden Werten für die Argumente 
aufgerufen und die Werte kontrolliert habe.
#3499219
Lesenswert?

Der Teil hier
1
ISR (INT1_vect)        //Leitdauer; 304us Verzögerung
2
{
3

4
    static int LAMBDAmess = 4000;
5
    if(leitdauerErkennung)
6
    {
7
      if(fallendeFlanke)
8
      {
9
        fallendeFlanke = 0;
10
        TCNT3 = 0;
11
      }
12
        else //(fallendeFlanke)
bei dem die 'fallende Flanke' mehr zufällig auch wirklich eine fallende 
Flanke ist, ist zb immer noch nicht korrigiert.
#3499370
Lesenswert?

Hallo,

bin ich der einzige, dem aufgefallen ist, dass bei der Berechnung von 
"bruch" eine Division durch Null durchgeführt wird? Es gilt:

nenner = (-(float)sinusVonLambda);

und

int  sinusVonLambda = 0;

und dazwischen wird sinusVonLambda nicht modifiziert.

Abgesehen davon ist bruch als int definiert. Was dann dabei herauskommt 
wage ich nicht zu beurteilen (hängt auch von den trigonometrischen 
Funktionen ab). Insgesammt ist die Linearisierung ein kruder Mix aus int 
und float, ich denke fast, dass hier durchgängig float oder aber eine 
richtige Fixpunkt-Implementation besser wären.

Schöne Grüße,
Martin

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren