Cosinus Bereichsanpassung

Gast #3616141
Lesenswert?

Hallo Jungs,
ich habe ein kleines Problem und ich hoffe ihr könnt mir Helfen.
Also ich benutze eine Atmega 2561 von Atmel der einen Triac abhängig von 
einer Funktion Ansteuert. Soweit so gut.
Die Funktion habe ich durch diverse Test geprüft (Messungen, durch das 
gute alte Excel geprüft ect.)
In der Funktion kommt ein Cosinus bzw. ArcCosinus zum Einsatz, je nach 
dem wie groß das Argument des Cosinus wird, muss eine Bereichsanpassung 
für die ArcCosinus Funktion gemacht werden (2Pi +/- ArcCosinus). Und 
hier liegt mein Problem. Die Bereichsabfrage habe ich mit drei If 
schleifen durchgeführt, in meinem Testprogramm bekomme ich auch das 
Passende Ergebnis, nur im Realen Leben geht’s dann irgendwie doch nicht.
Vielleicht hat ja von Euch einer eine Idee.
Grüße Tobias
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
//int ALPHAnutz = 17900;
17
volatile int ALPHAnutz = 17900;
18

19
volatile int ausschaltsicherung = 0;
20
volatile int netzErkannt = 0;
21
volatile int drehrichtung = 0;
22
volatile int frequenz = 0;
23

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

30
volatile int LAMBDA = 0;
31
int LAMBDAmess =0;
32

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

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

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

45
void AdcInit(void);
46
void Init(void);
47

48

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

52
/****************************Port W1 Initialisierung*****************/
53

54
void PortInit(void) {
55

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

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

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

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

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

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

76
  EIMSK |= (1<<INT0);        //aktiviere INT0
77
//  EICRA |= (1<<ISC01);       //INT0 reagiert auf fallende Flanke
78
  EICRA |= (0<<ISC01) | (1<<ISC00);       //INT0 reagiert auf fallende und steigende Flanke
79
  TCCR1B = 0x02;          //Vorteiler 8; start Timer1 bei 0
80
}
81

82

83
/****************************TIMER4-Initialisierung*****************/
84

85
void Timer4Init(void)  //Ermittlung der Periodendauer
86
{
87
  TCCR4B = 0x00;          //stop Timer
88
  TCCR4A = 0x00;          //normaler Modus = inkrementale Aufzählung
89
  TCCR4B = 0x02;          //Vorteiler 8; start Timer4 bei 0
90
  EICRA |= (1<<ISC11)| (1<<ISC10);      //Reagiert auf Steigende Flanke
91
  
92
  EIMSK |= (1<<INT1);
93
}
94
/****************************Hauptprogramm*****************/
95
int main() 
96
{
97
  
98
  int linearisierungsBeschraenkung = 20;
99
  
100

101
  cli();  //disable Interrupts
102
  Init();
103
  sei();  //alles initialisiert
104
      //enable Interrupts  
105

106
  while(1)
107
  {
108

109
    u = (((uMax - uMin) / (1023)) * (ADC_Lesen()) + uMin);
110

111

112
    if(linearisierungsBeschraenkung >= 20)    //alle 20 main-Durchläufe wird hier rein gesprungen
113
    {
114
      linearisierungsBeschraenkung = 0;
115
      ALPHA = Linearisierung(LAMBDA, u);// - 480;  //ACHTUNG! Dieser Korrekturfaktor ist nur für die ALTE Schaltung!
116
    
117

118
    }
119
    linearisierungsBeschraenkung++;  
120
  }
121
}    
122

123
/******************************EXTERNER Interrupt*****************/
124

125
/*Netzsynchronisierung*/
126
ISR (INT0_vect)        //NDG; 240us Verzögerung
127
{
128
  TCNT1=0;
129
  TCNT4=0;
130
  //OCR1A = ALPHA;
131
  steigendeFlanke=1;
132

133
  ALPHAnutz = ALPHA;
134
  OCR1A = ALPHAnutz;
135

136
}
137

138
ISR (INT1_vect)
139
{
140
  LAMBDAmess =TCNT4;
141

142
    if ((LAMBDAmess >=1000) && (LAMBDAmess <19000))
143
    {
144
      LAMBDA=LAMBDAmess;
145
    }
146
}
147
/******************************SOFTWARE Interrupts****************/
148

149
ISR (TIMER1_COMPA_vect)          //Zündung erste Halbwelle
150
{  
151
      PORTC &= ~(1<<PC0);    //T1 AN W1 Low Aktiv       
152
      OCR1B = ALPHAnutz + 400;  //ALPHAnutz + Zuendimpulsbreite;
153
}
154

155
ISR (TIMER1_COMPB_vect)          //Löschen aller gezündeten Pins
156
{
157
      PORTC |= (1<<PC0);    //T1 aus W1 Low Aktiv  
158
      //OCR1A = ALPHA;                           
159
} 
160

161
/****************************Funktionen************************/
162

163
/*Linearisierung*/
164
float Linearisierung(volatile int LAMBDA, volatile float u)  
165
{
166

167
  int result =0;  
168
  float Pi = 3.14159;
169
  float lambda =LAMBDA *0.009*Pi/180; //Takte in RAD
170
  float SINUSVonMinusLAMBDA = (-Sinus(LAMBDA));
171

172
  
173
  float relativ_u = u/uMax;
174
  float relativ_uQuadrat = relativ_u*relativ_u;
175

176
  float Term1 = relativ_uQuadrat*Pi;
177

178
  float Term2 = Term1-lambda;  
179
  float Term3 = SINUSVonMinusLAMBDA/10000; //der Wert aus der inustabelle muss durch 10.000 geteilt werden
180
  
181
  float Bruch = (Term2/Term3)*10000; //Argument für den Arccos //Arccosinus nimmt nur Takte entgegen
182

183
  float ArcCosinusVonBruch = 0;
184
  ArcCosinusVonBruch = (float)ArcCosinus((float)Bruch);
185

186
  float Term4 =ArcCosinusVonBruch/10000; //Von Takte in Radiant Zurückrechnen
187

188
  float Term5=2*Pi-Term4;
189

190
  float Term6 = Term5 - lambda;
191
  float Term7 = Term6/2; //ergebnis in Rad
192

193
  float Term8 = Term7*180/Pi;
194
  float Term9 = Term8*111.11; //Alpha in Takten
195

196
  float z = (Term9*2)+LAMBDA;//Sinus Argument
197

198
//Bereichsabfrage des Sinus Argumentes  
199
    if ((z)>1 & (z)<30000)
200
    {
201
      float Term_5=0;
202
      Term_5 = Pi+Term4;
203

204
      float Term_6 = 0;
205
      Term_6 =  Term_5 - lambda;
206

207
      float Term_7 = 0;
208
      Term_7= Term_6/2; //ergebnis in Rad
209

210
      float Term_8 = 0;
211
      Term_8=Term_7*180/Pi;
212

213
      float Term_9 = 0;
214
      Term_9=Term_8*111.11; //Alpha ;
215

216
      result = Term_9;
217
      return result;
218
    
219
    }
220
      else if ((z)>=30000 & (z) <=40000)
221
      {
222
      result = Term9;    
223
      return result;
224
      }
225
    
226
      else if ((z)>40000 & (z) <=50000)
227
        {
228
        float Term_52=0;
229
        Term_52 = 2*Pi+Term4;
230
        float Term_62 = 0;
231
        Term_62 =  Term_52 - lambda;
232
        float Term_72 = 0;
233
        Term_72= Term_62/2; //ergebnis in Rad
234

235
        float Term_82 = 0;
236
        Term_82=Term_72*180/Pi;
237
        float Term_92 = 0;
238
        Term_92=Term_82*111.11; //Alpha ;
239
        result = Term_92;
240
              return result;      
241
        }
242
                
243
    
244
}
245

246

247

248
/***************************************ADC W1**********************/
249
void AdcInit (void)
250
{
251
   ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2); //ADC0  
252
   ADMUX |= (1<<REFS0); //externe Referenzspannung nutzen 5 V
253
  ADCSRA |=(1<<ADEN); //ADC überhaupt zu aktivieren.
254
   ADMUX |= (0<< ADLAR);   // Die 8 bit kommen in das Low-Register 
255
  ADCSRA |=(1<<ADSC); //Messvorgang startet.
256
   ADCSRA |=(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//Vorteiler 128
257
}
258

259

260
int ADC_Lesen (void)
261
{
262
  ADCSRA |=(1<<ADEN);         //ADC aktivieren.
263
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
264
  while (ADCSRA & (1<<ADSC) );    // auf Abschluss der Konvertierung warten
265
 
266
  ADCSRA &= ~(1<<ADEN);        // ADC wieder deaktivieren
267
  return ADCW;
268
} 
269

270

271
//********************************INITIALISIERUNG*******************
272
void Init() 
273
{
274
  
275
  PortInit();
276

277
  Timer1Init();
278
//  Timer3Init();
279
  Timer4Init();
280
  PORTB |= (1<<PINB3);  //rote LED aus
281
  PORTB |=(1<<PINB3);  //rote LED aus
282
  AdcInit();
283
}
#3616158
Lesenswert?

Was immer du da auch rechnest:
Wäre es nicht um 3 Wohnblöcke einfacher, ganz einfach mit dem ADC Wert 
in eine Tabelle zu gehen und sich dann von dort den auf dem PC 
vorgerechneten OCR Wert aus der Tabelle zu holen?

Da es sich um eine Lüftereteuerung handelt und ich mir ehrlich gesagt 
nicht wirklich vorstellen kann, dass es bei einem Lüfter sinnvoll ist 
bzw. unterscheidbar ist, ob der 256 'Schaltstufen' hat oder doch 1024, 
würde eine Tabelle mit 256 vorgerechneten Werten meines Erachtens 
komplett ausreichend sein.
(Firma: Brainit GmbH) #3616279
Lesenswert?

Karl Heinz schrieb:
>> Wo liest du das raus? Ich les immer nur "Zündungen"
>
> Kommentar im Code, ganz oben.
> (ob der stimmt, weiß ich allerdings nicht)

Meine Güte, ich blinde Nuß! Sogar beim zweiten Mal musste ich suchen...

Tobias schrieb:
> Jetzt hängt an der Platine ein Universalmotor

Und der wird gezündet? ich versteh nämlich den Mörder-Aufwand den du 
hier treibst, überhaupt nciht...
Gast #3616387
Lesenswert?

Funktionieren soll das ganze so,
man habe z.B. einen Universalmotor (Bohrmaschine ect.) diese wird 
belastet. Bei Belastung geht der Universalmotor in die Knie man müsste 
mehr Gas geben. Das Gasgeben wird quasie durch die Formel ersetzt und 
die Ausgangsspannung bricht nicht mehr ein.
Der Vorteil wird dan sein, das die Anwendung eine deutlich bessere 
Dynamik hat.
In der Formel ist ein Cosinus bzw. ArcCosinus. Es ist also notwendig zu 
wissen in welchem Bereich sich das Argument des Cosinus befindet z.B. 
zwischen 270°-360°. Abhängig von dem Bereich muss die Formel etwas 
abgeändert werden (2Pi +/- ArcCosinus)
Und hier ist mein Probelm, die Bereiche frage ich mit den if/else if 
Bedingungen ab und da kommt der uc in's Straucheln

if ((z)>1 & (z)<30000)
    {
      float Term_5=0;
      Term_5 = Pi+Term4;

      float Term_6 = 0;
      Term_6 =  Term_5 - lambda;

      float Term_7 = 0;
      Term_7= Term_6/2; //ergebnis in Rad

      float Term_8 = 0;
      Term_8=Term_7*180/Pi;

      float Term_9 = 0;
      Term_9=Term_8*111.11; //Alpha ;

      result = Term_9;
      return result;

    }
      else if ((z)>=30000 & (z) <=40000)
      {
      result = Term9;
      return result;
      }

      else if ((z)>40000 & (z) <=50000)
        {
        float Term_52=0;
        Term_52 = 2*Pi+Term4;
        float Term_62 = 0;
        Term_62 =  Term_52 - lambda;
        float Term_72 = 0;
        Term_72= Term_62/2; //ergebnis in Rad

        float Term_82 = 0;
        Term_82=Term_72*180/Pi;
        float Term_92 = 0;
        Term_92=Term_82*111.11; //Alpha ;
        result = Term_92;
              return result;
        }
(Firma: Brainit GmbH) #3616404
Lesenswert?

Tobias schrieb:
> man habe z.B. einen Universalmotor (Bohrmaschine ect.) diese wird
> belastet. Bei Belastung geht der Universalmotor in die Knie man müsste
> mehr Gas geben. Das Gasgeben wird quasie durch die Formel ersetzt und
> die Ausgangsspannung bricht nicht mehr ein.

Erstmal würde ich davon ausgehen, dass bei einem 230V-Motor keine 
Spannung einbricht, dafür ist der Innenwiderstand des Netzes viel zu 
gering. Die Abnahme der Drehzahl hat andere Ursachen.

Was nimmt denn deine Formel als Input? Wie erkennst du die Belastung?
Gast #3616422
Lesenswert?

Also als Anwendung dient ein "Nackter" Motor.
Als Input bekommt die Formel die Leitdauer (wie lange liegt Strom am 
Triac) und das verhältnis von eingangsspannung und ausgangsspannung, das 
sich ind er Variable "u" niederschlägt, welches über die momentane 
stellung des Potis zur verfügung steht
(Firma: Brainit GmbH) #3616460
Lesenswert?

Sorry, aber das ist alles etwas konfus...

ich geh mal davon aus du arbeitst mit 230V Wechselspannung, und machst 
mit dem Triac eine Phasenanschnitt-Steuerung.

Hier von "Spannungen" zu sprechen ist mMn etwas verwirrend...

Die "Leitdauer" wird bei belastung eher nicht länger, weil der Triac im 
Nulldurchgang verlöscht. Außer du machst sie länger, indem du früher 
zündest.

Das Poti steuert sowas wie die Soll-Drehzahl, oder?

Also woher erfährst du die momentane Belastung?
Gast #3616476
Lesenswert?

ja ist etwas schwierig zu erklären.
 ja 230V Stimmt.

nein der Triac bleibt bei belastung länger leitend, bis max zum 
Nulldurchgang aber der variirt zwischen quasie 0ms und 8ms.(Es wird jede 
Sinushalbwelle für sich betrachtet)
Die entsprechende länge der leitdauer steht dem uc in Takten zur 
Verfügung (Timer 4)

Es handelt sich nicht um eine Phasenanschnitt-Steuerung, das Poti gibt 
an wie viel der Netzspannung (Eingangsspannung) zur verfügung stehen 
soll. Dieser wert ist zwischen 0->1 1 bedeutet 230V
(Firma: Brainit GmbH) #3616526
Lesenswert?

Jetzt verstehe ich wo du hin willst: du versuchst das Unterschreiten des 
Haltstroms schon vor dem nulldurchgang zu verwenden, um die Belastung zu 
erkennen.

ich fürchte nur, das wird bei induktiven lasten (und dein motor ist eine 
induktive last, sogar eine ekelhafte) nicht verläßlich funktionieren.

Außerdem dokterst du offensichtlich schon länger dran rum: 
Beitrag "AVR: Komischer Fehler nach dem Ein- / und Ausschalten"

> Und hier ist mein Probelm, die Bereiche frage ich mit den if/else if
> Bedingungen ab und da kommt der uc in's Straucheln

nein, das glaub ich nicht. DU kommst aufgrund mangelnder C-Erfahrung ins 
straucheln.

Ehrlich, dein Code ist so "eigenartig", da mag ich mich wirklich nicht 
reinlesen. ich glaub da hat sogar schon Karl-Heinz aufgegeben...

Formulier deine Berechnung mal mathematisch sauber (ohne C) und 
beschreibe sie so, dass ein Außernstehender das auch kapieren kann. Dann 
können wir dir vielleicht helfen, das in sauberes C zu übersetzen.
(Firma: Brainit GmbH) #3616618
Lesenswert?

Was du vielleicht noch überdenken solltest: Vermutlich detektierst du 
den Nulldurchgang der Spannung. Der Triac (und der Motor) 
interessieren sich aber kaum für Spannungen, für die sind Ströme 
relevant. Und jetzt kommt wieder die ekelhafte induktive Last ins Spiel, 
die dir da eine Phasenverschiebung reinbringt. Vermutlich solltest du 
auch den Nulldurchgang des Stroms detektieren.

Außerdem zweifle ich deine 8msec von oben an. ich behaupte das können 
maximal 4msec sein.

Und zuallerletzt hoffe ich, dass du da sauber galvanisch getrennt 
arbeitst. Da ist doch mindestens ein Optokoppler drin, oder?
Gast #3626111
Lesenswert?

Eine etwas verspätete Antwort.
Hier ist die Formel, (falls es noch jemanden interessiert)

An  Michael,
ja klar es ist ein Optokoppler zur Galvanischen Trennung in meiner 
Schaltung enthalten.

Der Strom wird auch über eine Leitdauererfassung gemessen.
Das Läuft vom Ablauf her so, dass der Timer der im Nulldurchgang der 
Netzspannung anfängt zu zählen, bis eine Steigende Flanke (der 
Leitdauererfassung) anliegt, die Differenz ist dann die Leitdauer.

Das geht zwar, aber es geht ein Teil der Leitdauer verloren (die 
Phasenverschiebung). Besser währe wenn der Timer Quasi bei der Fallenden 
Flanke (der Leitdauererfassung) losrennen würde bis die steigende Flanke 
(der Leitdauererfassung) erkannt wird, also unabhängig von der 
Netzspannung.

Warum habe ich das so gemacht.
Es ist ja möglich auf die Fallende und die steigende Flanke zu 
reagieren, aber dann kommt das Programm immer durcheinander. Mann misst 
dann immer von der Fallenden bis zur Steigenden und dann wieder bis zur 
Fallenden Flanke.
Angehängte Dateien:

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