Hallo,
ich möchte mit einem D1Mini S0 Stromzähler auslesen und die Werte in eine Datenbank ablegen.
Es sollen 4 Zähler ausgelesen werden. Im Array counter[4] werden die Impulse addiert. Nach 28 Sekunden wird der Zählerstand zur Datenbank übertragen. Es genügt hier ein Wertebereich von 0 bis 255.
//Die ISR darf keine Parameter annehmen und zurückgeben!
ICACHE_RAM_ATTR void interruptRoutine1(){
interruptRoutine(0);
}
Aus der Interrupt Routine rufe ich die eigentliche Zähler - Funtion auf.
Die Interrupts werden ausgelöst. In der Form des nachfolgenden Codes steht
counter[btKanalNr] immer auf 0. Auch wenn ich die Zeile "counter[btKanalNr] = 0;" auskommentiere tut sich nichts.
Füge ich am Ende der Funktion ein "counter[btKanalNr]++;" so wird tatsächlich um 1 hochgezählt. Das Verückte dabei ist jedoch, es bleibt dann dabei bei 1! Und wenn ich 2 mal direkt hintereinander inkrementiere dann bleibt es bei 2.
Ist counter[btKanalNr] mal z.B. auf 2 gesetzt worden, so wird der Wert auch tatsächlich der Versand - Funktion übergeben. Weitere Impulse werden aber nicht hinzuaddiert. Es bleibt bei 2.
Was das Programm dann wirklich macht kann ich nicht so recht
nachvollziehen. Jedenfalls war das Verhalten verwirrend.
Du könntest das auch einfach ausprobieren, eine Dummy if() Anweisung mit solch einer Zuweisung statt eines Vergleichs z.B. in der loop() einbauen und mit printf() Debug alles ausgeben was dich interessiert.
Einen weiteren Fallstrick sehe ich allerdings in deiner Interrupt-Service-Routine noch. Beim den ESPs mit externem seriellen Flash müssen die ISR mit der Compiler Directive IRAM_ATTR (obsolet=ICACHE_RAM_ATTR) versehen sein, damit der zugehörige Code eben nicht wie üblich aus dem Flash abläuft, sondern vorher ins RAM gecached wird und von dort abläuft (siehe auch Doku: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/spi_flash/spi_flash_concurrency.html#iram-safe-interrupt-handlers ). Für die IRS von oben stimmt das, aber es ist auch zwingend notwendig für Funktionen die innerhalb der IRS aufgerufen werden. In deinem Fall also auch für interruptRoutine(). Desweiteren findet sich in der Doku, dass die "globalen" Variablen auf welche die IRS zugreift per DRAM_ATTR Direktive abgesichert werden sollen.
Hhmm, ich hab die Schaltung grad nicht da und auch keine Ardunino IDE installiert. (ich verwende für ESPs VisualStudioCode + Platformio Extension)
Aber Du könntest doch am lebenden Objekt selber testen ob der Code ohne Warnungen durch den Compiler/Linker läuft und ob das Programm dann genau tut was Du erwartest.
Wie gesagt, die Info bezüglich DRAM seht so in den Espressif Doku. Ich kann mir vorstellen, dass die FW auch ohne DRAM Directive funktioniert und du gar nix merkst. Aber es ist eben auch wahrscheinlich, das die SW sproadisch Quatsch macht und du nicht weißt warum.
Variablen, die in einem ISR-kontext verändert werden (z.B. deine counter) müssen 'volatile' gekennzeichnet werden, damit der Compiler nicht weg-optimiert.
Variablen, die in einem ISR-kontext verändert werden (z.B. deine
counter) müssen 'volatile' gekennzeichnet werden, damit der Compiler
nicht weg-optimiert.
Danke, daran hatte ich gar nicht mehr gedacht.
mfg Klaus
Der Überlauf wird tatsächlich vorkommen. Der Prozessor soll solange laufen wie Strom vorhanden ist und selbst dann hängt er noch mit anderen Verbrauchern an einer USV dran.
In der Tat ist es ja so, daß der D1Mini selber nur rollierend Werte von 0 bis 255 sendet. Der S0 Stromzähler liefert 1000 Impulse pro 1 kWh.
Das sind bei 1 kW = 0,277 Impulse/s.
Mein Programm für die Einarbeitung in die Datenbank liest schon seit über 10 Jahren einen anderen S0 Stromzähler aus. Diesen Überlauf habe ich schon behandelt.
Bei D1Mini muß ich mir jedoch noch ein neues Konzept einfallen lassen. Gut das Du mich nochmal auf diese Problematiken hingewiesen hast, denn ich war schon sehr zufrieden.
Ich liebe Teamwork und hoffe Dir auch mal so helfen zu können.
mfg Klaus
ich befürchte, dass das ESP8266_RTOS_SDK nicht in den Arduino Libs enthalten ist. Daher fehlt das Include File natürlich. Dann hab ich für den ESP8266 leider auch keine gute Lösung. Sorry für den falschen Hinweis, dann wird es wohl auch ohne gehen, so wie in anderen Beispielen, die du im Netz gefunden hast.
ich befürchte, dass das ESP8266_RTOS_SDK nicht in den Arduino Libs
enthalten ist. Daher fehlt das Include File natürlich. Dann hab ich für
den ESP8266 leider auch keine gute Lösung. Sorry für den falschen
Hinweis, dann wird es wohl auch ohne gehen, so wie in anderen
Beispielen, die du im Netz gefunden hast.
Ja, man müßte wohl mit dem SDK und RTOS arbeiten. Das geht mir zu weit. Aber es funktionierte ja schon zuvor zufriedenstellend.
Besten Dank.
mfg Klaus
Variablen, die in einem ISR-kontext verändert werden (z.B. deine
counter) müssen 'volatile' gekennzeichnet werden, damit der Compiler
nicht weg-optimiert.
Ich habe mir die Referenz von 'volatile' angeschaut.
Ich habe mir die Referenz von 'volatile' angeschaut.
...
Ich verstehe das so, daß 'volatile' so etwas wie 'DRAM_ATTR' bewirkt.
Nein. Der Unterschied ist zum einen, daß volatile ein Schlüsselwort ist, das jeder C/C++ Compiler verstehen muß. DRAM_ATTR ist hingegen ein Makro, dessen Bedeutung nicht vom Standard festgelegt wird. Es sieht aus wie eine Festlegung der Speicherklasse.
Auch nicht. volatile verbietet dem Compiler bloß einige Optimierungen. Spezifisch die Optimierung, daß der Compiler davon ausgehen darf, daß die Variable nur innerhalb des betrachteten Programmcodes verändert werden kann. Deswegen muß er die Variable jedes Mal wieder vom Speicher lesen. Auch wenn er bereits eine Kopie in einem Register hat.
Einfach alle Variablen als volatile zu deklarieren, macht das Programm nicht sicherer, sondern nur langsamer.
Deswegen muß er die Variable jedes Mal wieder vom Speicher
lesen. Auch wenn er bereits eine Kopie in einem Register hat.
Um es präziser zu sagen: Der Compiler muss für die Variable einen Platz im Arbeitsspeicher (egal, ob Flash, RAM oder Register) fest reservieren. Und: Der Compiler muss dafür sorgen, dass der aktuelle Wert bei jedem Zugriff darauf von diesem festen Ort gelesen bzw. dorthin geschrieben wird.
Mit Registern hat das nichts zu tun. Theoretisch könnte der Compiler auch ein Register dafür fest reservieren; praktisch tut er das nur bei Prozessoren mit extrem vielen Registern. Wichtig ist in jedem Fall nur die oben erwähnte Zugriffstaktik, egal ob Register oder Arbeitsspeicher:
volatile int aa; Foo() { int bb; bb = aa; if (bb == aa) { x = 99; } }
Ohne 'volatile' wäre es dem Compiler erlaubt, die 'if'-Bedingung wegzuoptimieren, und das würde schieflaufen, falls eine Interruptroutine den Wert von 'aa' nach der Zuweisung und vor dem Auswerten der if-Bedingung verändern würde. Ob bzw. wann so ein Interrupt kommt, kann man aber nicht vorhersagen.
Das Beispiel ist natürlich nicht sinnvoll, aber es zeigt, dass ein Wegoptimieren mancher Teile ungeahnte Folgen haben kann.