Forum: Mikrocontroller und Digitale Elektronik AVR: Komischer Fehler nach dem Ein- / und Ausschalten


von Tobias (Gast)


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

: Bearbeitet durch Admin
von Karl H. (kbuchegg)


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.

von Tobias (Gast)


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

von Karl H. (kbuchegg)


Lesenswert?

Zeig mal. Das ist entweder unlogisch oder ein schwerer Compilerfehler.

von Tobias (Gast)


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
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

if(gezuendet_A=1)

sieht schon mal seltsam aus.
Beim gcc gibt es doch da eine Warnung! Hast du dir ignoriert?

Ditto:
  if(gezuendet_B=1)

von Karl H. (kbuchegg)


Lesenswert?

Dir ist klar, dass hier

K = 1023/230;

das >Ergebnis NICHT 4.447 lautet, selbst wenn K ein float ist?

von Tobias (Gast)


Lesenswert?

hallo karl Heinz,

ähmm ist mir nicht klar. warum sollte den 1023/230 nicht 4.447 sein?

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> Kann mal jemand meine Mathe kontrollieren?
>
> Im Code steht
>
1
> K = 1023/230;
2
> Ua = u/K;
3
> Ul = 230;
4
> UaDurchUl = Ua/Ul;
5
>

sollte man nicht erst mal die 1023 in frage stellen? Ein 1024 ist doch 
viel schöner

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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?

: Bearbeitet durch Moderator
von Tobias (Gast)


Lesenswert?

Also bis jetzt hat das alles keinen großen Einfluss. Abgesehen das die 
Version von

UaDurchUL = u / 1023

natürlich die weitaus schönere ist.

von Tobias (Gast)


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.

von Tobias (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


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?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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?

: Bearbeitet durch Moderator
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

if(gezuendet_A=1)

ist eigentlich immer wahr. Sicher, daß nicht if(gezuendet_A == 1)
gemint ist ? Das wäre ein Vergleich ...

von Karl H. (kbuchegg)


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.

von Tobias (Gast)


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

von Karl H. (kbuchegg)


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?

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Ach entschuldigung.

Übrigens:
Das ist besonders schlau, die Initialisierung der externen Interrupts in 
2 getrennte Funktionen zu stecken, die dann auch noch "TimerxInit" 
heißen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


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)

von Tobias (Gast)


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.

von Tobias (Gast)


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
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Tobias (Gast)


Lesenswert?

Mir ist nicht ganz klar, warum mein Programm wieder von vorne anfängt? 
Es würde aber erklären, warum das poti quasie keinen einfluss hat

von Dietrich L. (dietrichl)


Lesenswert?

Tobias schrieb:
> Mir ist nicht ganz klar, warum mein Programm wieder von vorne anfängt?

Der Compiler baut defaultmäßig für jede nicht vorhandene ISR einen 
Sprung zum Programmanfang ein (Adresse 0).

Gruß Dietrich

von lex (Gast)


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.

von Tobias (Gast)


Lesenswert?

ok, also wenn ich das richtig verstanden habe, kann ich den Reset 
umgehen in dem ich die nicht benötigten Vergleichswerte wieder lösche?

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Tobias (Gast)


Lesenswert?

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 :(

von Masl (Gast)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von GB (Gast)


Lesenswert?

Wenn 1023/230 ein Float mit dem Wert 4,4478 ergeben soll, muss man 
einfach 1023.0/230.0 schreiben.

von Martin L. (maveric00)


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

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.