Forum: Mikrocontroller und Digitale Elektronik Cosinus Bereichsanpassung


von Tobias (Gast)


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
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl Heinz schrieb:
> Da es sich um eine Lüftereteuerung handelt

Wo liest du das raus? Ich les immer nur "Zündungen"

von Tobias (Gast)


Lesenswert?

Es handelt sich nicht um eine Lüftersteuerung!
Die Platine auf der der mc Plaziert ist war aber mal für eine 
Lüftersteuerung gedacht. Jetzt hängt an der Platine ein Universalmotor

von Karl H. (kbuchegg)


Lesenswert?

Michael Reinelt schrieb:
> Karl Heinz schrieb:
>> Da es sich um eine Lüftereteuerung handelt
>
> Wo liest du das raus? Ich les immer nur "Zündungen"

Kommentar im Code, ganz oben.
(ob der stimmt, weiß ich allerdings nicht)

von Michael R. (Firma: Brainit GmbH) (fisa)


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...

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Michael Reinelt schrieb:

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

Da bist du nicht alleine.

: Bearbeitet durch User
von Tobias (Gast)


Lesenswert?

also Sinn des ganzen ist, dass die Ausgangsspannung bei belastung recht 
konstant bleibt.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tobias schrieb:
> also Sinn des ganzen ist, dass die Ausgangsspannung bei belastung recht
> konstant bleibt.

ich hab zwar mittlerweile das Zünden verstanden (Triac), aber welche 
Ausgangsspannung?

Vielleicht wäre es hilfreich, wenn du mal einen Schritt zurückmachst, 
und beschreibst was du überhaupt machen willst.

von Tobias (Gast)


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;
        }

von Michael R. (Firma: Brainit GmbH) (fisa)


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?

von Tobias (Gast)


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

von Tobias (Gast)


Lesenswert?

Bie größerer Belastung wird die Leitdauer entsprechend länger

von Michael R. (Firma: Brainit GmbH) (fisa)


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?

von Tobias (Gast)


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

von spess53 (Gast)


Lesenswert?

Hi

>nein der Triac bleibt bei belastung länger leitend, bis max zum
>Nulldurchgang

Wie schaltest du den TRiac ab?

MfG Spess

von Tobias (Gast)


Lesenswert?

der Triac geht aus, wenn der Strom am Triac 0 wird. Denn kann man nicht 
selber ausschalten

von Michael R. (Firma: Brainit GmbH) (fisa)


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.

von Tobias (Gast)


Lesenswert?

heheh
ja ich sitze da schon eine weile dran ;)
Formel in Mathematischer Form kommt morgen.

von Michael R. (Firma: Brainit GmbH) (fisa)


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?

von Tobias (Gast)


Angehängte Dateien:

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.

von Georg G. (df2au)


Lesenswert?

Tobias schrieb:
> if ((z)>1 & (z)<30000)

meinst du vielleicht if ((z < 1) && (z < 30000)) ?

von Tobias (Gast)


Lesenswert?

ich "meinte" größer als 1 und kleiner als 30.000 ob mit einem & oder && 
beides ausprobiert aber keinen unterschied festgestellt.

von DomeG (Gast)


Lesenswert?

Tobias schrieb:
> ich "meinte" größer als 1 und kleiner als 30.000
Ich denke das war ein Tippfehler
> ob mit einem & oder && beides ausprobiert aber keinen unterschied festgestellt.
Einzig && ist hier sinnvoll ;-)

also
1
if (z>1 && z<30000)

Gruß

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.