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.
// Zuordnung der GPIOs
#define Kanal1 14 // D5
#define Kanal2 12 // D6
#define Kanal3 13 // D7
#define Kanal4 5 // D1
byte counter[4]; // 0 bis 255
byte count_wert[4];
//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.
void interruptRoutine(int btKanalNr) {
pulsIstZeit[btKanalNr] = millis();
if (pulsIstZeit[btKanalNr] - pulsStartIst[btKanalNr] >= endPrellZeit)
{
pulsStartIst[btKanalNr] = pulsIstZeit[btKanalNr];
if (counter[btKanalNr] = 255) {
counter[btKanalNr] = 0;
} else {
counter[btKanalNr]++;
}
}
}
void setup() {
pinMode(Kanal1, INPUT_PULLUP);
.....
// ---- Starte Interrupts ----------
attachInterrupt(digitalPinToInterrupt(Kanal1), interruptRoutine1,
FALLING);
.....
}
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.
Wie komme ich da weiter?
mfg Klaus
Hallo Andreas,
ich wollte einen Vergleich durchführen und habe eine Zuweisung codiert.
1
if(counter[btKanalNr]=255){
Was das Programm dann wirklich macht kann ich nicht so recht
nachvollziehen. Jedenfalls war das Verhalten verwirrend.
Mit der korrekten Schreibweise läuft das Programm einwandfrei.
1
if(counter[btKanalNr]==255){
Dein Tipp die Parameter zu tauschen ist zumindest für Anfänger sehr
hilfreich weil der Compiler dann eher einen Schreibfehler entdeckt.
1
if(255==counter[btKanalNr]){
Die Ardurino IDE ist eben nicht mit Visual Studio von Microsoft zu
vergleichen.
Nochmals, vielen Dank.
mfg Klaus
>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.
Andreas B. schrieb:> Desweiteren findet sich in der Doku,> dass die "globalen" Variablen auf welche die IRS zugreift per DRAM_ATTR> Direktive abgesichert werden sollen.
1
DRAM_ATTRunsignedlongpulsStartIst[4];
2
DRAM_ATTRunsignedlongpulsIstZeit[4];
Ich nehme mal an das die Konstante nicht mit DRAM_ATTR zu deklarieren
ist.
1
constbyteendPrellZeit=100;
Ich nutze als Quelle gerne https://randomnerdtutorials.com .
Dort wird DRAM_ATTR wohl nirgendwo eingesetzt. Das heißt natürlich auch
wiederum nichts.
Bitte prüfe mal.
mfg Klaus
Klaus R. schrieb:> Anbei zeige ich Dir noch mal die betroffenen aktuellen Sourcen.
.... und als altes, erfahrenes Mitglied hier im Forum
berücksichtigen wir auch immer brav die Hinweise zum Posten
von Sourcecode, gelle?
1
Wichtige Regeln - erst lesen, dann posten!
2
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
>Bitte prüfe mal.
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.
Noch ein kleiner Hinweis, was du evtl. verbessern könntest. Die millis()
Routine liefert einen unsigned long zurück, d.h. nach ca. 50 Tage gibt
es einen Überlauf. Durch Umstellen der Entprellung kannst du das Problem
vermeiden. Einfach mal nach overflow+millis() googlen oder hier z.B.
eine Erklärung:
https://www.norwegiancreations.com/2018/10/arduino-tutorial-avoiding-the-overflow-issue-when-using-millis-and-micros/
Variablen, die in einem ISR-kontext verändert werden (z.B. deine
counter) müssen 'volatile' gekennzeichnet werden, damit der Compiler
nicht weg-optimiert.
Pit S. schrieb:> 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
Andreas B. schrieb:> Noch ein kleiner Hinweis, was du evtl. verbessern könntest. Die millis()> Routine liefert einen unsigned long zurück, d.h. nach ca. 50 Tage gibt> es einen Überlauf. Durch Umstellen der Entprellung kannst du das Problem> vermeiden. Einfach mal nach overflow+millis() googlen oder hier z.B.> eine Erklärung:> https://www.norwegiancreations.com/2018/10/arduino-tutorial-avoiding-the-overflow-issue-when-using-millis-and-micros/
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.
Andreas B. schrieb:> ESP8266_RTOS_SDKSven K. schrieb:> Hallo Klaus,>> versuche mal Krokodilmaul statt Gänsefüßchen wie bei den anderen> includes darüber.>> Um esp_attr.h>> Gruß Sven
Dein Tipp genügt manchmal, hier aber nicht. Der Header ist wirklich
nicht greifbar.
mfg Klaus.
Andreas B. schrieb:> 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
Klaus R. schrieb:> 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.
Google (https://www.google.com/search?&q=DRAM_ATTR) verrät, daß es sich
bei DRAM_ATTR um ein Attribut zur Steuerung der Lokalisierung der
Variable im DRAM handelt.
> Zumindest wird das Programm dadurch sicherer.
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.
Axel S. schrieb:> 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.