Neustart schlägt fehl, abhängig von SD-Karte

Ja das ist das Problem, von dem @compactflash immer berichtet hat. Bei ihm erzeugt das mit dem LTC2954 jedoch Probleme, weil das als Ausschaltsignal gewertet wird.

Genau so ist das bei mir , es passiert egal welchen Pin ich als Power_Pin definiere, kein anderer Pin zeigt das Verhalten, zumindest mit 5-6verschiedenen ausprobiert. Es passiert mit einem Develboard ohne angeschlossene Peripherie . Es hat mal funktioniert , für mich ganz klar ein Softwareproblem und ich vermute das Problem liegt in Arduino > 1.06
Ich verwende keinen Portexpander.

Ich denke, ich habe die Ursache für die Unterbrechung gefunden:

Port_Init() ruft Port_WriteInitMaskForOutputChannels() um Pins am PCA9555 auf Ausgang zu stellen. Allerdings schreibt es vor dem Config-Register noch das Output-Register, und zwar auf 0. PE_IO1_7 ist LOW-aktiv, also geht SW_POWER3.3 an.

Soweit, so gut, auch wenn ich kein Freund von Seiteneffekten bin (Power_Init() tut ja dann nichts).

Jetzt kommt aber AudioPlayer_Init(), ruft AudioPlayer_SetupVolumeAndAmps() und möchte PA_EN auf HIGH setzen. Dazu ruft es Port_Write() auf. Port_Write() hat einen Cache (Port_ExpanderPortsOutputChannelStatus) für den aktuellen Stand der Outputregister, der pauschal mit 255 initialisiert ist, nicht mit dem Wert, den Port_WriteInitMaskForOutputChannels() gesetzt hat.
Damit werden jetzt die Bits im Outputregister wieder auf 1 gesetzt, außer dem für PA_EN, das bleibt auf 0.

Jetzt ist SW_POWER3.3 also wieder aus.

Und nun kommt Power_PeripheralOn(), das auch Port_Write() aufruft, und damit das Bit für PE_IO1_7 im Outputregister wieder auf 0 setzt und SW_POWER3.3 geht wieder an.

Ich werde es heute Nachmittag mal probieren, ob ich mit einem Fix für diese Geschichte mein Neustartproblem gelöst bekomme.

3 „Gefällt mir“

Ich habe bei meinen Karten mit Selbstheilung seltsamerweise das Gegenteil beobachtet. Mit Neopixelring starteten die zuverlässiger. Vielleicht haben die Kondensatoren auf dem Ring (vor jeder LED) die 3,3V soweit stabilisiert, dass die SD Karte besser gelesen werden kann. Ich hätte aber auch eher die andere Richtung erwartet

Hier mal mein Signalverlauf :

Die gelbe Kurve ist die 3,3V Versorgungsspannung , die blaue der Power_Pin

Das deckt sich mit deinem Messergebnis. Es passiert bei mir nach etwa 2 Sekunden. Das liegt daran weil ich die Spannung komplett ausschalte. Der ESP geht bei mir nie in deep sleep, sondern wird hart ausgeschaltet. Es gibt auch einen Brown out im Monitor . Deshalb benötigt der ESP etwas länger zum Booten.
Das Bild ist fast 2 Jahre alt und seitdem „kämpfe“ ich damit . Ich habe das Problem zwar umschifft bin aber trotzdem sehr gespannt woran es liegt. Ich hatte geschrieben es liegt an Arduino >1.06. Zeitgleich war aber auch die Einführung der Power.cpp. Das deaktivieren dieser hat aber nicht geholfen .

Hmm, die blaue Linie wird für ca. 250ms herunter gezogen und zwar wirklich auf 0V, wenn ich das richtig sehe. Bei mir sind es 12,5ms und es geht nur auf 1,5V herunter.
Wenn Deine Schaltung und insbesondere der Code in setup() anders als das von biologist ist, könnte es natürlich trotzdem das gleiche Problem sein.

Ich habe einen Pullrequest mit meinem Fix gestellt: fix wrong states on PE output pins (and SD-card failure on restart) by r-schmidt · Pull Request #278 · biologist79/ESPuino · GitHub

Damit klappt bei mir der Neustart zu 100%.
Wenn es auch bei Deinem Problem helfen sollte, würde es mich natürlich freuen.

3 „Gefällt mir“

Hallo,
das hilft bei meinem Problem nicht. Ich hatte es auch nicht erwartet weil die Änderungen für PE sind , den ich nicht verwende. Ich bin nicht gut in Code, versuche aber mal herauszufinden ob es ähnliches bei Verwendung ohne PE gibt.

Ja , das ist ein „echtes“ LOW, was mir den ON/Off Controller ausschaltet. Hier ist eine Info zum LTC2954 und hier das Problem.

Habe Deinen Fix mit der blauen Platine (PE106) und der Mini4L (PE 115 + INVERT_POWER) getestet & Alles läuft wie gewünscht! Dein PR ist per Cherry-Pick in den DEV-Branch übernommen.

@36b6fp6s Vielen Dank für die genaue Buganalyse & den bereitgestellten Fix!

Danke für die Bereitstellung.
Was mir unklar ist: Warum wird da uint8_t zu unsigned int umgestellt? Gehst du davon aus, dass die Anzahl der Ports (derzeit 2) auf mehr als 255 steigt?

Ich vermeide spezifische Integertypen, wenn sie nicht unbedingt nötig sind, insbesondere als Schleifenzähler. Wenn man nicht gerade die native Registerbreite (also etwas das gleich int ist) erwischt, baut der Compiler zusätzliche Instruktionen zum Maskieren ein. Und der ESP ist ein 32-bitter, kein 8-bitter.
Z.B. würde

int a = 0;
for (uint8_t x = 0; x < 4; x++)
{
    a++;
}

effektiv zu dem hier werden:

int a = 0;
for (unsigned int x = 0; x < 4; x++, x &= 0xff)
{
    a++;
}

Das würde hier wohl nicht unbedingt passieren, da der Compiler die Schleifenzahl (2) kennt und die Schleife vermutlich einfach abrollen wird.

Bei Funktionsparametern gibt es einen vergleichbaren Effekt, weswegen man sich auch da auf „int“ und „unsigned int“ beschränken sollte, wenn es keinen guten Grund gibt, den Typ genauer zu spezifizieren.

3 „Gefällt mir“

Für mich liegt der gute Grund, das zu verwenden, einfach schon darin, dass ich beim Lesen des Codes direkt eine Vorstellung habe, wie groß die Zahl (grob) erwartbar sein wird, die sich darin befindet. Ich sehe jetzt nicht, dass wir das punktuell ändern und hätte die Datentypen gerne wieder so, wie sie vorher waren :slight_smile:.

1 „Gefällt mir“

kann muss aber nicht sein, viele Plattformen haben inzwischen spez. Befehle um sowas performant umzusetzen.
Ob der ESP32 das jetzt hat keine Ahnung…

Und die nächste Frage ist ob wir das überhaupt merken würden…

Ich hab mal einen kleinen Test aufgesetzt (aus eigenem Interesse):

#include <Arduino.h>

volatile int a;

void setup()
{
  unsigned long start=0;
  unsigned long stop=0;
  Serial.begin(115200);
  Serial.println("setup");

  start = millis();
  #pragma nounroll
  for (uint8_t x = 0; x < 250; x++)
  {
  #pragma nounroll
  for (uint8_t x = 0; x < 250; x++)
  {
    #pragma nounroll
    for (uint8_t x = 0; x < 250; x++)
    {
        asm("NOP");
        a++;
    }
  }
  }
  
  stop = millis();

  Serial.println(stop - start);

  start = millis();
  #pragma nounroll
  for (unsigned int x = 0; x < 250; x++)
  {
  #pragma nounroll
  for (unsigned int x = 0; x < 250; x++)
  {
    #pragma nounroll
    for (unsigned int x = 0; x < 250; x++)
    {
        asm("NOP");
        a++;
    }
  }
  }
  
  stop = millis();

  Serial.println(stop - start);
  
  Serial.println("setup END");
}

void loop() {
  // put your main code here, to run repeatedly:
}


Beide 3 fach Schleifen brauchen die gleiche Zeit (919 ms)

Ja, das wundert mich nicht. Heutige Compiler sind ja nicht ganz dumm und der hat in diesem Fall wohl tatsächlich das selbe erzeugt. Das wäre bei meinem Beispiel auch so gewesen. Evtl. müsste man mit -O0 compilieren, damit man es mit dem Code sieht (beim GCC 3.4 passierte es sogar bei -O2, der war etwas dümmer als heute). Das würde ich aber nicht auf den ESP laden, es könnte recht langsam sein. Da schaut man sich besser an, was der Disassembler ausspuckt.

Aber selbst wenn der Compiler unterschiedlichen Code erzeugt haben sollte, waren vermutlich die Sprünge und das „a++“ so teuer, dass die zusätzliche Maskierung von „x“ nicht auffallen würde. „x“ wird hier in einem Register gehalten, während „a“ als volatile immer wieder aus dem Cache geladen und zurückgeschrieben werden muss. Und da load und store üblicherweise nur am Anfang bzw. Ende der Pipeline passieren, dauert hier jeder Schleifendurchlauf mindestens so lange, wie die Pipeline lang ist. Und jeder Schleifendurchlauf bedeutet einen Sprung, was je nach Qualität der Sprungvorhersage richtig übel sein kann.

Prinzipiell geht es mir auch weniger um die zusätzliche Zeit, die evtl. gebraucht wird, sondern um den zusätzlichen ROM, der für unnötige Instruktionen gebraucht wird. Bei uCs mit 16MiB ROM ist das auch nicht mehr relevant, aber ich komme eher aus der Ecke, wo man mit jedem Byte geizen musste.

Ich bestehe nicht darauf, dass das so gemacht wird, es ist ja schließlich nicht mein Projekt. Und wenn ich mal wieder etwas beitragen sollte, werde ich auch versuchen, mich mehr an den vorhandenen Stil zu halten.

5 „Gefällt mir“

Problem ist erledigt @36b6fp6s hat mir freundlicherweise einen Patch (Einzeiler) dafür geschickt und es funktioniert wieder wie früher.

void Power_Init(void) {
#if (POWER >= 0 && POWER <= MAX_GPIO)
Port_Write(POWER, POWER_ON, false);
pinMode(POWER, OUTPUT); // Only necessary for GPIO. For port-expander it’s done (previously) via Port_init()
#endif
}

Tausend Dank dafür von mir. :+1:

Hi @tueddy

@36b6fp6s wollte eine PR dazu erstellen was ich zunächst nicht wollte. Ich habe es jetzt hinreichend getestet.
Folgende Änderung habe ich in Power.cpp erstellt:

void Power_Init(void) {

#if (POWER >= 0 && POWER <= MAX_GPIO)

#ifndef PORT_EXPANDER_ENABLE

Port_Write(POWER, POWER_ON, false);

#endif

Damit ist der Fehler bereinigt, es braucht keinen neuen #define und sollte sich auch sonst nirgendwo negativ auswirken. Kannst du das implementieren ?

@compactflash Leider kann ich noch nicht so wirklich etwas damit anfangen, kannst Du Deine ganze Power_Init() zeigen? Am besten formatiert mit grafik

Für mich sieht das nicht allgemeingültig aus:
Du schaltest in Power_Init() bereits auf POWER_ON? Weil das würde evt. nicht passen mit LPCD- Wakeupcheck, hier darf die Peripherie nicht zu früh eingeschaltet werden, egal ob mit/ohne Port-Expander. Oder habe ich etwas übersehen? Es muss nicht gleich ein PR sein, aber so ist es noch ein wenig mühsam…

Ja genau. Dieser Tipp ist von @36b6fp6s . Die Routine für den PE hatte diesen Fehler auch , sonst gäbe es diesen Thread nicht. Er hat das gefixt aber leider hat das bei meiner Anwendung nicht geholfen, erst das hier. Verwende keine Zeit mehr damit, ich füge die 1 Zeile hinzu und gut ist. Da ich 2 verschiedene Boards veröffentlicht habe und ich nicht weiß ob diese benutzt werden hätte ich das gerne gefixt. Ich hatte beim 1 Board über 3000 Downloads, bei Github weiß ich es nicht. Seit @biologist seine Boards vertreibt habe ich allerdings keine Anfragen mehr gehabt und deshalb ist es auch nicht mehr so wichtig.
Hier und hier ist noch etwas Info dazu.

Also mein Vorschlag war eigentlich der hier:

void Power_Init(void) {
#if (POWER >= 0 && POWER <= MAX_GPIO)
	Port_Write(POWER, POWER_ON, false);
	pinMode(POWER, OUTPUT); // Only necessary for GPIO. For port-expander it's done (previously) via Port_init()
#endif
}

Das ist aber nicht im Sinne des Erfinders der Power_Init(), weil damit ja schon frühzeitig die externe Spannung eingeschaltet wird. (Allerdings immer noch später als bei Verwendung des PE, wo es schon in Port_Init() passiert…)

Den Defaultwert einfach konfigurierbar zu machen, hilft auch nicht, denn ein Default POWER_OFF würde bei einem Neustart während des Startens die Spannung abschalten.

Eigentlich müsste man Power_Init() und Port_Init() als Parameter mitgeben, ob es ein Neustart oder ein Power-On war. Und dann kann in den Funktionen entschieden werden, ob und an welchen Pins herumgestellt wird.

Für @compactflash könnte das reichen:

void Power_Init(void) {
#if (POWER >= 0 && POWER <= MAX_GPIO)
	#ifdef POWER_DEFAULT
	Port_Write(POWER, POWER_DEFAULT, false);
	#endif
	pinMode(POWER, OUTPUT); // Only necessary for GPIO. For port-expander it's done (previously) via Port_init()
#endif
}

Dann kann er

#define POWER_DEFAULT POWER_ON

in seine settings.h schreiben und für alle anderen bleibt es wie es ist. Und es ist vielleicht etwas verständlicher, was der Code machen soll.

1 „Gefällt mir“

Habe es ausprobiert, es funktioniert .

Ihr habt natürlich recht und mit dieser Lösung wäre auch diese Variante abgedeckt.

Danke nochmals

Es ist auf jeden Fall schon einmal gut, dass du dir darüber Gedanken machst, wie das sinnvoll integriert werden kann.
Es ist auch nicht immer leicht, den Überblick über alle Interaktionen an verschiedenen Stellen im Code zu behalten. Ebenso hat die Reihenfolge der Funktionen in main schon oft Probleme gemacht.

Meine persönlich favorisierte Lösung wäre immer noch, den POWER pin weiterhin nur als Peripherie-Pin zu betrachten: eine Doppelverwendung verwirrt nur. Den tatsächlichen board-strom kann ja ein anderer Pin mit fast gleicher Verwendung steuern.

Aber solange daran gedacht wird, dass viele verschiedene Schaltungen und Konfigurationen im Einsatz sind, und nicht einfach wild Änderungen vorgenommen werden, die nur zur eigenen Hardware passen, sollte auch eine andere (wie von dir vorgeschlagene Lösung) passen.
Wenn jetzt nämlich einfach das Standardverhalten angepasst wird, kommt in ein paar Wochen der nächste und schreit dass das letzte Update einen Fehler eingeführt hat.