Forum: Mikrocontroller und Digitale Elektronik Programmgeschwindigkeit Arduino


von millioner2 (Gast)


Lesenswert?

Hallo,

ich habe ein System gebaut um frühzeitig Fehler in einer Schweißanlage 
zu erkennen. Habe alle Teilsysteme einzeln getestet und war sehr 
zufrieden. Nun wollte ich gerade das gesamte Systen in betrieb nehmen 
und mein Programmcode ist viel zu langsam...

Ich muss mit drei Hallsensoren Wechselströme von 0-30A +/-1A messen.
Außerdem einen 400A +/-3A Gleichstrom über einen Shunt mit 
Messvorverstärker.

Den aktuellen Wert würde ich gerne, muss aber nicht, auf einem LCD 
anzeigen.
Nach dem Abbruch des Lichtbogens möchte ich gerne den Mittelwert über 
die gesamte Lichtbogenzeit anzeigen.

Im Fehlerfall soll sofort der Schweißprozess unterbrochen werden.

Ich denke um sauber einen 50Hz Sinus messen zu können sollte es schon 
maximal 2ms pro Zyklus sein, schätze ich.

Im moment sind es knapp 100ms pro Zyklus. Ich möchte gerne den 
bisherigen elektischen Aufbau erhalten. Als Arduino nutze ich einen 
Arduino Mega 2560 R3.


Wenn noch irgendwelche Fragen sind immer her damit.
1
// Laden von externen Quellen
2
#include <EEPROM.h>
3
#include <Wire.h> 
4
#include <LiquidCrystal_I2C.h>
5
6
7
// Ein-/Ausgangsbelegung
8
#define i_stromL1     A0
9
#define i_stromL2     A1
10
#define i_stromL3     A2
11
#define i_stromOut    A3
12
13
#define o_ein1        22
14
#define o_ein2        23
15
16
17
// Variabeln
18
unsigned long error1ZeitSpeicher;                 // Zeitzwischenspeicher
19
int           error1ZeitWert = 500;               // [ms] Entprellzeit für Error 1-3
20
unsigned long error4ZeitSpeicher;                 // Zeitzwischenspeicher
21
int           error4ZeitWert = 500;               // [ms] Entprellzeit für Error 4-6
22
unsigned long error7ZeitSpeicher;                 // Zeitzwischenspeicher
23
int           error7ZeitWert = 500;               // [ms] Entprellzeit für Error 7
24
unsigned long error8ZeitSpeicher;                 // Zeitzwischenspeicher
25
int           error8ZeitWert = 500;               // [ms] Entprellzeit für Error 8-10
26
bool          errorEinmal;
27
28
float         nullStromIn = 1.0;                  // [A] Wert der als "kein Stromfluss" anerkannt wird
29
float         nullStromOut = 2.0;                 // [A] Wert der als "kein Stromfluss" anerkannt wird
30
float         maxInStrom = 31.0;                  // [A] Maximaler Eingangsstrom pro Phase
31
float         maxOutStrom = 350.0;                // [A] Maximaler Ausgangsstrom
32
float         inL1;                               // Eingangsstrom Phase 1
33
float         inL2;                               // Eingangsstrom Phase 2
34
float         inL3;                               // Eingangsstrom Phase 3
35
float         inOut;                              // Ausgangsstrom
36
float         inL1SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 1
37
float         inL2SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 2
38
float         inL3SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 3
39
float         inOutSummePeriode;                  // Summe der Ausgangsströme wärend einer Messperiode
40
float         inL1SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 1
41
float         inL2SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 2
42
float         inL3SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 3
43
float         inOutSummeProzess;                  // Summe der Ausgangsströme wärend eines Prozesses
44
unsigned long inSchleife;                         // Zählt die Programmdurchläufe wärend des Schweißprozesses
45
unsigned long inSummePeriodeZeitSpeicher;
46
unsigned long lichtbogenZeitSpeicher;
47
unsigned long lichtbogenStunden;
48
bool          inEinmal;                           // Positive Flanke Schweißvorgang
49
int           inSummePeriodeSchleife;             // Zählt die Programmdurchläufe wärend einer Messperiode
50
int           lcdL1;                              // Angezeigter Wert für Phase 1
51
int           lcdL2;                              // Angezeigter Wert für Phase 2
52
int           lcdL3;                              // Angezeigter Wert für Phase 3
53
int           lcdOut;                             // Angezeigter Wert für den Ausgangsstrom
54
int           lcdErrorCode;                       // Error Code
55
int           lichtbogenSekunden;
56
int           lichtbogenMinuten;
57
58
// LCD Verbindung herstellen
59
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
60
61
62
void setup()
63
{
64
  // Ein-/Ausgangsaktivierung
65
  pinMode (i_stromL1, INPUT);
66
  pinMode (i_stromL2, INPUT);
67
  pinMode (i_stromL3, INPUT);
68
  pinMode (i_stromOut, INPUT);
69
  pinMode (o_ein1, OUTPUT);
70
  pinMode (o_ein2, OUTPUT);
71
  pinMode (24, OUTPUT);
72
73
74
  // LCD initiieren
75
  lcd.begin(16,2);
76
  lcd.home();
77
  lcd.print("Letzter Fehler:");
78
  lcd.setCursor(0, 1);
79
  lcd.print("Error ");
80
  lcd.print(EEPROM.read(0));
81
  delay(4000);
82
  
83
  // Überlaufene Zähler löschen
84
  if(EEPROM.read(10) > 60) EEPROM.write(10, 0);
85
  if(EEPROM.read(11) > 60) EEPROM.write(11, 0);
86
  if(EEPROM.read(12) > 100) EEPROM.write(12, 0);
87
  if(EEPROM.read(13) > 100) EEPROM.write(13, 0);
88
  if(EEPROM.read(14) > 200) EEPROM.write(14, 0);
89
90
91
  // Lichtbogenstandzeit auslesen und anzeigen
92
  lichtbogenSekunden = EEPROM.read(10);
93
  lichtbogenMinuten = EEPROM.read(11);
94
  lichtbogenStunden = EEPROM.read(12) + (EEPROM.read(13) * 100) + (EEPROM.read(14) * 10000);
95
  lcd.clear();
96
  lcd.print(lichtbogenStunden);
97
  lcd.print("H");
98
  lcd.setCursor(0, 1);
99
  lcd.print(lichtbogenMinuten);
100
  lcd.print("M");
101
  lcd.print("  ");
102
  lcd.print(lichtbogenSekunden);
103
  lcd.print("S");
104
  delay(4000);
105
  lcd.clear();
106
}
107
108
void loop()
109
{ 
110
  
111
  // Messwerte auslesen und verrechnen
112
  inL1SummePeriode += sq(analogRead(i_stromL1) / 13.14 - 38.92);
113
  inL2SummePeriode += sq(analogRead(i_stromL2) / 13.14 - 38.92);
114
  inL3SummePeriode += sq(analogRead(i_stromL3) / 13.14 - 38.92);
115
  inOutSummePeriode += analogRead(i_stromOut) / 2.58;
116
  inSummePeriodeSchleife ++;
117
  if(millis() - inSummePeriodeZeitSpeicher >= 200){
118
    inL1 = sqrt(inL1SummePeriode / inSummePeriodeSchleife);
119
    inL2 = sqrt(inL2SummePeriode / inSummePeriodeSchleife);
120
    inL3 = sqrt(inL3SummePeriode / inSummePeriodeSchleife);
121
    inOut = inOutSummePeriode / inSummePeriodeSchleife;
122
    inSummePeriodeZeitSpeicher = millis();
123
    inL1SummePeriode = 0;
124
    inL2SummePeriode = 0;
125
    inL3SummePeriode = 0;
126
    inOutSummePeriode = 0;
127
    inSummePeriodeSchleife = 0;
128
  }
129
130
    
131
  // Anlage auf Fehler überprüfen
132
  // Fehler: Leckstromüberwachung Phase 1 
133
  if(inL1 > nullStromIn and inOut < nullStromOut){
134
    if(millis() - error1ZeitSpeicher >= error1ZeitWert){
135
      lcdErrorCode = 1;
136
    }
137
  }
138
  // Fehler: Leckstromüberwachung Phase 2
139
  else if(inL2 > nullStromIn and inOut < nullStromOut){
140
    if(millis() - error1ZeitSpeicher >= error1ZeitWert){
141
      lcdErrorCode = 2;
142
    }
143
  }
144
  // Fehler: Leckstromüberwachung Phase 3
145
  else if(inL3 > nullStromIn and inOut < nullStromOut){
146
    if(millis() - error1ZeitSpeicher >= error1ZeitWert){
147
      lcdErrorCode = 3;
148
    }
149
  }
150
  // Entprellzeit zurücksetzen
151
  else{
152
    error1ZeitSpeicher = millis();
153
  }
154
  
155
  // Fehler: Maximaler Strom Phase 1 überschritten
156
  if(inL1 > maxInStrom){
157
    if(millis() - error4ZeitSpeicher >= error4ZeitWert){
158
      lcdErrorCode = 4;
159
    }
160
  }
161
  // Fehler: Maximaler Strom Phase 2 überschritten
162
  else if(inL2 > maxInStrom){
163
    if(millis() - error4ZeitSpeicher >= error4ZeitWert){
164
      lcdErrorCode = 5;
165
    }
166
  }
167
  // Fehler: Maximaler Strom Phase 3 überschritten
168
  else if(inL3 > maxInStrom){
169
    if(millis() - error4ZeitSpeicher >= error4ZeitWert){
170
      lcdErrorCode = 6;
171
    }
172
  }
173
  // Entprellzeit zurücksetzen
174
  else{
175
    error4ZeitSpeicher = millis();
176
  }
177
  
178
  // Fehler: Maximaler Schweißstrom überschritten
179
  if(inOut > maxOutStrom){
180
    if(millis() - error7ZeitSpeicher >= error7ZeitWert){
181
      lcdErrorCode = 7;
182
    }
183
  }
184
  // Entprellzeit zurücksetzen
185
  else{
186
    error7ZeitSpeicher = millis();
187
  }
188
  
189
  // Fehler: Überwachung L1 fehlerhaft
190
  if(inOut > nullStromOut and inL1 < nullStromIn){
191
    if(millis() - error8ZeitSpeicher >= error8ZeitWert){
192
      lcdErrorCode = 8;
193
    }
194
  }
195
  // Fehler: Überwachung L2 fehlerhaft
196
  else if(inOut > nullStromOut and inL2 < nullStromIn){
197
    if(millis() - error8ZeitSpeicher >= error8ZeitWert){
198
      lcdErrorCode = 9;
199
    }
200
  }
201
  // Fehler: Überwachung L3 fehlerhaft
202
  else if(inOut > nullStromOut and inL3 < nullStromIn){
203
    if(millis() - error8ZeitSpeicher >= error8ZeitWert){
204
      lcdErrorCode = 10;
205
    }
206
  }
207
  // Entprellzeit zurücksetzen
208
  else {
209
    error8ZeitSpeicher = millis();
210
  }
211
212
  // Schweißvorgang
213
  if(inOut > nullStromOut){
214
    // Schweißstrom auf LCD Anzeigen
215
    lcdL1 = inL1;
216
    lcdL2 = inL2;
217
    lcdL3 = inL3;
218
    lcdOut = inOut;
219
220
    // Mittelwert vom letzten Prozess zurücksetzen
221
    if(inEinmal == false){
222
      inSchleife = 0;
223
      inL1SummeProzess = 0;
224
      inL2SummeProzess = 0;
225
      inL3SummeProzess = 0;
226
      inOutSummeProzess = 0;
227
      inEinmal = true;
228
      lichtbogenZeitSpeicher = millis();
229
      }
230
231
    // Mittelwertbildung Schweißströme vorbereiten
232
    inSchleife ++;
233
    inL1SummeProzess += inL1;
234
    inL2SummeProzess += inL2;
235
    inL3SummeProzess += inL3;
236
    inOutSummeProzess += inOut;
237
238
    //Lichtbogenstandzeit
239
    if(millis() - lichtbogenZeitSpeicher >= 1000){
240
      lichtbogenSekunden ++;
241
      EEPROM.write(10, lichtbogenSekunden);
242
      if(lichtbogenSekunden >= 60){
243
        lichtbogenMinuten ++;
244
        EEPROM.write(11, lichtbogenMinuten);
245
        lichtbogenSekunden = 0;
246
        if(lichtbogenMinuten >= 60){
247
          lichtbogenStunden ++;
248
          EEPROM.write(14, lichtbogenStunden / 10000);
249
          EEPROM.write(13, (lichtbogenStunden - (EEPROM.read(14) * 10000)) / 100);
250
          EEPROM.write(12, lichtbogenStunden - ((EEPROM.read(14) * 10000) + (EEPROM.read(13) * 100)));
251
          lichtbogenMinuten = 0;
252
        }
253
      }
254
    lichtbogenZeitSpeicher = millis();
255
    }
256
  }
257
  // Kein Schweißvorgang
258
  else{
259
    // Mittelwert vom letzten Prozess anzeigen
260
    lcdL1 = inL1SummeProzess / inSchleife;
261
    lcdL2 = inL2SummeProzess / inSchleife;
262
    lcdL3 = inL3SummeProzess / inSchleife;
263
    lcdOut = inOutSummeProzess / inSchleife;
264
    inEinmal = false;
265
    }
266
267
  // Fehler Anzeige
268
  if(lcdErrorCode > 0){
269
    switch(lcdErrorCode){
270
      case 1:
271
        lcd.clear();
272
        lcd.print("Error 1");
273
        lcd.setCursor(0, 1);
274
        lcd.print("L1 Leckstrom");
275
      break;
276
      case 2:
277
        lcd.clear();
278
        lcd.print("Error 2");
279
        lcd.setCursor(0, 1);
280
        lcd.print("L2 Leckstrom");
281
      break;
282
      case 3:
283
        lcd.clear();
284
        lcd.print("Error 3");
285
        lcd.setCursor(0, 1);
286
        lcd.print("L3 Leckstrom");
287
      break;
288
      case 4:
289
        lcd.clear();
290
        lcd.print("Error 4");
291
        lcd.setCursor(0, 1);
292
        lcd.print("L1 Ueberlast");
293
      break;
294
      case 5:
295
        lcd.clear();
296
        lcd.print("Error 5");
297
        lcd.setCursor(0, 1);
298
        lcd.print("L2 Ueberlast");
299
      break;
300
      case 6:
301
        lcd.clear();
302
        lcd.print("Error 6");
303
        lcd.setCursor(0, 1);
304
        lcd.print("L3 Ueberlast");
305
      break;
306
      case 7:
307
        lcd.clear();
308
        lcd.print("Error 7");
309
        lcd.setCursor(0, 1);
310
        lcd.print("Iout Ueberlast");
311
      break;
312
      case 8:
313
        lcd.clear();
314
        lcd.print("Error 8");
315
        lcd.setCursor(0, 1);
316
        lcd.print("Sensor L1 defekt");
317
      break;
318
      case 9:
319
        lcd.clear();
320
        lcd.print("Error 9");
321
        lcd.setCursor(0, 1);
322
        lcd.print("Sensor L2 defekt");
323
      break;
324
      case 10:
325
        lcd.clear();
326
        lcd.print("Error 10");
327
        lcd.setCursor(0, 1);
328
        lcd.print("Sensor L3 defekt");
329
      break;
330
    }
331
         
332
      // Hauptschütz ausschalten
333
      digitalWrite(o_ein1, LOW);
334
      digitalWrite(o_ein2, LOW);
335
      
336
      errorEinmal = true;
337
    }
338
    delay(5000);
339
  }
340
  
341
  // Anzeige im Normalbetrieb
342
  else {
343
    lcd.setCursor(0, 0);
344
    lcd.print("IN");
345
    if(lcdL1 < 10) lcd.print("    ");
346
    else lcd.print("   ");
347
    lcd.print(lcdL1);
348
    lcd.print("A");
349
    if(lcdL2 < 10) lcd.print("  ");
350
    else lcd.print(" ");
351
    lcd.print(lcdL2);
352
    lcd.print("A");
353
    if(lcdL3 < 10) lcd.print("  ");
354
    else lcd.print(" ");
355
    lcd.print(lcdL3);
356
    lcd.print("A");
357
  
358
    lcd.setCursor(0, 1);
359
    lcd.print("OUT");
360
    if(lcdL1 < 10) lcd.print("   ");
361
    else if(lcdOut < 100)lcd.print("  ");
362
    else lcd.print(" ");
363
    lcd.print(lcdOut);
364
    lcd.print("A  ");
365
    digitalWrite(o_ein1, HIGH);
366
    digitalWrite(o_ein2, HIGH);
367
    }
368
}

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

millioner2 schrieb:
> Wenn noch irgendwelche Fragen sind immer her damit.

Ich habe jetzt bei Dir keine Frage gefunden. Ich vermutet aber mal, dass 
Du nach Möglichkeiten suchst, dass "schneller" hin zu bekommen: Das 
sieht doch ganz übersichtlich aus und solltest Du doch mit relativ wenig 
Aufwand direkt in bare metal C oder C++ umsetzen können.

von millioner2 (Gast)


Lesenswert?

Torsten R. schrieb:
> Ich habe jetzt bei Dir keine Frage gefunden. Ich vermutet aber mal, dass
> Du nach Möglichkeiten suchst, dass "schneller" hin zu bekommen:

Ja genau ich möchte gerne von 100ms auf < 2ms und dacht vieleicht sind 
hier ein paar Programmfreaks die direkt sehen wie man das viel schneller 
hinbekommen... war in der vergangenheit schon öfters so...

Torsten R. schrieb:
> Das
> sieht doch ganz übersichtlich aus und solltest Du doch mit relativ wenig
> Aufwand direkt in bare metal C oder C++ umsetzen können.

Ich versuche übersichtlich zu schreiben ja aber ich weiß nicht was du 
damit meinst ist "bare metal" ein uC?

von Harry L. (mysth)


Lesenswert?

Ersetzt mal deine ganzen float-Geschichten durch Festkommaarithmetik.
float ist auf AtMegas einfach nur lahm.
https://www.mikrocontroller.net/articles/Festkommaarithmetik

von Sönke P. (snke_p)


Lesenswert?

Schreibe nur auf das LCD, wenn sich Daten geändert haben. Wenn das immer 
noch zu langsam ist, dann aktualisiere die Daten nur alle paar 
loop()-Durchläufe vollständig (ggf. auch zeilenweise).
Das LCD ist hier klar der Flaschenhals. Ggf. einen anderen Typ 
einsetzen, nicht per I2C, sondern parallel anbinden.

Schau dir mal an, wie oft die EEPROM-Operationen durchgeführt werden und 
minimiere auch die Schreiboperationen, das geht auf die Lebensdauer 
dieses Speichers.

Dann prüfe, ob du Berechnungen vereinfachen kannst. Momentan aast du mit 
Float und Long Datentypen nur so rum. Das ist für so einen 
8-bit-Controller Gift.

Das Lesen vom ADC kannst du auch in eine Interrupt-Routine verlegen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dein Programm wird deswegen so "langsam" sein, weil Du viel 
floatingpoint-Arithmetik betreibst. Das aber ist für einen 8-Bit-AVR 
(wie er auf den üblichen Arduinos verbaut ist) ziemlicher Aufwand.

Versuche Deine gesamten Berechnungen auf ints umzustellen, und 
transformiere in float höchstens, um einen Wert für die Displayausgabe 
zu erhalten.

Natürlich kannst Du Deine float-Variablen nicht einfach 1:1 in ints 
konvertieren; Du musst den endlichen Wertebereich von int durch 
geschickte Skalierung sinnvoll ausnutzen.
1
  inL1SummePeriode += sq(analogRead(i_stromL1) / 13.14 - 38.92);

Hier musst Du eine Betrachtung anstellen - was ist der kleinste zu 
erwartende Wert und was ist der größte zu erwartende Wert? Wird der 
volle Umfang des ADC ausgenutzt, oder sogar nur ein Teil davon?
1
    inL1 = sqrt(inL1SummePeriode / inSummePeriodeSchleife);

Und das ist natürlich tödlich in Sachen Rechenzeit. Eine Quadratwurzel. 
Das dauert Äonen. Versuche, auf solche Operationen zu verzichten.

von Theor (Gast)


Lesenswert?

millioner2 schrieb:
> Torsten R. schrieb:
> Torsten R. schrieb:
>> Das
>> sieht doch ganz übersichtlich aus und solltest Du doch mit relativ wenig
>> Aufwand direkt in bare metal C oder C++ umsetzen können.
>
> Ich versuche übersichtlich zu schreiben ja aber ich weiß nicht was du
> damit meinst ist "bare metal" ein uC?

Mit "bare metal" ist, im Zusammenhang mit Arduino, gemeint: Ohne das 
Arduino-Framework. Das besteht aus Programmierersicht vor allem aus der 
Verwendung von Funktionen wie Setup und loop. Ich bin darüber nicht im 
einzelnen orientiert, aber ich nehme an, dass zwischen den Aufrufen von 
loop noch allerhand im Hintergrund passiert, das mit der Ansteuerung von 
ADC, Port-pins und Entprellung zu tun hat. Das müsste auf den 
Arduino-Foren vermutlich mehr im Detail diskutiert werden.

Ohne Arduino-Framework würde bedeuten, dass Du eine einge main-Funktion 
schreibst und die INitialisierung der Peripherie, sowie das holen der 
Messwerte selbst hinschreiben musst. (Das erledigt eben das nicht-"bare 
metal"-Arduino-Framework für Dich. Da es aber alle möglichen Anwendungen 
erschlagen muss und nicht für eine spezielle Anwendung optimiert ist, 
braucht es mehr Zeit und mehr Code als vielleicht notwendig).

Ich denke aber auch, dass der eigentliche Show-Stopper die 
float-Arithmetik ist. (Ob man mit Arduino und dessen Framework 
Abtastezeiten von 2ms für vier Werte erreichen kann, weiss ich nicht).

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

millioner2 schrieb:
> Ich versuche übersichtlich zu schreiben ja aber ich weiß nicht was du
> damit meinst ist "bare metal" ein uC?

Nein, das würde bedeuten, dass Du auf der selben Hardware die Zugriffe 
auf die Hardware selbst implementierst und nicht Arduino dafür 
verwendest. Dann wärest Du in der Lage, vieles über Interrupts zu 
implementieren und damit nur noch auf Änderungen zu reagieren.

Mir war aber nicht klar, dass die Hardware nur ein 8-Bit Controller ist. 
Dann würde ich Dir vorschlagen doch einfach zu dickerer Hardware zu 
greifen.

von millioner2 (Gast)


Lesenswert?

Harry L. schrieb:
> Ersetzt mal deine ganzen float-Geschichten durch Festkommaarithmetik.
> float ist auf AtMegas einfach nur lahm.
> https://www.mikrocontroller.net/articles/Festkommaarithmetik

Das ist doch schonmal eine gute Aussage damit fang ich an alles wird in 
INT und LONG umgewandelt bis kein einiger Float mehrda ist.

Sönke P. schrieb:
> Schreibe nur auf das LCD, wenn sich Daten geändert haben. Wenn das immer
> noch zu langsam ist, dann aktualisiere die Daten nur alle paar
> loop()-Durchläufe vollständig (ggf. auch zeilenweise).
> Das LCD ist hier klar der Flaschenhals. Ggf. einen anderen Typ
> einsetzen, nicht per I2C, sondern parallel anbinden.

Ich habe gerade eben mal alles was bildschirm ist rausgeschmissen, inkl. 
Lib. und komme auf 20ms aber wie oben schon gesagt der muss nicht 
zwingend wärend dessen laufen da kann ich drauf verzichten geht 
eigentlich nur um die Fehleranzeige und dann ist ja jede Zeit der Welt.

Sönke P. schrieb:
> Schau dir mal an, wie oft die EEPROM-Operationen durchgeführt werden und
> minimiere auch die Schreiboperationen, das geht auf die Lebensdauer
> dieses Speichers.

Damit bin ich selber auch nicht zufrieden und wird auch noch geändert 
der EEPROM 10 wird im moment alle 1Sek beschrieben, der wird nicht lange 
halten. Aber auch diesen Punkt habe ich für die zweite Messung 
rausgeschmissen und wie oben geschrieben 20ms.

Sönke P. schrieb:
> Das Lesen vom ADC kannst du auch in eine Interrupt-Routine verlegen.

Was soll das bringen wenn ich un unterbrochen signale wärend des 
laufenden Prozesses rein bekomme das überschlägt sich doch mein 
Interrupt nur... oder nicht?

von millioner2 (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> inL1 = sqrt(inL1SummePeriode / inSummePeriodeSchleife);
>
> Und das ist natürlich tödlich in Sachen Rechenzeit. Eine Quadratwurzel.
> Das dauert Äonen. Versuche, auf solche Operationen zu verzichten.

Ich wüsste aber nicht wie ich einen Effektivwert ohne Quadratwurzel 
ziehen soll... wenn du ne idee hast bin für alles offen und danke für 
deine restliche Erkärung !

von Einer K. (Gast)


Lesenswert?

Theor schrieb:
> dass zwischen den Aufrufen von
> loop noch allerhand im Hintergrund passiert, das mit der Ansteuerung von
> ADC, Port-pins und Entprellung zu tun hat. Das müsste auf den
> Arduino-Foren vermutlich mehr im Detail diskutiert werden.
Können wir auch gerne hier machen....

Zwischen den Loop Aufrufen:
Einzig der Aufruf von https://www.arduino.cc/en/Reference/SerialEvent .
Und auch das nur, wenn diese definiert ist.

Ansonsten läuft Timer0, um den millis() Wert hoch zu zählen.

Das wars....

von millioner2 (Gast)


Lesenswert?

Ich muss euch jetzt mal Danken für die vielen echt tollen Erklärungen 
werde über die Nacht mal versuchen alles um zu setzten und mich morgen 
nach der Arbeit wieder mit den Ergebnissen melden!

** THANKS **

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

millioner2 schrieb:
> Ich wüsste aber nicht wie ich einen Effektivwert ohne Quadratwurzel
> ziehen soll... wenn du ne idee hast bin für alles offen und danke für
> deine restliche Erkärung !

Dich interessiert der Wert ja nicht direkt, sondern nur, ob er eine 
Schwelle überschreitet. Nun kannst Du ja auch überprüfen, ob der Wert 
das Quadrat einer Schwelle überschreitet. Was insbesondere dann viel 
günstiger ist, wenn die Schwelle eine Konstante ist:
1
 inL1 = sqrt(inL1SummePeriode / inSummePeriodeSchleife);
2
3
...    
4
  // Anlage auf Fehler überprüfen
5
  // Fehler: Leckstromüberwachung Phase 1 
6
  if(inL1 > nullStromIn and inOut < nullStromOut){
1
 inL1 = inL1SummePeriode / inSummePeriodeSchleife;
2
3
...    
4
  // Anlage auf Fehler überprüfen
5
  // Fehler: Leckstromüberwachung Phase 1 
6
  if(inL1 > nullStromIn*nullStromIn and inOut < nullStromOut){

von Manfred (Gast)


Lesenswert?

millioner2 schrieb:
> lcd.print("Error 4");
Wenn ich mich recht erinnere, dauert das Beschreiben des LCD per I2C um 
20ms pro Zeile.

millioner2 schrieb:
> Ich wüsste aber nicht wie ich einen Effektivwert ohne Quadratwurzel
> ziehen soll...
Das ist mir nicht ganz klar, zwischen Spitze und Effektiv herrscht doch 
eine feste Beziehung SQR(2) = 1,41.. ?

Andersherum ist mir nicht klar, wie Du Wechselspannung zuverlässig 
messen willst, ohne über einen längeren Zeitraum abzutasten - Du hast 
doch nicht unter Kontrolle, bei welcher Phasenlage Deine ADC-Abfrage 
startet.

von Felix F. (wiesel8)


Lesenswert?

millioner2 schrieb:
> Rufus Τ. F. schrieb:
>> inL1 = sqrt(inL1SummePeriode / inSummePeriodeSchleife);
>>
>> Und das ist natürlich tödlich in Sachen Rechenzeit. Eine Quadratwurzel.
>> Das dauert Äonen. Versuche, auf solche Operationen zu verzichten.
>
> Ich wüsste aber nicht wie ich einen Effektivwert ohne Quadratwurzel
> ziehen soll... wenn du ne idee hast bin für alles offen und danke für
> deine restliche Erkärung !
Ich habe mir dein Programm nicht angesehen, aber ich gehe mal nicht 
davon aus dass du deine Wurzel auf die 1000ste Stelle genau haben musst. 
Dementsprechend könntest du eine eigene sqrt()-Funktion implementieren, 
die nicht so genau ist, dafür schneller.

Aber du solltest erstmal die Flaschenhälse in deinem Programm 
identifizieren (Ich benutze kein Arduino, kenne also den Overhead 
nicht). Es macht nämlich mehr Sinn ein 5ms Codestück um 20% zu 
optimieren, als ein 1ms Codestück um 50%.

mfg

von millioner2 (Gast)


Lesenswert?

Manfred schrieb:
> Andersherum ist mir nicht klar, wie Du Wechselspannung zuverlässig
> messen willst, ohne über einen längeren Zeitraum abzutasten - Du hast
> doch nicht unter Kontrolle, bei welcher Phasenlage Deine ADC-Abfrage
> startet.

Genau das mache ja, ich messe über eine dauer von 200ms so viele werte 
wie eben geht mit dem selben abstand zwischen den werten. Da 200ms ein 
Teiler von 20ms = T ist sollte ich einfach den Quadatischenmittelwert 
bilden können ohne zu wissen wo ich in der Phase angefangen habe, da ich 
ja auch dort wieder ende.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

EEprom schreiben kostet Zeit, warum schreibst du nicht in eine 
RAM-Variable?

lcd.clear kostet viel Zeit, cursor setzen und überschreiben ist 
schneller.
Das LCD würde ich auch nicht permanent mit jedem loop Durchlauf 
beschreiben.
Ich würde mit millis aller >= 1s das Display aktualisieren.

Die Formeln könnte man bestimmt optimieren, wie gesagt wurde.

von Karl M. (Gast)


Lesenswert?

Hallo,

ein weiterer Flaschenhals ist der I2C Zugriff auf das LCD, hier 
blockiert die Ausgabe.
Wenn man einen direkte Zugriff auf das LCD, also ohne I2C dazwischen 
implementiert, und die Ausgabe innerhalb eines 1ms Timerinterrupts 
erfolgt, so muss man kaum noch LCD-Wartzeiten einplanen.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Zum Wurzelziehen:
http://www.diaware.de/html/wurzel.html
oder auch hier:
http://www.umnicom.de/Elektronik/Mikrokontroller/Atmel/AtSoft/Wurzel/AtWurzel.html

Weiter kann man mit (nahezu) beliebig großen Zahlen jonglieren - so 
teilt mir eine Routine auf einem ATtiny45 eine 32-bit-Zahl durch eine 
8-bit-Zahl.
Wenn Das nicht reicht, braucht die Routine ein(oder zwei...) Register 
mehr und macht ein paar Zusatzrunden.

Allerdings sind, zumindest akut bei meinem ATtiny45, 1ms auch nur 1000 
Programmschritte (oder weniger bei Sprüngen ect.pp.) @1MHz

Vll. helfen Dir oder einem Anderen ja die Wurzel-Routinen.
Bei Dir ist ja, wie bereits geschrieben wurde, der 'menschen lesbare 
Wert' ja erst nötig, wenn Dieser von einem Menschen gelesen werden soll 
- also auf dem Display.

MfG

von Andreas R. (daybyter)


Lesenswert?

http://www.stm32duino.com

wäre evtl noch eine Option. Arduino Code läuft auf stm32 ARM 
Controllern. Viel Leistung für relativ wenig Geld.

von chris_ (Gast)


Lesenswert?

Stichwort EEPROM:
Angenommen, das EEPROM erlaubt 100.000 Schreibzyklen. Dann ergibt sich 
bei einer Zykluszeit von 100ms

100000/(0.1*3600)~278 Stunden Lebensdauer.

Wenn das reicht ....

von chris_ (Gast)


Lesenswert?

Noch ein kleiner Hinweis zum Speed-Profiling:
Meiner Einschätzung nach ist der Zeitfresser weder die 
Floating-Pointer-Berechnung noch die hier mystifizierten Arduino 
Setup/Loop Funktionen( die sind schnell ) und im übrigen kann man auch 
in "loop" ein while(1) einfügen falls man da anderer Ansicht sein 
sollte.

Das Problem sind die Interface-Funktionen

- EEPROM
- LCD

Ich tippe mal als größter Zeitfresser auf das EEPROM. Zum Test einfach 
auskommentieren oder durch RAM-Variablen ersetzen und Zeit messen.

von Bastian W. (jackfrost)


Lesenswert?

Wenn du die Werte speichern willst , so dass die auch ohne Strom nicht 
weg sind, dann schau dir beim Mouser mal die FRAMs mal an. Die haben 
10^14 Schreib/Lesezyklen. Sind bei 1kHz 3100 Jahre. Die haben nicht so 
viel Platz aber ggf reichen dir ja 512 Byte. Über die Sammelbestellung 
hier im Markt kommst du auch günstig an die Sachen vom Mouser.

Gruß JackFrost

von Pandur S. (jetztnicht)


Lesenswert?

Wenn man Variablen Speichern will, die halten kann man auch die Speisung 
halten und RAM verwenden. Wenn man nur die CPU speizt, dh LCD und 
anderes nicht, kommt man mit mA durch. Wenn's jetzt nicht ein Arduino 
sein muss, wuerd ich einen AVR mit externem Memory vorschlagen, Ein SRAM 
mit 64k zieht fast keinen Strom im Betrieb, waehrend Idle nichts.

Ohne jetzt den Code genau angeschaut zu haben darf es keine Delays 
geben, und gewartet werden darf auch nirgendwo. Das erledigt man mit 
einer Zustandsmaschine (Statemachine).

Allenfalls ist der ADC des AVR zu langsam. Je schneller man ihn laufen 
laesst, desto mehr Muell produziert er. Ausser man schaut auf eine 
exterm saubere Speisung.
Fuer die Leistungsmessung, hier 3 Phasen AC kann man auch gleich einen 
spezialisieren Leistungsmess chip verwenden. zB einen ADE7758, oder 
aehnlich. Nennt sich energy measurement. Von dieser Serie gibt es viele 
verschiedene. Strommessung ist meistens per Stromtransformer.

Einen LCD kann man hoechstens 2mal pro Sekunde per Text ablesen, per 
Bargraph schneller. Der Einfachheit halber beschreibt man den ganzen LDC 
aufs mal ab einem Buffer. Aufs mal bedeutet jeden Tick, dem Systemtakt, 
von 1ms oder so, ein Zeichen. Dnn muss man weder warten, noch ein Ready 
auswerten. Ein Byte pro Tick.Vergiss Graphik LCD's dafuer benoetigt man 
einen 32 bit controller wenn man etwas Fluesstiges will.

von Falk B. (falk)


Lesenswert?

@ millioner2 (Gast)

>zufrieden. Nun wollte ich gerade das gesamte Systen in betrieb nehmen
>und mein Programmcode ist viel zu langsam...

Tja, sowas kommt öfter vor ;-)

>Ich muss mit drei Hallsensoren Wechselströme von 0-30A +/-1A messen.
>Außerdem einen 400A +/-3A Gleichstrom über einen Shunt mit
>Messvorverstärker.

Hmm.

>Im Fehlerfall soll sofort der Schweißprozess unterbrochen werden.

Was ist sofort? In 1ms? 1s?

>Ich denke um sauber einen 50Hz Sinus messen zu können sollte es schon
>maximal 2ms pro Zyklus sein, schätze ich.

Das wären ja gerade mal 10 Abtastungen/Periode, da wird die Messung 
schon arg unrund.

>Im moment sind es knapp 100ms pro Zyklus. Ich möchte gerne den
>bisherigen elektischen Aufbau erhalten. Als Arduino nutze ich einen
>Arduino Mega 2560 R3.

;-)

Der AVR schafft das, wenn man weiß was man tut.

>Wenn noch irgendwelche Fragen sind immer her damit.

Zuerst mal ein Anschiß!

Lange Quelltexte gehören in den Anhang! Siehe Netiquette.

Wie bereits mehrfach gesagt.

1.) Alle Fließkommaoperationen entfernen und auf Feskommaarithmetik 
umstellen. Dabei reichen 16 Bit Variablen für die ADC-Werte locker aus, 
bestenfalls die Akkus für die Mittelwerte brauchen 32 Bit long. 
Wahrscheinlich reichen auch 8 Bit für die ADC-Werte.
2.) den Käse mit millis() etc. wegferfen. Hier braucht man eine normale 
Timer-ISR, das geht auch mit der Arduino-Umgebung. Also Timer 1 mit 1 
Milisekunde im CTC Modus einstellen und los. Dort drin läuft dein 
schnelles Programm mit Datenerfassung und Berechnung.
3.) Die LCD-Ausgabe als "Nebengeschäft" in der Hauptschleife ( loop() 
)laufen lassen und nur alle paar hundert ms aktualisieren, siehe 
Interrupt.
4.) Aufwändige mathematische Operationen wie Division und Wurzel 
möglichst vermeiden. Eine Division kann man durch rechtschieben 
ersetzen, wenn man den Divisor als 2er Potenz auslegt, also 2, 4, 8, 16 
etc. Die Wurzel kann man über eine Tabelle "ausrechnen", das ist sehr 
schnell und ausreichend genau.
5.) Durch geschicktes Verschachteln von ADC-Auslesen und Berechnung kann 
man einiges an Zeit sparen bzw. vertrödelt die CPU dann keine sinnlose 
Zeit.

Etwa so.

ADC-Messung für Kanal 1 starten
Auf Ende der ADC-Messung warten
ADC-Ergebnis 1 auslesen
ADC-Messung für Kanal 2 starten
ADC-Ergebnis für Kanal 1 verarbeiten (quadrieren und akkumulieren)
Auf Ende der ADC-Messung warten
ADC-Ergebnis 2 auslesen
ADC-Messung für Kanal 3 starten
ADC-Ergebnis für Kanal 2 verarbeiten (quadrieren und akkumulieren)
Auf Ende der ADC-Messung warten
ADC-Ergebnis 3 auslesen
ADC-Ergebnis für Kanal 2 verarbeiten (quadrieren und akkumulieren)

So arbeiten CPU und ADC zumindest zwei mal parallel, beim erstem Mal 
geht es halt nicht.

All diese Dinge bedeuten aber, daß man nicht alle Arduino-Funktionen 
benutzen kann und selber direkt auf die IO-Register zugreifen muss. Der 
Lohn für die Mühe ist ein schnelles Programm ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Da das Programm syntaktisch fehlerhaft ist (die Anzahl der öffnenden und
schließenden geschweiften Klammern stimmt nicht überein), macht es wenig
Sinn, über Laufzeiten zu diskutieren, da überhaupt nicht klar ist,
welche Codeabschnitte in jedem Schleifendurchlauf und welche nur bedingt
(z.B. nur alle 200ms) ausgeführt werden.

Poste das Programm doch noch einmal in kompilierbarer Form, wegen der
Größe am besten als Anhang.

Mein Gefühl sagt mir, dass das Ganze mit etwas Nachdenken auch in
FLoating-Point ausreichend schnell implementiert werden kann.

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> 5.) Durch geschicktes Verschachteln von ADC-Auslesen und Berechnung kann
> man einiges an Zeit sparen bzw. vertrödelt die CPU dann keine sinnlose
> Zeit.

Richtig!

analogRead() wartet jedes mal bis der Wert parat ist.

Das lässt sich auch nebenläufig erledigen. Dafür hat Atmel die ISRs 
eingeplant.

von Cyblord -. (cyblord)


Lesenswert?

Arduino F. schrieb:

> analogRead() wartet jedes mal bis der Wert parat ist.
>
> Das lässt sich auch nebenläufig erledigen. Dafür hat Atmel die ISRs
> eingeplant.

Gibt's dafür ein ISR-Shield, oder wie soll ein Arduino-Nutzer damit 
arbeiten?

von Huh (Gast)


Lesenswert?

Cyblord -. schrieb:
> Arduino F. schrieb:
>
>> analogRead() wartet jedes mal bis der Wert parat ist.
>>
>> Das lässt sich auch nebenläufig erledigen. Dafür hat Atmel die ISRs
>> eingeplant.
>
> Gibt's dafür ein ISR-Shield, oder wie soll ein Arduino-Nutzer damit
> arbeiten?

Gibt's in der Arduino-Software keine ISR? Wäre ja echt blöd!

von Falk B. (falk)


Lesenswert?

@Huh (Gast)

>Gibt's in der Arduino-Software keine ISR?

Nicht wirklich.

> Wäre ja echt blöd!

Die primäre Zielgruppe braucht sie nicht und kann damit auch nicht 
umgehen. Wer schon etwas weiter ist, kann die einfach selber 
hinschreiben und nutzen, das unterscheidet sich exakt NULL von normalem 
C im Atmelstudio, denn es ist ja der gleiche avr gcc Compiler.

von Einer K. (Gast)


Lesenswert?

Huh schrieb:
> Gibt's in der Arduino-Software keine ISR? Wäre ja echt blöd!
Reingefallen!


Falk B. schrieb:
> Wer schon etwas weiter ist, kann die einfach selber hinschreiben
> und nutzen, ... denn es ist ja der gleiche avr gcc Compiler.
So ist es!

Und, ich denke, dass das hier auch Sinn macht.

von Cyblord -. (cyblord)


Lesenswert?

Arduino F. schrieb:
> Huh schrieb:
>> Gibt's in der Arduino-Software keine ISR? Wäre ja echt blöd!
> Reingefallen!

Sieht das Arduino-Framework nun ISRs vor oder nicht?

> Falk B. schrieb:
>> Wer schon etwas weiter ist, kann die einfach selber hinschreiben
>> und nutzen, ... denn es ist ja der gleiche avr gcc Compiler.
> So ist es!
>
> Und, ich denke, dass das hier auch Sinn macht.

Und ich denke das man da sehr schnell Probleme bekommt, weil Arduino 
intern sicher ISRs nutzt (denke da an die Serial FIFOs) und sich das 
dann überschneiden kann. Ebenso mit aller anderen Peripherie die man am 
Framework vorbei anspricht (z.B. Timer).

von Einer K. (Gast)


Lesenswert?

Cyblord -. schrieb:
> Und ich denke das man da sehr schnell Probleme bekommt, weil Arduino
> intern sicher ISRs nutzt (denke da an die Serial FIFOs) und sich das
> dann überschneiden kann. Ebenso mit aller anderen Peripherie die man am
> Framework vorbei anspricht (z.B. Timer).

Auch analogRead() wird durch diese ISRs unterbrochen.
Ansonsten sehe ich da keine Probleme.

timer0 wird für millis genutzt, die andern Timer sind frei.

Welche Probleme hättest du da gerne?

Der Mann hat die Sorge, dass seine Zykluszeiten zu lang sind.
Und das kann man verbessern, wenn man möglichst auf Polling verzichtet.

analogRead() betreibt polling

Und TWI, also Wire, auch.
Da wäre also noch ein weiterer Ansatzpunkt.

Cyblord -. schrieb:
> Sieht das Arduino-Framework nun ISRs vor oder nicht?
Ja klar!
Timer0 immer
Serial, wenn genutzt
TWI/Wire und SPI, intern

Man kann die Arduino IDE überreden, auf das ganze Arduino spezifische 
Zeugs zu verzichten. Das geht mit einem "Fingerschippen". Natürlich 
verliert man dann den ganzen Komfort.

von Cyblord -. (cyblord)


Lesenswert?

Arduino F. schrieb:
> Cyblord -. schrieb:
>> Und ich denke das man da sehr schnell Probleme bekommt, weil Arduino
>> intern sicher ISRs nutzt (denke da an die Serial FIFOs) und sich das
>> dann überschneiden kann. Ebenso mit aller anderen Peripherie die man am
>> Framework vorbei anspricht (z.B. Timer).
>
> Auch analogRead() wird durch diese ISRs unterbrochen.
> Ansonsten sehe ich da keine Probleme.

Es geht darum wenn das Framework einen ISR bereits selbst nutzt.

> timer0 wird für millis genutzt, die andern Timer sind frei.
Muss man aber wissen und woher weißt du dass das über alle Erweiterungen 
und Sketche so bleibt?

> Man kann die Arduino IDE überreden, auf das ganze Arduino spezifische
> Zeugs zu verzichten. Das geht mit einem "Fingerschippen". Natürlich
> verliert man dann den ganzen Komfort.
Eben, du redest aber davon den Komfort zu haben + noch was daran vorbei 
zu realisieren. Freilich wenn man ALLES rausschmeißt kann man machen was 
man will, aber dann isses auch kein Arduino mehr.

Und alles was man angeblich mit einem "Fingerschnippen" machen kann, 
würde ich mal hinterfragen. Meist geht das nur zwischen Mitternacht und 
Null Uhr, und bei allem anderen schlägt man dann wieder hier auf und 
nichts geht mehr.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Cyblord -. schrieb:
> und bei allem anderen schlägt man dann wieder hier auf und
> nichts geht mehr.
Dafür übernehme ich keine Verantwortung!

> Es geht darum wenn das Framework einen ISR bereits selbst nutzt.
Ich wiederhole:
Seriell, Timer0, und I2C
Die anderen Interrupts sind hier ungenutzt

Cyblord -. schrieb:
> Muss man aber wissen und woher weißt du dass das über alle Erweiterungen
> und Sketche so bleibt?
Wenn man weitere Libs nutzt, mögen auch weitere ISR genutzt werden!
Ja, das ist so. Und das ist auch gut und richtig so.
Aber hier nicht der Fall.

Und woher ich das weiß?
Weil ich mir, erstens, die Innereien angesehen habe. Liegt alles im 
Quellcode vor.
Und weil mir, zweitens, meine Arduino IDE bei jeder Kompilation ein 
Disassembler Listing auswirft. Und ja, das kann ich lesen.

von Cyblord -. (cyblord)


Lesenswert?

Arduino F. schrieb:
> Wenn man weitere Libs nutzt, mögen auch weitere ISR genutzt werden!
> Ja, das ist so. Und das ist auch gut und richtig so.
> Aber hier nicht der Fall.

Aber darum gings bei meinen Einwänden. Pauschal zu empfehlen, nach Lust 
und Laune am Framework vorbei Peripherie zu nutzen, kann eben große 
Probleme machen. Du kannst dich hier nicht auf den speziellen Fall 
zurückziehen. Es geht darum obs allgemein problemlos geht.
Und da ist die Antwort nun mal NEIN. Je nach Sketchen kann es zu 
erheblichen Konflikten kommen.

> Und woher ich das weiß?
> Weil ich mir, erstens, die Innereien angesehen habe. Liegt alles im
> Quellcode vor.
> Und weil mir, zweitens, meine Arduino IDE bei jeder Kompilation ein
> Disassembler Listing auswirft. Und ja, das kann ich lesen.

Schön für dich. Ein toller Workflow. Langsam hast du mich von Arduino 
überzeugt. Normalerweise zu langsam, aber nach intensivem Studium der 
Codes und Listings kann man dann ja "einfach" und "mit einem 
Fingerschnippen", daran vorbei erweitern. DANKE!

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

> Langsam hast du mich von Arduino überzeugt.
Da habe ich überhaupt kein Interesse dran!

Cyblord -. schrieb:
> kann man dann ja "einfach" und "mit einem
> Fingerschnippen", daran vorbei erweitern.
Habe ich das gesagt?
Nee: Das hast du dir ausgedacht.

von millioner2 (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

wie versprochen habe ich nun die "einfachsten" von euren Ideen umgesetzt 
und muss noch mal allen vielen Dank sagen.

Um es mal vor weg zu nehmen ich bin jetzt viel schneller als ich muss. 
Von Ursprünglich 100ms auf 200uS! (Siehe Bild)

Die sachen zum Thema Bildschirm sind zwar korrekt aber den hatte ich ja, 
da er nicht zwingend Notwendig ist, wie oben beschrieben auskommentiert.
Die größten Bremsen waren in absteigender Rheinfolge:
- Rechnen mit Float-Variabeln
- Wurzelziehen
- Errechnen der Lichtbogenzeit

Da ich jetzt alles so verlegt habe, das diese Sachen alle wärend des 
Prozesses nicht benutzt werden, bin ich jetzt halt deutlich schneller.

Leider weiß ich nicht wie ich hier .ino Datein hochladen darf, daher 
mein Programm nochmal als Code.

[CLOSED]
1
// Laden von externen Quellen
2
#include <EEPROM.h>
3
#include <Wire.h> 
4
#include <LiquidCrystal_I2C.h>
5
6
7
// Ein-/Ausgangsbelegung
8
#define i_stromL1     A0
9
#define i_stromL2     A1
10
#define i_stromL3     A2
11
#define i_stromOut    A3
12
13
#define o_ein1        22
14
#define o_ein2        23
15
16
17
// Variabeln
18
unsigned long error1ZeitSpeicher;                 // Zeitzwischenspeicher
19
int           error1ZeitWert = 500;               // [ms] Entprellzeit für Error 1-3
20
unsigned long error4ZeitSpeicher;                 // Zeitzwischenspeicher
21
int           error4ZeitWert = 500;               // [ms] Entprellzeit für Error 4-6
22
unsigned long error7ZeitSpeicher;                 // Zeitzwischenspeicher
23
int           error7ZeitWert = 500;               // [ms] Entprellzeit für Error 7
24
unsigned long error8ZeitSpeicher;                 // Zeitzwischenspeicher
25
int           error8ZeitWert = 500;               // [ms] Entprellzeit für Error 8-10
26
bool          errorEinmal;
27
28
29
unsigned long inL1SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 1 mal 10
30
unsigned long inL2SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 2 mal 10
31
unsigned long inL3SummePeriode;                   // Summe der Eingangsströme wärend einer Messperiode Phase 3 mal 10
32
unsigned long inOutSummePeriode;                  // Summe der Ausgangsströme wärend einer Messperiode mal 10
33
unsigned long inL1SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 1 mal 10
34
unsigned long inL2SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 2 mal 10
35
unsigned long inL3SummeProzess;                   // Summe der Eingangsströme wärend eines Prozesses Phase 3 mal 10
36
unsigned long inOutSummeProzess;                  // Summe der Ausgangsströme wärend eines Prozesses mal 10
37
unsigned long inSummePeriodeZeitSpeicher;
38
unsigned long lichtbogenZeitSpeicher;
39
int           lcdL1;                              // [A] Angezeigter Wert für Phase 1
40
int           lcdL2;                              // [A] Angezeigter Wert für Phase 2
41
int           lcdL3;                              // [A] Angezeigter Wert für Phase 3
42
int           lcdOut;                             // [A] Angezeigter Wert für den Ausgangsstrom
43
int           lcdErrorCode;                       // Error Code
44
int           lichtbogenSekunden;                 // [S] Schweißzeit seit Bau der Anlage
45
int           lichtbogenMinuten;                  // [M] Schweißzeit seit Bau der Anlage
46
unsigned int  lichtbogenStunden;                  // [H] Schweißzeit seit Bau der Anlage
47
int           lichtbogenMinutenNeu;               // [S] Schweißzeit des letzten Prozesses
48
int           nullStromIn = 10;                   // [1/10A] Wert der als "kein Stromfluss" anerkannt wird
49
int           nullStromOut = 20;                  // [1/10A] Wert der als "kein Stromfluss" anerkannt wird
50
int           maxInStrom = 310;                   // [1/10A] Maximaler Eingangsstrom pro Phase
51
int           maxOutStrom = 3500;                 // [1/10A] Maximaler Ausgangsstrom
52
int           inL1;                               // [A] Eingangsstrom Phase 1
53
int           inL2;                               // [A] Eingangsstrom Phase 2
54
int           inL3;                               // [A] Eingangsstrom Phase 3
55
int           inOut;                              // [A] Ausgangsstrom
56
int           inL1Q;                              // Eingangsstrom Phase 1 zum Quadrat
57
int           inL2Q;                              // Eingangsstrom Phase 2 zum Quadrat
58
int           inL3Q;                              // Eingangsstrom Phase 3 zum Quadrat
59
int           inSummePeriodeSchleife;             // Zählt die Programmdurchläufe wärend einer Messperiode
60
int           inSummeProzessSchleife;             // Zählt die Programmdurchläufe wärend des Schweißprozesses
61
bool          inEinmal;                           // Positive Flanke Schweißvorgang           
62
bool          lcdNeu = true;                      // Positive Flanke neue Werte für LCD
63
64
65
// LCD Verbindung herstellen
66
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
67
68
69
void setup()
70
{
71
  // Ein-/Ausgangsaktivierung
72
  pinMode (i_stromL1, INPUT);
73
  pinMode (i_stromL2, INPUT);
74
  pinMode (i_stromL3, INPUT);
75
  pinMode (i_stromOut, INPUT);
76
  pinMode (o_ein1, OUTPUT);
77
  pinMode (o_ein2, OUTPUT);
78
  pinMode (24, OUTPUT);
79
80
81
  // LCD initiieren
82
  lcd.begin(16,2);
83
  lcd.home();
84
  lcd.print("Letzter Fehler:");
85
  lcd.setCursor(0, 1);
86
  lcd.print("Error ");
87
  lcd.print(EEPROM.read(0));
88
  delay(4000);
89
90
  // Letzte Fehler an PC übergeben
91
  Serial.begin(9600);
92
  Serial.print("1:  ");
93
  Serial.println(EEPROM.read(0));
94
  Serial.print("2:  ");
95
  Serial.println(EEPROM.read(1));
96
  Serial.print("3:  ");
97
  Serial.println(EEPROM.read(2));
98
  Serial.print("4:  ");
99
  Serial.println(EEPROM.read(3));
100
  Serial.print("5:  ");
101
  Serial.println(EEPROM.read(4));
102
  Serial.print("6:  ");
103
  Serial.println(EEPROM.read(5));
104
  Serial.print("7:  ");
105
  Serial.println(EEPROM.read(6));
106
  Serial.print("8:  ");
107
  Serial.println(EEPROM.read(7));
108
  Serial.print("9:  ");
109
  Serial.println(EEPROM.read(8));
110
  Serial.print("10: ");
111
  Serial.println(EEPROM.read(9));
112
113
  
114
  // Überlaufene Zähler löschen
115
  if(EEPROM.read(11) > 60) EEPROM.write(11, 0);
116
  if(EEPROM.read(12) > 100) EEPROM.write(12, 0);
117
  if(EEPROM.read(13) > 250) EEPROM.write(13, 0);
118
119
120
  // Lichtbogenstandzeit auslesen und anzeigen
121
  lichtbogenSekunden = EEPROM.read(10);
122
  lichtbogenMinuten = EEPROM.read(11);
123
  lichtbogenStunden = EEPROM.read(12) + (EEPROM.read(13) * 100) + (EEPROM.read(14) * 10000);
124
  lcd.clear();
125
  lcd.print(lichtbogenStunden);
126
  lcd.print("H");
127
  lcd.setCursor(0, 1);
128
  lcd.print(lichtbogenMinuten);
129
  lcd.print("M");
130
  lcd.print("  ");
131
  lcd.print(lichtbogenSekunden);
132
  lcd.print("S");
133
  delay(4000);
134
  lcd.clear();
135
    
136
}
137
138
void loop()
139
{ 
140
  // Messwerte auslesen und verrechnen
141
  inL1SummePeriode += sq(analogRead(i_stromL1) * 1000 / 1314 - 389);
142
  inL2SummePeriode += sq(analogRead(i_stromL2) * 1000 / 1314 - 389);
143
  inL3SummePeriode += sq(analogRead(i_stromL3) * 1000 / 1314 - 389);
144
  inOutSummePeriode += analogRead(i_stromOut) * 1000 / 258;
145
  inSummePeriodeSchleife ++;
146
  if(millis() - inSummePeriodeZeitSpeicher >= 200){
147
    inL1Q = inL1SummePeriode / inSummePeriodeSchleife;
148
    inL2Q = inL2SummePeriode / inSummePeriodeSchleife;
149
    inL3Q = inL3SummePeriode / inSummePeriodeSchleife;
150
    inOut = inOutSummePeriode / inSummePeriodeSchleife;
151
    inSummePeriodeZeitSpeicher = millis();
152
    inL1SummePeriode = 0;
153
    inL2SummePeriode = 0;
154
    inL3SummePeriode = 0;
155
    inOutSummePeriode = 0;
156
    inSummePeriodeSchleife = 0;
157
158
    
159
    // Anlage auf Fehler überprüfen
160
    // Fehler: Leckstromüberwachung Phase 1 
161
    if(inL1 > nullStromIn * nullStromIn and inOut < nullStromOut){
162
      if(millis() - error1ZeitSpeicher >= error1ZeitWert){
163
        lcdErrorCode = 1;
164
      }
165
    }
166
    // Fehler: Leckstromüberwachung Phase 2
167
    else if(inL2 > nullStromIn * nullStromIn and inOut < nullStromOut){
168
      if(millis() - error1ZeitSpeicher >= error1ZeitWert){
169
        lcdErrorCode = 2;
170
      }
171
    }
172
    // Fehler: Leckstromüberwachung Phase 3
173
    else if(inL3 > nullStromIn * nullStromIn and inOut < nullStromOut){
174
      if(millis() - error1ZeitSpeicher >= error1ZeitWert){
175
        lcdErrorCode = 3;
176
      }
177
    }
178
    // Entprellzeit zurücksetzen
179
    else{
180
      error1ZeitSpeicher = millis();
181
    }
182
    
183
    // Fehler: Maximaler Strom Phase 1 überschritten
184
    if(inL1 > maxInStrom * maxInStrom){
185
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
186
        lcdErrorCode = 4;
187
      }
188
    }
189
    // Fehler: Maximaler Strom Phase 2 überschritten
190
    else if(inL2 > maxInStrom * maxInStrom){
191
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
192
        lcdErrorCode = 5;
193
      }
194
    }
195
    // Fehler: Maximaler Strom Phase 3 überschritten
196
    else if(inL3 > maxInStrom * maxInStrom){
197
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
198
        lcdErrorCode = 6;
199
      }
200
    }
201
    // Entprellzeit zurücksetzen
202
    else{
203
      error4ZeitSpeicher = millis();
204
    }
205
    
206
    // Fehler: Maximaler Schweißstrom überschritten
207
    if(inOut > maxOutStrom){
208
      if(millis() - error7ZeitSpeicher >= error7ZeitWert){
209
        lcdErrorCode = 7;
210
      }
211
    }
212
    // Entprellzeit zurücksetzen
213
    else{
214
      error7ZeitSpeicher = millis();
215
    }
216
    
217
    // Fehler: Überwachung L1 fehlerhaft
218
    if(inOut > nullStromOut and inL1 < nullStromIn * nullStromIn){
219
      if(millis() - error8ZeitSpeicher >= error8ZeitWert){
220
        lcdErrorCode = 8;
221
      }
222
    }
223
    // Fehler: Überwachung L2 fehlerhaft
224
    else if(inOut > nullStromOut and inL2 < nullStromIn * nullStromIn){
225
      if(millis() - error8ZeitSpeicher >= error8ZeitWert){
226
        lcdErrorCode = 9;
227
      }
228
    }
229
    // Fehler: Überwachung L3 fehlerhaft
230
    else if(inOut > nullStromOut and inL3 < nullStromIn * nullStromIn){
231
      if(millis() - error8ZeitSpeicher >= error8ZeitWert){
232
        lcdErrorCode = 10;
233
      }
234
    }
235
    // Entprellzeit zurücksetzen
236
    else {
237
      error8ZeitSpeicher = millis();
238
    }
239
  
240
241
    // Schweißvorgang
242
    if(inOut > nullStromOut){
243
  
244
      // Mittelwert vom letzten Prozess zurücksetzen
245
      // LCD anzeige ändern
246
      if(inEinmal == false){
247
        inSummeProzessSchleife = 0;
248
        inL1SummeProzess = 0;
249
        inL2SummeProzess = 0;
250
        inL3SummeProzess = 0;
251
        inOutSummeProzess = 0;
252
        lcd.clear();
253
        lcd.print("Am Schweißen ...");
254
        inEinmal = true;
255
        }
256
  
257
      // Mittelwertbildung Schweißströme vorbereiten
258
      inSummeProzessSchleife ++;
259
      inL1SummeProzess += inL1;
260
      inL2SummeProzess += inL2;
261
      inL3SummeProzess += inL3;
262
      inOutSummeProzess += inOut;
263
    }
264
  
265
    // Kein Schweißvorgang
266
    else{
267
      // Mittelwert vom letzten Prozess errechnen
268
      if(inL1SummeProzess > 0 and inL2SummeProzess > 0 and inL3SummeProzess >0 and inOutSummeProzess > 0){
269
        lcdL1 = sqrt(inL1SummeProzess / inSummeProzessSchleife) / 10;
270
        lcdL2 = sqrt(inL2SummeProzess / inSummeProzessSchleife) / 10;
271
        lcdL3 = sqrt(inL3SummeProzess / inSummeProzessSchleife) / 10;
272
        lcdOut = inOutSummeProzess / inSummeProzessSchleife / 10;
273
        lcdNeu = true;
274
        }
275
      
276
     //Lichtbogenstandzeit
277
      if(inEinmal == true){
278
        lichtbogenSekunden = (millis() - lichtbogenZeitSpeicher) / 1000 + lichtbogenSekunden;
279
        lichtbogenMinutenNeu += lichtbogenSekunden / 60;
280
        lichtbogenSekunden -= lichtbogenMinutenNeu * 60;
281
        lichtbogenMinuten += lichtbogenMinutenNeu;
282
        if(lichtbogenMinuten >= 60){
283
          lichtbogenStunden ++;
284
          lichtbogenMinuten -= 60;
285
          EEPROM.write(13, lichtbogenStunden / 100);
286
          EEPROM.write(12, lichtbogenStunden - (EEPROM.read(13) * 100));
287
        }
288
        if(lichtbogenMinutenNeu > 0) EEPROM.write(11, lichtbogenMinuten);
289
      }
290
      lichtbogenZeitSpeicher = millis();
291
      inEinmal = false;
292
    
293
      // Fehler Anzeige
294
      if(lcdErrorCode > 0){
295
        switch(lcdErrorCode){
296
          case 1:
297
            lcd.clear();
298
            lcd.print("Error 1");
299
            lcd.setCursor(0, 1);
300
            lcd.print("L1 Leckstrom");
301
          break;
302
          case 2:
303
            lcd.clear();
304
            lcd.print("Error 2");
305
            lcd.setCursor(0, 1);
306
            lcd.print("L2 Leckstrom");
307
          break;
308
          case 3:
309
            lcd.clear();
310
            lcd.print("Error 3");
311
            lcd.setCursor(0, 1);
312
            lcd.print("L3 Leckstrom");
313
          break;
314
          case 4:
315
            lcd.clear();
316
            lcd.print("Error 4");
317
            lcd.setCursor(0, 1);
318
            lcd.print("L1 Ueberlast");
319
          break;
320
          case 5:
321
            lcd.clear();
322
            lcd.print("Error 5");
323
            lcd.setCursor(0, 1);
324
            lcd.print("L2 Ueberlast");
325
          break;
326
          case 6:
327
            lcd.clear();
328
            lcd.print("Error 6");
329
            lcd.setCursor(0, 1);
330
            lcd.print("L3 Ueberlast");
331
          break;
332
          case 7:
333
            lcd.clear();
334
            lcd.print("Error 7");
335
            lcd.setCursor(0, 1);
336
            lcd.print("Iout Ueberlast");
337
          break;
338
          case 8:
339
            lcd.clear();
340
            lcd.print("Error 8");
341
            lcd.setCursor(0, 1);
342
            lcd.print("Sensor L1 defekt");
343
          break;
344
          case 9:
345
            lcd.clear();
346
            lcd.print("Error 9");
347
            lcd.setCursor(0, 1);
348
            lcd.print("Sensor L2 defekt");
349
          break;
350
          case 10:
351
            lcd.clear();
352
            lcd.print("Error 10");
353
            lcd.setCursor(0, 1);
354
            lcd.print("Sensor L3 defekt");
355
          break;
356
        }
357
        
358
        // Fehler im EEPROM Speichern
359
        if(errorEinmal == false){
360
          EEPROM.write(9, EEPROM.read(8));
361
          EEPROM.write(8, EEPROM.read(7));
362
          EEPROM.write(7, EEPROM.read(6));
363
          EEPROM.write(6, EEPROM.read(5));
364
          EEPROM.write(5, EEPROM.read(4));
365
          EEPROM.write(4, EEPROM.read(3));
366
          EEPROM.write(3, EEPROM.read(2));
367
          EEPROM.write(2, EEPROM.read(1));
368
          EEPROM.write(1, EEPROM.read(0));
369
          EEPROM.write(0, lcdErrorCode);
370
          
371
          // Hauptschütz ausschalten
372
          digitalWrite(o_ein1, LOW);
373
          digitalWrite(o_ein2, LOW);
374
          
375
          errorEinmal = true;
376
        }
377
        delay(5000);
378
      }
379
      
380
      // Anzeige im Normalbetrieb
381
      else if(lcdNeu == true) {
382
        lcd.setCursor(0, 0);
383
        lcd.print("IN");
384
        if(lcdL1 < 10) lcd.print("    ");
385
        else lcd.print("   ");
386
        lcd.print(lcdL1);
387
        lcd.print("A");
388
        if(lcdL2 < 10) lcd.print("  ");
389
        else lcd.print(" ");
390
        lcd.print(lcdL2);
391
        lcd.print("A");
392
        if(lcdL3 < 10) lcd.print("  ");
393
        else lcd.print(" ");
394
        lcd.print(lcdL3);
395
        lcd.print("A");
396
      
397
        lcd.setCursor(0, 1);
398
        lcd.print("OUT");
399
        if(lcdL1 < 10) lcd.print("   ");
400
        else if(lcdOut < 100)lcd.print("  ");
401
        else lcd.print(" ");
402
        lcd.print(lcdOut);
403
        lcd.print("A  ");
404
        
405
        digitalWrite(o_ein1, HIGH);
406
        digitalWrite(o_ein2, HIGH);
407
        
408
        lcdNeu = false;
409
        }
410
      }
411
    }
412
  digitalWrite(24, LOW);
413
  delayMicroseconds(500);
414
  digitalWrite(24, HIGH);
415
}

von chris_ (Gast)


Lesenswert?

Autor: Cyblord ---- (cyblord)
>Sieht das Arduino-Framework nun ISRs vor oder nicht?

Das Framework sieht ISRs vor:
https://www.arduino.cc/en/Reference/attachInterrupt

Allerdings leider nur PinChange Interrupts. Meiner Meinung nach sollten 
sie auch standardmäßig Timer-Callbacks anbieten, das wäre ziemlich 
nützlich.

von Forist (Gast)


Lesenswert?

millioner2 schrieb:
> Leider weiß ich nicht wie ich hier .ino Datein hochladen darf, daher
> mein Programm nochmal als Code.

Einfach die Taste "Durchsuchen..." rechts von dem Wort "Dateianhang:" 
drücken. Dann klapp's auch mit dem Anhängen von Datei.

von Walter S. (avatar)


Lesenswert?

chris_ schrieb:
> Meiner Meinung nach sollten
> sie auch standardmäßig Timer-Callbacks anbieten, das wäre ziemlich
> nützlich.

das ham die sich bei Arduino auch gedacht:

Timer1.initialize(alle_x_mikrosekunden);
Timer1.attachInterrupt(do_this_funktion);

von Manfred (Gast)


Lesenswert?

Cyblord -. schrieb:
> Sieht das Arduino-Framework nun ISRs vor oder nicht?
Zumindest vom externen Pin (Rotary-Encoder) habe ich den Interrupt schon 
benutzt, am Uno / Nano Anschlüsse D2 und D3.

von Yalu X. (yalu) (Moderator)


Lesenswert?

millioner2 schrieb:
> Die größten Bremsen waren in absteigender Rheinfolge:
> - Rechnen mit Float-Variabeln
> - Wurzelziehen
> - Errechnen der Lichtbogenzeit

Den die allergrößte Bremse hast du vergessen: Nämlich eine falsch
gesetzte geschweifte Klammer, die du in der neuen Version an die
richtige Stelle verschoben hast.

Alte Version:

1
void loop()
2
{ 
3
4
  ... Codeabschnitt A (kurz) ...
5
6
  if(millis() - inSummePeriodeZeitSpeicher >= 200){
7
8
    ... Codeabschnitt B (kurz) ...
9
10
  } // <- diese Klammer
11
12
  ... Codeabschnitt C (gaaanz lang) ...
13
14
}


Neue Version:

1
void loop()
2
{ 
3
4
  ... Codeabschnitt A (kurz) ...
5
6
  if(millis() - inSummePeriodeZeitSpeicher >= 200){
7
8
    ... Codeabschnitt B (kurz) ...
9
10
    ... Codeabschnitt C (gaaanz lang) ...
11
12
  } // <- diese Klammer
13
}

Da der sehr rechenaufwendige Codeabschnitt C, der vermutlich den
Bärenanteil der von dir gemessenen 100ms verschlungen hat, jetzt nicht
mehr in jedem Zyklus, sondern nur noch alle 200ms ausgeführt wird (und
immer erst, nachdem alle Messwerte bereits eingelesen und aufsummiert
sind), stört er das Timing der Abtastung überhaupt nicht mehr.

Ich hätte dich gerne schon in meinem letzten Beitrag darauf hingewiesen,
konnte dort aber noch nicht wissen, welche Klammer(n) falsch waren, da
dort an anderer Stelle eine Codezeile mit einer geschweiften Klammer
(wahrscheinlich beim Copy/Paste) verloren gegangen ist.

Nur der folgende Codeabschnitt wird in jedem Zyklus ausgeführt, deswegen
lohnt sich hier die Optimierung besonders:

1
  inL1SummePeriode += sq(analogRead(i_stromL1) * 1000 / 1314 - 389);
2
  inL2SummePeriode += sq(analogRead(i_stromL2) * 1000 / 1314 - 389);
3
  inL3SummePeriode += sq(analogRead(i_stromL3) * 1000 / 1314 - 389);
4
  inOutSummePeriode += analogRead(i_stromOut) * 1000 / 258;
5
  inSummePeriodeSchleife ++;
6
  if(millis() - inSummePeriodeZeitSpeicher >= 200){

Bei deinem Versuch, von Float- auf Integer-Arithmetik umzustellen, ist
dir allerdings ein Fehler unterlaufen: Schon ab einem ADC-Wert von 33
führt die Multiplikation mit 1000 zu einen Überlauf. Wenn due die 1000
durch 1000L ersetzt, wird der gesamte Ausdruck in long gerechnet, dann
sollten keine Überläufe mehr auftreten.

Auch hier gibt es einen Überlauf:

1
int           maxInStrom = 310;
2
...
3
    if(inL1 > maxInStrom * maxInStrom){


Noch etwas:

sq() ist ein Makro und in Arduino.h folgendermaßen definiert:

1
#define sq(x) ((x)*(x))

D.h. die Zeile

1
  inL1SummePeriode += sq(analogRead(i_stromL1) * 1000 / 1314 - 389);

wird expandiert zu:

1
  inL1SummePeriode += ((analogRead(i_stromL1) * 1000 / 1314 - 389)*
2
                       (analogRead(i_stromL1) * 1000 / 1314 - 389));

Wie du siehst, wird damit der ADC sinnloserweise zweimal gelesen. Auch
die Umrechnung des Werts erfolgt zweimal. Das konntest du natürlich
nicht wissen, da die Arduino-Dokumentation diesen wichtigen Sachverhalt
schlichtweg verschweigt. Schlimmer noch: In der Language Reference sind
die Makros unter "Functions" gelistet, was einfach nur falsch ist.

Da behaupte noch einer, Arduinos seien für Anfänger. Es lauern ähnlich
fiese Fallen wie der klassischen C-Programmierung, nur dass sie beim
Arduino fein säuberlich mit losem Laub zugedeckt worden sind, um dem
Anfänger die Angst davor zu nehmen und damit absolut sicher zu gehen,
dass er auch wirklich in sie hineintappt :)

: Bearbeitet durch Moderator
von Joachim B. (jar)


Lesenswert?

Falk B. schrieb:
> ADC-Messung für Kanal 1 starten
> Auf Ende der ADC-Messung warten
> ADC-Ergebnis 1 auslesen
> ADC-Messung für Kanal 2 starten

wieso das denn?

warten auf was?

Arduino F. schrieb:
> Das lässt sich auch nebenläufig erledigen. Dafür hat Atmel die ISRs
> eingeplant.

und die arbeiten auch in der Arduino IDE!

ich warte doch nicht auf ADC wenn ich keine Zeit habe!

#if USE_ADC
  ANALOG_ON;
#endif

#if USE_ADC
void ADC_Init(void)
{  ADMUX =((1<<REFS0) | (1<<REFS1)); // ref 2,5V
  //Free Running Mode, Division Factor 128, Interrupt on
  ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|( 
1<<ADIE);
}

ISR (ADC_vect)
{
  ANALOG_OFF; //ADC OFF
  static unsigned char start=0;
  if(!start)
  {  merke_ss12v=time%60;
    merke_ss5v=merke_ss12v;
    merke_ss33v=merke_ss5v;
    start=1;
  }
  switch(channel)
  {  case 7:
      if(!u12_rechne)      // jar10
      {  u12+=ADC;
        u12_cnt++;
        if(u12_cnt>31)
          u12_rechne=1;  // jar10
      }
      break;
    case 6:
      if(!u5_rechne)      // jar10
      {  u5+=ADC;
        u5_cnt++;
        if(u5_cnt>31)
          u5_rechne=1;  // jar10
      }
      break;
    case 5:
      if(!u33_rechne)      // jar10
      {  u33+=ADC;
        u33_cnt++;
        if(u33_cnt>31)
          u33_rechne=1;  // jar10
      }
      break;
    default:
      var_array[channel]=0;
      break;
  }
  channel++; // jar temp umrechnung gefunden
  if(channel > 7)
    channel = 5;
  ADMUX =((1<<REFS0) | (1<<REFS1)) + channel; // ref 2,5V
  ANALOG_ON;//ADC ON
}
#endif //USE_ADC

von Karl M. (Gast)


Lesenswert?

Was soll das sein ?
1
ANALOG_OFF; //ADC OFF
2
ANALOG_ON;//ADC ON

von millioner2 (Gast)


Lesenswert?

Yalu X. schrieb:
> Bei deinem Versuch, von Float- auf Integer-Arithmetik umzustellen, ist
> dir allerdings ein Fehler unterlaufen: Schon ab einem ADC-Wert von 33
> führt die Multiplikation mit 1000 zu einen Überlauf. Wenn due die 1000
> durch 1000L ersetzt, wird der gesamte Ausdruck in long gerechnet, dann
> sollten keine Überläufe mehr auftreten.

Ok danke für den Hinweis ich bin davon ausgegangen, das immer der 
Variabeltyp in den geschrieben werden soll auch zum Rechnen verwendet 
wird. Aber wenn ich das da noch mal extra angeben muss dann sei es so ^^

von Joachim B. (jar)


Lesenswert?

Karl M. schrieb:
> Was soll das sein ?

// defines hier könnte man noch optimieren
//und #include "config.h" hinter #if USE_ADC setzen

#include "config.h"

#if USE_ADC
#ifndef ADC_H
  #define ADC_H

  #include <avr/io.h>
  #include <avr/interrupt.h>
  #include "httpd.h" //für Variablen Array
  #define ANALOG_OFF ADCSRA=0

  #define ANALOG_ON 
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|( 
1<<ADIE);

  volatile unsigned char channel;
  extern void ADC_Init(void);
#endif //_ADC_H_

#endif //USE_ADC

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

millioner2 schrieb:
> ich bin davon ausgegangen, das immer der
> Variabeltyp in den geschrieben werden soll auch zum Rechnen verwendet
> wird

Aktiviere doch mal alle Meldungen in den Voreinstellungen...

von millioner2 (Gast)


Lesenswert?

Arduino F. schrieb:
> Aktiviere doch mal alle Meldungen in den Voreinstellungen...

Hab ich gemacht aber es stehen da nur Warnungen für die Libs die ich 
include und ein paar mal diese hier:
1
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
2
3
       if(millis() - error8ZeitSpeicher >= error8ZeitWert){

von Yalu X. (yalu) (Moderator)


Lesenswert?

Arduino F. schrieb:
> Aktiviere doch mal alle Meldungen in den Voreinstellungen...

Eine Warnung wird nur dann ausgegeben, wenn der Überlauf zur Compilezeit
sicher festgestellt werden kann. Das ist bei

1
  inL1SummePeriode += sq(analogRead(i_stromL1) * 1000 / 1314 - 389);

wegen des variablen Resultats von analogRead() nicht der Fall.

Im zweiten Beispiel

1
int           maxInStrom = 310;
2
...
3
    if(inL1 > maxInStrom * maxInStrom){

könnte der Compiler den Überlauf erkennen, wenn er wüsste, dass
maxInStrom während des gesamten Programmablaufs den Wert von 310
beibehält. Hier hätte es geholfen, maxInStrom als const zu deklarieren.
Dies würde es es dem Compiler zudem ermöglichen, das Quadrat der Zahl
(96100) als fixe Konstante in den Code einzubauen, was das Laden der
Zahl aus dem RAM und die Multiplikation einspart.

von Joachim B. (jar)


Lesenswert?

millioner2 schrieb:
> warning: comparison between signed and unsigned integer expressions
> [-Wsign-compare]
>
>        if(millis() - error8ZeitSpeicher >= error8ZeitWert){

bei Differenz auch bei Überlauf kommt es wohl auf die Reihenfolge an

Ich meine gelesen zu haben das 1. millis() überlaufen kann also kleiner 
sein kann aber die Differenz zu error8ZeitSpeicher trotzdem stimmt nur 
weiss ich immer noch nicht genau wie, Lothar Miller hats mal erklärt 
hier, finde ich grad nicht.

: Bearbeitet durch User
von millioner2 (Gast)


Lesenswert?

Yalu X. schrieb:
> Auch hier gibt es einen Überlauf:
>
> int           maxInStrom = 310;
> ...
>     if(inL1 > maxInStrom * maxInStrom){

Wie kann ich das denn weg bekommen? Mir würde nur eine Sache einfallen 
und da weiß ich auch nicht ob das funktioniert.
1
int           maxInStrom = 310;
2
...
3
    if(inL1 > maxInStrom * 1L * maxInStrom){

von Yalu X. (yalu) (Moderator)


Lesenswert?

millioner2 schrieb:
1
   if(inL1 > maxInStrom * 1L * maxInStrom){

Das ist richtig, nur nicht besonders schön wegen des scheinbar
überflüssigen zusätzlichen Faktors.

Normalerweise würde man hier (mindestens) einen der beiden Faktoren in
long casten:

1
   if(inL1 > (long)maxInStrom * maxInStrom){

Ach, und inL1 sollte natürlich ebenfalls long (und nicht int) sein,
sonst gibt es schon beim Aufsummieren der quadrierten Messwerte einen
Überlauf.

Ja es ist gar nicht so leicht, ein bestehendes Programm von Float- auf
Integer-Arithmetik umzustellen. Manche der Überläufe entdeckt man u.U.
erst, wenn das Programm schon eine Weile im produktiven Betrieb ist :)

von millioner2 (Gast)


Lesenswert?

Als ich die Variabel "maxInStrom" gerade als const angegeben habe, musst 
ich noch eine weitere Fehler feststellen.

Geändert hab ich das:
Jetzt gibt es laut Arduino keine Warnungen mehr.
1
...
2
unsigned long inL1Q;                              // Eingangsstrom Phase 1 zum Quadrat
3
unsigned long inL2Q;                              // Eingangsstrom Phase 2 zum Quadrat
4
unsigned long inL3Q;                              // Eingangsstrom Phase 3 zum Quadrat
5
...
6
const int     nullStromIn = 10;                   // [1/10A] Wert der als "kein Stromfluss" anerkannt wird
7
const int     nullStromOut = 20;                  // [1/10A] Wert der als "kein Stromfluss" anerkannt wird
8
const int     maxInStrom = 310;                   // [1/10A] Maximaler Eingangsstrom pro Phase
9
const int     maxOutStrom = 3500;                 // [1/10A] Maximaler Ausgangsstrom
10
...
11
    // Fehler: Maximaler Strom Phase 1 überschritten
12
    if(inL1Q > maxInStrom * 1L * maxInStrom){
13
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
14
        lcdErrorCode = 4;
15
      }
16
    }
17
    // Fehler: Maximaler Strom Phase 2 überschritten
18
    else if(inL2Q > maxInStrom * 1L * maxInStrom){
19
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
20
        lcdErrorCode = 5;
21
      }
22
    }
23
    // Fehler: Maximaler Strom Phase 3 überschritten
24
    else if(inL3Q > maxInStrom * 1L * maxInStrom){
25
      if(millis() - error4ZeitSpeicher >= error4ZeitWert){
26
        lcdErrorCode = 6;
27
      }
28
    }
29
...

von Joachim B. (jar)


Lesenswert?

millioner2 schrieb:
> Jetzt gibt es laut Arduino keine Warnungen mehr....
> unsigned long inL1Q;

auch wenn du Arduino benutzt

unsigned long ist out weil nicht portabel

besser ist uint32_t das ist klarer denn long kann woanders 32 bit sein 
muss es aber nicht.

früher gabs halt

byte -> 8 bit (immer)
word -> 16 bit (meist)
long -> 32 bit (???)

das ganze noch mit unsigned

aber heute gibt es long long oder qword

um Klarheit zu schaffen finde ich uint64_t oder int64_t verständlicher.

von Falk B. (falk)


Lesenswert?

@millioner2 (Gast)

>Um es mal vor weg zu nehmen ich bin jetzt viel schneller als ich muss.
>Von Ursprünglich 100ms auf 200uS! (Siehe Bild)

Du kannst nicht mal eine Zeitmessung vom Oszi ablesen . . .  :-(

Dort sehe ich 0,2ms/DIV und dein Puls hat eine Periodendauer von ca. 4,5 
DIV, also ca. 0,9ms.

>Da ich jetzt alles so verlegt habe, das diese Sachen alle wärend des
>Prozesses nicht benutzt werden, bin ich jetzt halt deutlich schneller.

Schon mal ein großer Fortschritt.

>Leider weiß ich nicht wie ich hier .ino Datein hochladen darf, daher
>mein Programm nochmal als Code.


http://www.dummeausrede.de

!!!!

von Wsk (Gast)


Lesenswert?

Falk B. schrieb:
> Du kannst nicht mal eine Zeitmessung vom Oszi ablesen . . .  :-(
>
> Dort sehe ich 0,2ms/DIV und dein Puls hat eine Periodendauer von ca. 4,5
> DIV, also ca. 0,9ms.
Er misst die Laufzeit doch in der High-Phase, welche ca. 1 DIV lang ist, 
also 0,2ms?

Die Low-Phase, welche laut Code 0,5ms (delayMicroseconds(500)) dauern 
soll, ist doch 4,5 DIV lang und somit 0,9ms??

Oder bezieht sich das DIV auf die Marker innerhalb der Quadrate??

mfg

von Falk B. (falk)


Lesenswert?

@Wsk (Gast)

>> Dort sehe ich 0,2ms/DIV und dein Puls hat eine Periodendauer von ca. 4,5
>> DIV, also ca. 0,9ms.

>Er misst die Laufzeit doch in der High-Phase, welche ca. 1 DIV lang ist,
>also 0,2ms?

Hmm stimmt, da war ich wohl etwas zu vorschnell . . . 8-0

Aber wer mißt denn bitte schön auch mit negativer Logik die Dauer einer 
Funktion, welche dazu noch deutlich kürzer ist als die Zwangspause? 
Naja, man könnte es als low active chip select interpretieren ;-)

von millioner2 (Gast)


Lesenswert?

Falk B. schrieb:
> @Wsk (Gast)
>
>>> Dort sehe ich 0,2ms/DIV und dein Puls hat eine Periodendauer von ca. 4,5
>>> DIV, also ca. 0,9ms.
>
>>Er misst die Laufzeit doch in der High-Phase, welche ca. 1 DIV lang ist,
>>also 0,2ms?
>
> Hmm stimmt, da war ich wohl etwas zu vorschnell . . . 8-0
>
> Aber wer mißt denn bitte schön auch mit negativer Logik die Dauer einer
> Funktion, welche dazu noch deutlich kürzer ist als die Zwangspause?
> Naja, man könnte es als low active chip select interpretieren ;-)

Gut das hat sich geklärt, ich wollte gerne die Zyklusdauer als HIGH 
Signal haben und das delay hab ich nur zur besseren ansicht dahin 
gemacht.

von millioner2 (Gast)


Lesenswert?

Falk B. schrieb:
>>Leider weiß ich nicht wie ich hier .ino Datein hochladen darf, daher
>>mein Programm nochmal als Code.
>
> http://www.dummeausrede.de
>
> !!!!

Da hier im Forum gefühlt alles sehr streng bezüglich Links und Uploads 
ist war ich mir nich sicher, wie ich den Code anhänge darf ohne direkt 
wieder gelöscht zu werden. Unter Datei anhängen steht nur was von 
Bildern daher bin ich davon ausgegangen, das man dort keine belibigen 
Dateien hochladen darf.

von Falk B. (falk)


Lesenswert?

@millioner2 (Gast)

>hat sich geklärt, ich wollte gerne die Zyklusdauer als HIGH
>Signal haben

Ist ja auch sinnvoll, aber dazu setzt man das IO-Pin am Anfang auf HIGH 
und am Ende auf LOW. Da erkennt man auch im Quelltext gleich den Anfang 
der Messung und das Ende. Mit deiner Schreibweise ist es eher ein 
Rätsel.

>und das delay hab ich nur zur besseren ansicht dahin
>gemacht.

Schon klar, aber das kann man dann auch hinter das anschließende LOW 
schreiben.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wsk schrieb:
> Die Low-Phase, welche laut Code 0,5ms (delayMicroseconds(500)) dauern
> soll, ist doch 4,5 DIV lang und somit 0,9ms??

Das hat mich ebenfalls gewundert. Es ist zwar bekannt, dass die Funktion
digitalWrite() viel Overhead hat, aber doch keine 400µs?

Und warum dauert die High-Phase nur 200µs, obwohl alleine die 4 bzw. 7
Aufrufe von analogRead() nach Arduino-Dokumentation schon 400µs bzw.
700µs dauern?

von Falk B. (falk)


Lesenswert?

@Yalu X. (yalu) (Moderator)

>> Die Low-Phase, welche laut Code 0,5ms (delayMicroseconds(500)) dauern
>> soll, ist doch 4,5 DIV lang und somit 0,9ms??

>Das hat mich ebenfalls gewundert. Es ist zwar bekannt, dass die Funktion
>digitalWrite() viel Overhead hat, aber doch keine 400µs?

Nein, eher um die 6-8us.

>Und warum dauert die High-Phase nur 200µs, obwohl alleine die 4 bzw. 7
>Aufrufe von analogRead() nach Arduino-Dokumentation schon 400µs bzw.
>700µs dauern?

Gute Frage ;-)

Wahrscheinlich weil sie in dem Zustand gar nicht aufgerufen werden.

Wer Mist mißt, mißt Mist.

von millioner2 (Gast)


Lesenswert?

Falk B. schrieb:
>>> Die Low-Phase, welche laut Code 0,5ms (delayMicroseconds(500)) dauern
>>> soll, ist doch 4,5 DIV lang und somit 0,9ms??
>
>>Das hat mich ebenfalls gewundert. Es ist zwar bekannt, dass die Funktion
>>digitalWrite() viel Overhead hat, aber doch keine 400µs?
>
> Nein, eher um die 6-8us.

Das hat mich auch sehr gewundert habe es mit verschiedenen Werten 
versucht und der Fehler wird Prozentual größer je kleiner ich die Zeit 
mache aber bleibt nicht identisch.

Falk B. schrieb:
>>Und warum dauert die High-Phase nur 200µs, obwohl alleine die 4 bzw. 7
>>Aufrufe von analogRead() nach Arduino-Dokumentation schon 400µs bzw.
>>700µs dauern?
>
> Gute Frage ;-)
>
> Wahrscheinlich weil sie in dem Zustand gar nicht aufgerufen werden.
>
> Wer Mist mißt, mißt Mist.

Nachdem ich jetzt alles zu geändert habe, wie es mir gesagt wurde habe 
ich nocheinmal gemessen und erreiche 450µS. Kann mir nur vorstellen, das 
durch das Overflow irgendwas nicht oder nicht bis zum Ende ausgeführt 
wurde und daher die geringere Zeit.
Natürlich habe auch ich schon viel mist gemessen. Vorallem da ich auch 
Beruflich öfters mit dem Oszilloskop arbeite aber ich denke nicht das 
ich bei dieser "unglaublich komplizierten" Messung ein Fehler gemacht 
habe.
1
  inL1SummePeriodeNeu = analogRead(i_stromL1) * 1000L / 1314 - 389;
2
  inL2SummePeriodeNeu = analogRead(i_stromL2) * 1000L / 1314 - 389;
3
  inL3SummePeriodeNeu = analogRead(i_stromL3) * 1000L / 1314 - 389;
4
  inL1SummePeriode += (long)inL1SummePeriodeNeu * inL1SummePeriodeNeu;
5
  inL2SummePeriode += (long)inL2SummePeriodeNeu * inL2SummePeriodeNeu;
6
  inL3SummePeriode += (long)inL3SummePeriodeNeu * inL3SummePeriodeNeu;
7
  inOutSummePeriode += analogRead(i_stromOut) * 1000L / 258;
8
  inSummePeriodeSchleife ++;
9
  if(millis() - inSummePeriodeZeitSpeicher >= 200){

von chris_ (Gast)


Lesenswert?

>Ich denke um sauber einen 50Hz Sinus messen zu können sollte es schon
>maximal 2ms pro Zyklus sein, schätze ich.

Meiner Meinung nach wäre es am besten, wenn Du eine vollständige 50Hz 
Periode (20ms) mit z.B. 200us aufnimmst, dann sind die Störungen weg 
gemittelt. Die Float-Berechnungen kannst Du hinterher auf das Datenarray 
machen. Ich schätze mal, das die 100ms Lücken zwischen den Berechnungen 
für die Auswertung des Schweißprozesses nicht so schlimm sind.

von hans_ (Gast)


Lesenswert?

Nimm einen Arduino DUE. Die Arithmetik ist frei von Zeitverbrauch 
gelöst,
da der verwendetet arm 3 float Arithmrtik kann. Die Anschlüsse sind wie 
beim
Mega-Arduino.

von Nico W. (nico_w)


Lesenswert?

Der Due hat keine FPU.

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.