"ungenutzte" Wire.h verbraucht Speicher

Ich suche mir schon einen Wolf:

//                          PROGMEM 1490/ SRAM 188 ohne alles
#include <SPI.h>  // macht nichts
#include <Wire.h> // 2680/301

void setup() {
  Serial.begin(115200);
  Serial.println(F("test")); 
}

void loop() {
}

Wenn man die Wire.h included werden etwa 1200 Byte verbraucht bevor man überhaupt das erste Wire.begin() setzt.

Auf was ist das zurückzuführen?

Und dann habe ich noch eine Anschlussfrage:

Ich habe eine größere Library für I2C LCDs. Jetzt will eine Unterstützung für SPI Portexpander ergänzen.

Wenn ich nun von meiner (I2C) Basisklasse in eine "SPI" Klasse runtervererbe, nehme ich offenbar den ganzen Wire.h Overhead mit.

Wie schafft man den eine Aufteilung der Library in I2C und SPI, sodass wirklich nur entweder i2c oder SPI included wird? Nur mit zwei separaten #includes im Usersketch?
Und dann als Ausgangsbasis eine Basisklasse, die weder I2C noch SPI benötigt (im Anwendungsfall also eine LCD Klasse für direkten Pin-Anschluss oder noch besser eine dummy Klasse ohne Pins, I2C Adressen oder CS Pin?)

noiasca:
Ich suche mir schon einen Wolf:

#include <Wire.h> // 2680/301

Wenn man die Wire.h included werden etwa 1200 Byte verbraucht bevor man überhaupt das erste Wire.begin() setzt.

Auf was ist das zurückzuführen?

Der Speicherverbrauch ist auch noch abhängig von der verwendeten IDE-Version.
Inklusive Fehlermeldungen.

Für'n MEGA:
// ohne: 656 / 9
// IDE 1.8.10: 1416 / 116
// IDE 1.8.13 2152 / 122
In der 1.8.10 dann dazu noch:

hardware/arduino/avr/libraries/Wire/src/utility/twi.c: In function '__vector_39':
hardware/arduino/avr/libraries/Wire/src/utility/twi.c:447:49: warning: this statement may fall through [-Wimplicit-fallthrough=]
       twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
                                                 ^
hardware/arduino/avr/libraries/Wire/src/utility/twi.c:448:5: note: here
     case TW_MR_SLA_ACK:  // address sent, ack received
     ^~~~
hardware/arduino/avr/libraries/Wire/src/utility/twi.c:529:9: warning: this statement may fall through [-Wimplicit-fallthrough=]
       if(0 == twi_txBufferLength){
         ^
hardware/arduino/avr/libraries/Wire/src/utility/twi.c:534:5: note: here
     case TW_ST_DATA_ACK: // byte sent, ack returned
     ^~~~

vermutlich wird mit einbinden nach der Hardware geschaut und die I2C-Hardware "vorkonfiguriert"...

Wenn man die Wire.h included werden etwa 1200 Byte verbraucht bevor man überhaupt das erste Wire.begin() setzt.

Auf was ist das zurückzuführen?

Schaut man in die twi.c finden sich eine menge Variablen/Arrays, welche als volatile deklariert sind.
Und auch einen recht großem Automaten in einer ISR.

Das ist per se nicht böse, wenn es denn gebraucht wird, kann der Compiler aber nur schlecht optimieren.

Die Wire Instanz kann er bei Nichtbenutzung entfallen lassen, aber nicht den zugehörigen Kram.

Das führt zu der einfachen Regel:
Wenn man Wire nicht nutzt, darf man es nicht einbinden.

Warum können andere Libs das?
z.B. Serial

Denn, wird Serial nicht verwendet, dann werden auch nicht die UART ISR etabliert.

Der Grund dürfte sein, dass die HardwareSerial Klasse die volatile Arrays enthält.
Kann die Serial Instanz weg optimiert werden, dann entfallen auch die volatilen Arrays.
Und da die ISR nur eine Instanz Methode aufruft, kann/muss auch diese dann auch entfallen.

Da steckt dann auch eine mögliche Lösung:
Der gnadenlose Umbau der Wire Klasse!

Wobei ich denke, dass das nicht die schlechteste Wahl wäre....
Wenn man es breit genug anlegt.

Ein gemeinsame Basis/Interface Klasse für alle I2C Instanzen.
TinyWire, Wire, Wire1, ESP Wire, SoftwareWire und viele weitere

Denn mal abgesehen vom Speicherverbrauch, ist es auch schwierig, eine I2C EEPROM Klasse zu bauen, welche auf dem DUE mit EEPROMS mit Wire1 und Wire, umgehen kann. Soll auch noch TinyWire bedient werden, hörts dann schon fast ganz auf.

Viele viele Fremdlibs basieren auf Wire.
Eine Verwendung von anderen I2C Implementierungen ist nicht vorgesehen.

Hier krankelt es etwas in der Arduinowelt!

karma+

Viele viele Fremdlibs basieren auf Wire.
Eine Verwendung von anderen I2C Implementierungen ist nicht vorgesehen.

ja. Dazu kommt dann der fixe include von Wire.h und ein Wire.begin() in vielen Libs...

weitergedacht, sollte man vieleicht die ganzen

Wire.beginTransmission
Wire.write
Wire.endTransmission
etc,

als extern deklarieren und die Wire.h im Usersketch einbinden lassen?

Angenommen ich habe eine Library für 16x2 Displays.
Zunächst gibt es eine Klasse

LiquidCrystal_dummy

von der vererbe ich dan runter in 3 HW Implemtierungen:

LiquidCrystal_4bit
LiquidCrystal_SPI_4bit
LiquidCrystal_I2C

Dann habe die HW-Implementierungen in Unterverzeichnisse verschoben und separate .h files erstellt.

NoiascaLiquidCrystal.png

Man muss also im Usersketch sowohl die Libary wie auch die jeweilige Hardware Includen.

Z.B. eben

#include <NoiascaLiquidCrystal.h>      // 
#include <4bit/lcd_4bit.h>             // parallel interface, 4 bit Mode
LiquidCrystal_4bit lcd(rs, en, d4, d5, d6, d7, bl, cols, rows);  // create lcd object

trotzdem sieht man, dass die Implementierungen für SPI und I2C mit eingebunden werden, und somit die I2C wieder zuschlägt:

ompiling libraries...
Compiling library "NoiascaLiquidCrystal"
Zuvor kompilierte Datei wird verwendet: C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\NoiascaLiquidCrystal\NoiascaLiquidCrystal.cpp.o
Zuvor kompilierte Datei wird verwendet: C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\NoiascaLiquidCrystal\4bit\lcd_4bit.cpp.o
Zuvor kompilierte Datei wird verwendet: C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\NoiascaLiquidCrystal\i2c\lcd_i2c.cpp.o
Zuvor kompilierte Datei wird verwendet: C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\NoiascaLiquidCrystal\spi\lcd_spi.cpp.o
Compiling library "Wire"
"C:\Users\werner\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10813 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\cores\arduino" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\variants\standard" "-IC:\Daten\myrepository\Arduino\libraries\NoiascaLiquidCrystal\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\Wire\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\SPI\src" "C:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\Wire\src\Wire.cpp" -o "C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\Wire\Wire.cpp.o"
"C:\Users\werner\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-gcc" -c -g -Os -Wall -Wextra -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10813 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\cores\arduino" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\variants\standard" "-IC:\Daten\myrepository\Arduino\libraries\NoiascaLiquidCrystal\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\Wire\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\SPI\src" "C:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\Wire\src\utility\twi.c" -o "C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\Wire\utility\twi.c.o"
Compiling library "SPI"
"C:\Users\werner\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10813 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\cores\arduino" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\variants\standard" "-IC:\Daten\myrepository\Arduino\libraries\NoiascaLiquidCrystal\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\Wire\src" "-IC:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\SPI\src" "C:\Users\werner\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3\libraries\SPI\src\SPI.cpp" -o "C:\Users\werner\AppData\Local\Temp\arduino_build_734020\libraries\SPI\SPI.cpp.o"
Compiling core...

Kann man das vieleicht mit einer anderen Include-Struktur lösen, oder geht das im Arduino Umfeld nur mit 4 separaten Libraries auf gleicher Ebene (was aber schon eigenartig wäre).

P.S.: das Zip enthält eine "grobe" Library, die 3 LCDs laufen damit.

NoiascaLiquidCrystal.png

NoiascaLiquidCrystal.zip (20.5 KB)

Ich könnte mir sowas, in etwa, vorstellen.
Bedeutet natürlich, dass man ein paar Treiberklassen/Spezialisierungen schreiben muss

#include <SPI.h>
#include <DriverSPI.h>



template<Schnittstelle> class LiquidCrystal_Basis{};

//von der vererbe ich dann runter in 3 HW Implemtierungen:


//LiquidCrystal<Driver4bit> lcd(3,4,5,6); // pins
LiquidCrystal<DriverSPI> lcd(SPI,10); // SPI Instanz, CS Pin
//LiquidCrystal<DriverI2C>lcd(Wire1,43); // Wire Instanz, adresse

nur was hilft das in einer Lib? in der kommt es ja wieder zu einer #include <Wire.h> ... ergibt das nicht wieder das gleiche Problem?

OK, dann etwas konkreter .....

// Programm mit SPI Ausgabe

#include <SPI.h>
#include <DriverSPI.h>


LiquidCrystal<DriverSPI> lcd(DriverSPI(SPI),10); // SPI Instanz, CS Pin
// Programm mit Wire Ausgabe

#include <Wire.h>
#include <DriverI2C>

LiquidCrystal<DriverI2C>lcd(DriverI2C(Wire1,43)); // Wire Instanz, adresse

Deine LiquidCrystal Klasse muss dann nicht mehr die konkrete Arduino Schnittstelle kennen, sondern nur die Treiberklasse
Und eben die Treiberklasssen, kann man dann einzeln einbinden, und benötigt nicht den ganzen Wust.

du gehst aber davon aus dass die Files von

#include <SPI.h>
#include <DriverSPI.h>
#include

in drei separaten Libraries liegen oder wie liegst du sie in einer ab?

:o

MyLCD.zip (2.44 KB)

danke.
Werde ich am Abend analysieren!

dritter Abend. Ich kann noch nicht folgen.
Liegt es daran dass du nur .h files nutzt?

Glaube ich nicht.

Wo ist dein Problem?

Also:
Bei einer Library im 1.5.x+ Format (also mit den .cpp / .h Dateien im src Verzeichnis)
werden alle cpp/.h files im src Verzeichnis gelinkt.

"The source code found in src folder and all its subfolders is compiled and linked in the user’s sketch."
https://arduino.github.io/arduino-cli/library-specification/

Damit erklärt sich wohl das Phänomen aus meinem Eingangspost.

Daher muss man wohl das ältere Lib-Format verwenden. D.h. die library.cpp/library.h im Hauptverzeichnis der Library.

Der optionale Include auf .h Dateien in irgend einem Unterverzeichnis klappt dann auch wie vorgesehen.
Den Include auf aufgeteilte .cpp/.h Dateien in ein Unterverzeichnis bekomme ich weiterhin nicht hin. Scheitere immer wieder am Include.

Auch wenn ich gern 1.5.x+ und .cpp/.h genutzt hätte - ich lass das mal, und kümmere mich nun um den eigentlichen Source...

Brauchst du die *cpp Dateien überhaupt?

Leider sehe ich deine Dateien nicht, so dass mir leider auch nix einfällt, was man besser machen könnte.


Gerade getestet.
Wire wird mit eingebunden, ob man will oder nicht.

Es bleibt dabei:

Brauchst du die *cpp Dateien überhaupt?

"brauchen" ist gut gesagt, ich hab mir halt angewohnt in .h und .cpp zu teilen.

Leider sehe ich deine Dateien nicht, so dass mir leider auch nix einfällt, was man besser machen könnte.

in Beitrag #4 habe ich die gesamte Lib als ZIP inkl. 3 Beispielen angefügt. Zumindest compilierbare Prototypen.

Es hat sich aber insofern erübrigt, als ich jetzt die Driver Sachen eben als reine .h und die Lib im alten Format mache.

Jetzt noch ein wenig Hübsch machen und das RGB Sureeno Display dazu geben, dann sollte ich irgendwann nächste Woche mal fertig werden...

noiasca:
Jetzt noch ein wenig Hübsch machen und das RGB Sureeno Display dazu geben, dann sollte ich irgendwann nächste Woche mal fertig werden...

Ums mal ganz ehrlich zu sagen - ich bin Euch die ganze Zeit "gefolgt" - aber so richtig bin ich da nicht bei.
Gut. sicher auch, weil ich die Frage noch nie in der Form hatte.

Aber nur aus reinem Interesse: Bitte bitte - Am Ende:
a) Lösung präsentieren
b) den Grund dafür

Schöne Woche!

vereinfacht die Motivation:

LCD's kennst ja sicher.
Über die IDE kannst du eine
Liquid Crystal und eine
Liquid Crystal I2C
installieren.

97% des Codes ist gleich. Unterscheidet sich nur minimal - einmal gibts DigitalWrite auf Pins, beim zweiten wird der PCF Portexpander über I2C angesprochen.

Für ein anderes Projekt brauchte ich einen Expander mit MCP23S08 - wegen SPI. Auch das ist wieder nur ein paar Änderungen für die Zugriffe.

Und da ich ja eine Lib habe, die Umlaute und Sonderzeichen aus den oberen 127 Characters konvertiert bzw. UTF8 mag (äöüß), will ich das nicht in 3 separate Libs einbauen, sondern in einer und die Hardware-Schicht / die "Treiber" austauschen.

Und dann kam noch das RGB-Display von Sureeno ... das ist zwar I2C aber nicht mit dem PCF Chip sondern wieder ganz was anderes.

Und der Teufel steckte halt im Detail, also muss ich eben ein paar Umweg machen. So lange es am Ende funktioniert, eh wurst ...

noiasca:
vereinfacht die Motivation:

Und der Teufel steckte halt im Detail,

Ho Ho Ho - Manchmal auch im NoiseReduce... :wink: :wink:

Vielen Dank für die Ausführung!!!
So detailiert werd' ichs wohl - in meinem Leben - nicht brauchen, find das aber sehr spannend und auch für die Nachwelt wichtig.
Selbst die Erklärung leuchtet ein ++

hat jetzt länger gedauert, aber es funkt.

Momentan mit den I2C Backpacks, mit dem SPI MCP23S08 und das Surenoo Display mit dem nativen (8bit) I2C.

Am parallel interface muss ich noch ein wenig arbeiten, das will ich so noch nicht dazu geben.

An der Doku muss man auch noch feilen.

Download am Ende der Seite:
https://werner.rothschopf.net/202009_arduino_liquid_crystal_wire_en.htm

das wenige was da steht: trotzdem lesen :wink: