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.
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.
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?
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.
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.
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?
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).
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.
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?
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 !
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....
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 **
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:
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.
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
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.
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.
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.
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
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 ....
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.
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
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.
@ 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 ;-)
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.
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.
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?
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!
@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.
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.
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).
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.
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.
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.
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!
> 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.
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]
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.
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.
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);
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.
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
voidloop()
2
{
3
4
...CodeabschnittA(kurz)...
5
6
if(millis()-inSummePeriodeZeitSpeicher>=200){
7
8
...CodeabschnittB(kurz)...
9
10
}// <- diese Klammer
11
12
...CodeabschnittC(gaaanzlang)...
13
14
}
Neue Version:
1
voidloop()
2
{
3
4
...CodeabschnittA(kurz)...
5
6
if(millis()-inSummePeriodeZeitSpeicher>=200){
7
8
...CodeabschnittB(kurz)...
9
10
...CodeabschnittC(gaaanzlang)...
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:
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
intmaxInStrom=310;
2
...
3
if(inL1>maxInStrom*maxInStrom){
Noch etwas:
sq() ist ein Makro und in Arduino.h folgendermaßen definiert:
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 :)
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
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 ^^
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...
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:
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
wegen des variablen Resultats von analogRead() nicht der Fall.
Im zweiten Beispiel
1
intmaxInStrom=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.
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.
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.
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 :)
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
unsignedlonginL1Q;// Eingangsstrom Phase 1 zum Quadrat
3
unsignedlonginL2Q;// Eingangsstrom Phase 2 zum Quadrat
4
unsignedlonginL3Q;// Eingangsstrom Phase 3 zum Quadrat
5
...
6
constintnullStromIn=10;// [1/10A] Wert der als "kein Stromfluss" anerkannt wird
7
constintnullStromOut=20;// [1/10A] Wert der als "kein Stromfluss" anerkannt wird
8
constintmaxInStrom=310;// [1/10A] Maximaler Eingangsstrom pro Phase
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.
@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
!!!!
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
@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 ;-)
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.
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.
@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.
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?
@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.
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.
>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.
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.