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
> 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.
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
Zeig mal. Das ist entweder unlogisch oder ein schwerer Compilerfehler.
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
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)
Dir ist klar, dass hier K = 1023/230; das >Ergebnis NICHT 4.447 lautet, selbst wenn K ein float ist?
hallo karl Heinz, ähmm ist mir nicht klar. warum sollte den 1023/230 nicht 4.447 sein?
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.
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
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
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
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.
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.
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?
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?
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
if(gezuendet_A=1) ist eigentlich immer wahr. Sicher, daß nicht if(gezuendet_A == 1) gemint ist ? Das wäre ein Vergleich ...
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.
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
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?
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
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)
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.
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
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
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
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
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.
ok, also wenn ich das richtig verstanden habe, kann ich den Reset umgehen in dem ich die nicht benötigten Vergleichswerte wieder lösche?
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
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 :(
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.
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.
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.
Wenn 1023/230 ein Float mit dem Wert 4,4478 ergeben soll, muss man einfach 1023.0/230.0 schreiben.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.