Hallo,
aus der leidigen Erfahrung eines "Stackoverflow" heraus bei den winzigen
2k eines AVR328 muss nun optimiert werden. Stringverarbeitung hat es
leider in sich, auch sprintf nimmt sich einen fetten Batzen des Stacks.
Versuche mit malloc und calloc waren fruchtlos, nach 4-5 Durchläufen kam
nur noch ein NULL Zeiger zurück, obwohl alles ge-free()'t wurde. Keine
Ahnung wieso, beim stm32 lief das alles immer prima. ich weiss aber auch
nicht wie gross der Heap beim Arduino eingestellt worden ist.
Annahme:
void nixgut() {
int a,b,c;
(.....)
{
int e,f,g;
}
(weiter im Text)
}
a,b,c, liegen in einer inneren Klammer und sind außerhalb dieser nicht
sichtbar. Aber reduziert sich damit auch wirklich der Stack, damit das
Nachfolgende mehr Platz hat? Ich versuche alles nur solange leben zu
lassen wie es gebraucht wird und keine Variable mehr am Anfang zu
deklarieren.
Christian
Christian J. schrieb:> Versuche mit malloc und calloc waren fruchtlos
Die machen es eher schlimmer. Der Verwaltungsoverhead und die ggf.
suboptimale Aufteilung erhöhen den Bedarf.
Christian J. schrieb:> beim stm32 lief das alles immer prima.
Die haben ja auch viel mehr Speicher...
Christian J. schrieb:> Aber reduziert sich damit auch wirklich der Stack, damit das> Nachfolgende mehr Platz hat?
Nein. Compiler optimieren Funktionsaufrufe meist so, dass einmal zu
Beginn alles benötigte allokiert wird. Es werden aber durchaus
Stack-Plätze wiederverwendet, falls möglich.
Schau dir doch einfach den erzeugen Assembler-Code an, der räumte alle
Zweifel aus. Und zeig mal den ganzen Code, die paar Integer-Variablen
sind überhaupt kein Problem.
Niklas G. schrieb:> Nein. Compiler optimieren Funktionsaufrufe meist so, dass einmal zu> Beginn alles benötigte allokiert wird.
Oder auch nicht.
Vielleicht hält der Compiler die auch gar nicht auf dem Stack, sondern
nur in Registern. Da ist alles möglich.
Oliver
Niklas G. schrieb:> Daher eben den Assembler-Code anschauen und sich das Raten ersparen.
Geht leider nicht in der Arduino IDE und selbst bei EmBitz ist das ein
Problem, was nicht ohne Skripte lösbar ist :-(
Hätte ja sein können, dass die Klammern eben ein Abbauen des Stack Top
verursachen.
Der Code soll so schonend wie möglich für den Speicher geschrieben
werden.
Christian J. schrieb:> Geht leider nicht in der Arduino IDE
Kann man sehr wohl:
Beitrag "Re: Auf Arduino erstelltes Programm in C und Assembler anschauen"Christian J. schrieb:> Der Code soll so schonend wie möglich für den Speicher geschrieben> werden.
Jaa.
Christian J. schrieb:> char MsgToSend[160];Christian J. schrieb:> char signal[6];Christian J. schrieb:> char atcmd[40];
Du kannst auch berechnete kurze Strings oder einzelne Zeichen sofort
absenden, statt sie erst komplett zwischenzuspeichern. Du kannst statt
sprintf itoa und strcpy nutzen. Du kannst den Code auf einzelne
Funktionen aufteilen, die nacheinander aufgerufen werden. Du kannst die
Puffer ggf. global allokieren damit der Linker den Speicherverbrauch
berechnet.
Niklas G. schrieb:> Du kannst den Code auf einzelne> Funktionen aufteilen, die nacheinander aufgerufen werden.
Das habe ich schon gemacht. Eine Funktion berechnet die Strings, ein
Flag, Die nächste nimmt das Flag und arbeitet werden, damit keine
Verschachtelungen
entstehen. Und ja, das GSM Modul wartet solange bis alles da ist nach
dem ">". Das liesse sich auch noch ausnutzen. Global ist die letzte
Lösungh, wenn alles nicht geht. Ein Puffer für alles, sind ja ein paar
Module, die sowas machen. Aber nie alle gleichzeitig.
Am liebsten wäre ja calloc (strlen(....))) gewesen aber das haute gar
nicht hin, 2 Std für die Katz. Ich lese mir das nachher mal durch,
scheint ne größere Baustelle zu sein. Beim stm32 habe ich mixed Code im
Debug Fenster, da ist da sehr schön zu sehen und vor allem per JTAG und
nicht Bootloader...
Weisst du vielleicht auch warum man mit strstr nicht nach dem "+"
Zeichen suchen kann? Er nimmt alles, nur das nicht.
Niklas G. schrieb:> Du kannst auch berechnete kurze Strings oder einzelne Zeichen sofort> absenden, statt sie erst komplett zwischenzuspeichern.
Er könnte zumindest Pointer verwenden. Es gibt keinen Grund dafür, die
String-Konstanten erst in einen Buffer zu kopieren.
Beispiel:
1
constchar*signal;
2
3
switch(CSQuality){
4
case1:signal="-";break;
5
case2:signal="+";break;
6
case3:signal="++";break;
7
case4:signal="+++";break;
8
default:signal="NA";break;
9
}
> Du kannst statt sprintf itoa und strcpy nutzen.
Jepp. Das hier:
/* SMS Text aus Progmem oder RAM lesen und verketten */
3
charMsgContent[MAX_MSG_LENGTH+2];
4
if(mode==FLASH)
5
ReadFromFlash(MsgContent,text);
6
else{
7
if(strlen(text)>sizeof(MsgToSend)){
8
debugln(F("Text zu lang!"));
9
return0;
10
}
11
strcpy(MsgContent,text);
12
}
13
14
/* Hauptstring zusammen fügen */
15
strcat(MsgToSend,MsgContent);
16
}
schmeisst nur mit RAM so um sich. Da muss überhaupt nichts erst in ein
Array kopiert werden.
Könnte man zum Beispiel so machen:
1
/* SMS Text aus Progmem oder RAM lesen und verketten */
2
3
if(mode==FLASH)
4
ReadFromFlash(MsgToSend+strlen(MsgToSend),text);
5
else{
6
if(strlen(text)>sizeof(MsgToSend)){
7
debugln(F("Text zu lang!"));
8
return0;
9
}
10
strcat(MsgToSend,text);
11
}
Den Kunstgriff mit den umschließenden Klammern kann man hier direkt
vermeiden und man braucht kein Array. Zudem ist der Code kürzer.
Obwohl ich nicht ganz diese Prüfung auf die Länge verstehe. Der TO
arbeitet hier mit strcat(), setzt also zwei Strings zusammen. Er prüft
aber nur auf die Länge von text. Das reicht aber nicht. Es müsste
heißen:
Beachte auch ">=" statt dem falschen ">".
Warum diese Prüfung überhaupt gar nicht gemacht wird, wenn der Text aus
dem Flash kommt, ist mir auch schleierhaft.
Frank M. schrieb:> Er könnte zumindest Pointer verwenden. Es gibt keinen Grund dafür, die> String-Konstanten erst in einen Buffer zu kopieren.
Das erzeugt genau 34 Bytes mehr "globale Variablen" und reduziert den
Platz für lokale um 34 Bytes.
Aber ansonsten setze ich deine guten Ideen grad mal um.... man fängt ja
erst an zu sparen, wenn es eng wird. Die Funktion ist ja erstmal da.
Christian J. schrieb:> if (flags.ShowLED) strcpy(leds,"LED");> else strcpy(leds,"NoLED");
Dir ist klar, dass hier die Strings "LED" und "NoLED" beide erstmal vom
Flash in den RAM kopiert und dann je nach Ergebnis der if-Abfrage einer
davon per strcpy noch ein zweites mal im RAM kopiert wird?
Lies dich mal darüber ein, wie man Stringliterale im Flash benutzt und
was strcpy_P macht.
Rolf M. schrieb:> Dir ist klar, dass hier der String "NoLED" erst vom Flash in den RAM> kopiert und dann per strcpy noch ein zweites mal im RAM kopiert wird?
Ja... weiss ich .... irgendwo muss es ja liegen und das kann nur im
Flash sein und bei der Init Sequenz wird es zugewiesen. Das sind
ellenlange Sequenzen bei jeder Funktion am Anfang.
Christian J. schrieb:> iklas G. schrieb:>> Daher eben den Assembler-Code anschauen und sich das Raten ersparen.>> Geht leider nicht in der Arduino IDE und selbst bei EmBitz ist das ein> Problem, was nicht ohne Skripte lösbar ist :-(>> Hätte ja sein können, dass die Klammern eben ein Abbauen des Stack Top> verursachen.>> Der Code soll so schonend wie möglich für den Speicher geschrieben> werden.
Dann sollte man sich mal mit dem Thema PROGMEM vertraut machen, beim
Arduino ist das die Funktion F(). Damit werden konstante Strings in den
Flash gepackt. Im jetzigen Zustand verschwenden all deine konstanten
Strings viel RAM. Klingt komisch, ist aber so.
https://www.arduino.cc/reference/en/language/variables/utilities/progmem/
Christian J. schrieb:> Rolf M. schrieb:>> Dir ist klar, dass hier der String "NoLED" erst vom Flash in den RAM>> kopiert und dann per strcpy noch ein zweites mal im RAM kopiert wird?>> Ja... weiss ich .... irgendwo muss es ja liegen und das kann nur im> Flash sein und bei der Init Sequenz wird es zugewiesen.
Es reicht aber, wenn es einmal im Flash liegt und nicht zusätzlich dazu
noch einmal im ja eh schon so knappen RAM.
Naja, kenne ich schon, sämtliche Nachrichten liegen da auch und werden
erst geholt, wenn sie gebraucht werden. Aber so ganz bin ich da noch
nicht durch, geht vielleicht noch besser.
1
constcharflash_sysdown[]PROGMEM="Shutdown!";
2
constcharflash_alarm_off[]PROGMEM="Alarm ist jetzt AUS!";
3
constcharflash_alarm_on[]PROGMEM="Alarm ist jetzt scharf!";
Christian J. schrieb:> Aber so ganz bin ich da noch nicht durch, geht vielleicht noch besser.
Statt die Strings aus dem Flash ins Ram zu kopieren, um sie dann
irgendwo hin auszugeben, könntest Du auch eine Variante Deiner
Ausgabefunktion bauen, der direkt ein String aus dem Flash übergeben
wird.
Rufus Τ. F. schrieb:> Statt die Strings aus dem Flash ins Ram zu kopieren, um sie dann> irgendwo hin auszugeben, könntest Du auch eine Variante Deiner> Ausgabefunktion bauen, der direkt ein String aus dem Flash übergeben> wird.
Das Schleudern fing erst an als ich um meine Bastelei aufzubohren anfing
Flash Strings und sprintf dynamische Strings zu sammen zu bauen. Ab da
wurde es immer unübersichtlicher und ich habe diesen Monat bereits 50
Euro nur an sms kosten verbraten auf der Pay Card, weil das kaum
simulierbar ist. Wird Zeit ne flat zu bestellen.....
strcpy_P(leds,PSTR("NoLED"));
---->
Der Sketch verwendet 15114 Bytes (49%) des Programmspeicherplatzes. Das
Maximum sind 30720 Bytes. Globale Variablen verwenden 1397 Bytes (68%)
des dynamischen Speichers, 651 Bytes für lokale Variablen verbleiben.
Das Maximum sind 2048 Bytes.
vs
Der Sketch verwendet 15096 Bytes (49%) des Programmspeicherplatzes. Das
Maximum sind 30720 Bytes.
Globale Variablen verwenden 1403 Bytes (68%) des dynamischen Speichers,
645 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
Spart also die 6 Bytes RAM ein!
Junge, Junge, ihr habts echt drauf.... !
Christian J. schrieb:> Junge, Junge, ihr habts echt drauf.... !
Ungefähr so? "Jungs und Mädels, hier habt ihr ein paar Zeilen meines
Programms. Es funktioniert nicht, weil das Grundkonzept im Umgang mit
Speicher von A bin Z vergurkt ist und jede Korrektur wie beim Mikado
endet. Löst mein Problem, aber dalli, sonst setzt es Spott."
Wenn man merkt, dass das Pferd tot ist, sollte man absteigen (*).
Passiert öfter, dass man nochmal ans Reissbrett muss.
*: Für Freunde der Bürokratie, eine nette Sammlung alternativer
Lösungsvorschläge: http://www.roland-schaefer.de/totespferd.htm
A. K. schrieb:> Löst mein Problem, aber dalli, sonst setzt es Spott."
Ich befasse mich seit 4 Wochen mit Arduino.... vorher habe ich damit
nichts zu tun gehabt. Das nur dazu.
Christian J. schrieb:> Ich befasse mich seit 4 Wochen mit Arduino.... vorher habe ich damit> nichts zu tun gehabt. Das nur dazu.
Kein Problem damit. Aber ob es zur Lösung beträgt, in dieser Lage die
bisherigen Hilfesteller zu verspotten?
Mein Tipp mit dem Reissbrett war ernst gemeint. So lange du kein Konzept
zustande bringst, das radikal RAM einspart, wird das bestenfalls
Flickwerk.
Christian J. schrieb:> Spart also die 6 Bytes RAM ein!
Du hast ja nicht alles umgesetzt, ich habe Dir beschrieben, wie Du
dieses hier komplett einsparen kannst:
1
charMsgContent[MAX_MSG_LENGTH+2];
Das sind weitaus mehr als 6 Bytes, die erst mal auf dem Stack angelegt
werden wollen!
Desweiteren kannst Du auch dieses hier
1
charMsgToSend[160];
komplett einsparen, wenn Du die Strings und numerischen Werte sofort
rauspustest, statt sie erstmal in diesem Array zu sammeln.
Das sind schon mal 160 + 162 = 322 Bytes weniger auf dem Stack.
> Junge, Junge, ihr habts echt drauf.... !
Setze doch erstmal alles um und präsentiere Deinen korrigierten Code,
bevor Du ein Urteil fällst.
Vielleicht nützt das was, vielleicht nicht: Seit vielen Jahren gibts im
AVR Compiler auch managed pointer, ich weiss nur nicht mehr wie die da
heissen. Sind Pointer, die auf ROM und RAM zeigen können und zur
Laufzeit anhand des Wertes entsprechend drauf zugreifen. Etwas
langsamer, etwas mehr Code, aber weniger nervend.
Und schon wieder 40 Bytes eingespart.
Und komm mir jetzt nicht mit dem Output vom Linke/Compiler. Ich glaube
nicht, dass der Arrays, die auf dem Stack liegen, überhaupt mitzählt.
Da gibt es noch jede Menge mehr dieser Codeoptimierungen, die gemacht
werden könnten. Ich werde Dir aber jetzt nicht alle Stellen vorlesen. Du
musst obiges nur auf die anderen Stellen genauso übertragen.
Christian J. schrieb:> strcpy_P(leds,PSTR("NoLED"));>> ---->>> Der Sketch verwendet 15114 Bytes (49%) des Programmspeicherplatzes.
Falscher Ansatz, statt die Strings in lowpower[], leds[] zu sammeln,
kannst Du auch hier direkt die Strings ausgeben.
A. K. schrieb:> Vielleicht nützt das was, vielleicht nicht: Seit vielen Jahren> gibts im> AVR Compiler auch managed pointer, ich weiss nur nicht mehr wie die da> heissen. Sind Pointer, die auf ROM und RAM zeigen können und zur> Laufzeit anhand des Wertes entsprechend drauf zugreifen. Etwas> langsamer, etwas mehr Code, aber weniger nervend.
__memx
Oliver
Zwischenmeldung,
aktuell sind über 300 Bytes eingespart worden an RAM durch Die
Ratschläge oben. dauert aber alles etwas...... da ist viel schief
gelaufen.....
Ich glaube nicht, dass der Arrays, die auf dem Stack liegen, überhaupt
mitzählt.
Nein, tut er nicht.... daher ja die Abstürze.....
Christian J. schrieb:> Spart also die 6 Bytes RAM ein!
Du musst das natürlich nicht nur mit einem String machen, sondern mit
allen!
> Junge, Junge, ihr habts echt drauf.... !
Findest du es ok, andere für ihre Hilfe auch noch blöd anzumachen, statt
zu versuchen, es zu verstehen?
Christian J. schrieb:> Ich glaube nicht, dass der Arrays, die auf dem Stack liegen, überhaupt> mitzählt.>> Nein, tut er nicht.... daher ja die Abstürze.....
Natürlich nicht. Der Stack ist keine globale Variable. Die Arrays
existieren zur Compilezeit noch nicht.
Findest du es ok, andere für ihre Hilfe auch noch blöd anzumachen, statt
zu versuchen, es zu verstehen?
Es scheint unmöglich zu sein, sich hier normal zu unterhalten, ohne dass
früher oder später irgendjemand kommt, der irgendwas falsch auffasst und
dann in den persönlichen Angriff übergeht. Das gilt auch für Mods. Das
war ein Lob, aber egal.
Christian J. schrieb:> Das gilt auch für Mods. Das war ein Lob, aber egal.
Der letzte Satz liest sich halt mal überhaupt nicht so, mit dem "Junge,
Junge" und den drei Punkten. Aber wenn's nicht bös gemeint war, dann ist
ja alles gut.
Hier haut das nicht hin mit dem Makro, gibt nur Müll auf der
seriellen.... nehme ich das PSTR weg ist alles wieder gut.
/* Setze 9600 baud */
SIM800_SendAT(PSTR("AT+IPR=9600\r\n"),"OK");
/* Meldungen abschalten */
SIM800_SendAT(PSTR("AT+CCWA=0\r\n"),"OK");
/* Echo abschalten */
SIM800_SendAT(PSTR("ATE0\r\n"),"OK");
da ich gut 100 dieser Sequenzen haben sind das die größten Ram Fresser.
Christian J. schrieb:> Hier haut das nicht hin mit dem Makro, gibt nur Müll auf der> seriellen.... nehme ich das PSTR weg ist alles wieder gut.
Du darfst das PSTR nur mit Funktionen verwenden, die den String auch im
Flash erwarten. Deshalb musst du ja auch strcpy_P statt strcpy
verwenden.
Rolf M. schrieb:> Du darfst das PSTR nur mit Funktionen verwenden, die den String auch im> Flash erwarten. Deshalb musst du ja auch strcpy_P statt strcpy> verwenden.
Ok, danke! Dann werde ich mir dafür mal ein Makro schreiben oder es
sonst wie kapseln..... mir qualmt die Birne aber jetzt, erstmal vor die
Türe gehen.....
SIM800_SendAT("AT+CSCLK=0\r\n","OK");
Denke mal eine Kapselung wäre das beste, das sind über 120 wie ich grad
gezählt habe von diesen Aufrufen...
Sag mal Meister,
baue ich jetzt kompletten Blödsinn? Bin schon etwas erschöpft aber noch
nicht fertig eben.... möchte diese konstanten Arrays weg haben und daher
doch wieder auf malloc. Gibt zwei Sorten von SendAT, jene die konstant
sind aber auch welche mit Parameter, die errehcnet werden. daher zwei
Routinen jetzt. Läuft bisher....
Wie gesagt macht "malloc" verbrauchstechnisch alles schlimmer (und
langsamer). Das solltest du komplett vermeiden! Das Problem an dieser
Stelle ist ja nur, dass Arduino.print einen RAM-String braucht. Es soll
aber angeblich auch einfach so gehen:
1
serialSIM800.print(F(atc));
Erspart die Kopie komplett.
sizeof(char) ist übrigens immer 1, denn die Einheit, in welcher sizeof
die Größe angibt, ist in Vielfachen von char. Und da char immer genau 1
char groß ist, ist sizeof(char)=1. Gilt analog auf für signed char und
unsigned char.
Ok, das mit dem char weiss ich, ich schreibe auch if (a==true) statt nur
das a, damit es leslicher ist.
Jut, so langsam rieselt der Kalk und morgen gehts weiter im Text, heute
ist Ende Gelände, Festplatte voll nach 7h vor der Kiste.
Danke nochmal an alle für die wertvolle Hilfe!
Christian J. schrieb:> Ok, das mit dem char weiss ich, ich schreibe auch if (a==true) statt nur> das a, damit es leslicher ist.
Jeder C++-Programmierer weiß aber dass das so ist, stolpert über den
unnötig umständlichen Code und fragt sich ob da noch mehr
"falsch"/unnötig ist...
Niklas G. schrieb:> serialSIM800.print(F(atc));
Lässt sich nicht kompilieren.... angeblich ist macro F dafür nicht
geeignet. Hmmm
K:\ArduinoSoftware\radar_gsm_wachhund\simctrl.ino:260:21: note: in
expansion of macro 'F'
serialSIM800.print(F(atc));
74: error: array must be initialized with a brace-enclosed initializer
Das aber schon:
serialSIM800.print(F("Hello"));
Niklas G. schrieb:> Kenne mich mit Arduino auch nicht so gut aus. Das sind so die Gründe> warum ich ARM bevorzuge :-)
Ach.... das ist aber eine Neuigkeit... :-)))) Ich prügel mich damit auch
nur herum, weil der F103 auf dem Bluepill Board zu gross ist....
Christian J. schrieb:> Ich prügel mich damit auch> nur herum, weil der F103 auf dem Bluepill Board zu gross ist....
Wie, zu groß? Die gibt's in allen Größen...
Apropos ARM, mit dem 407 habe ich vor 2 Jahren mal was gemacht... plus 3
weitere Displayanzeigen im Haus... , alles DIY :-) Nur dem EEPROM, da
hat nie hingenauen mit der eingebauten Technik. Jetzt Software Ic2. Hab
da Tage mit verbracht :-(
Christian J. schrieb:> Jetzt Software Ic2.
Ja das Hardware-I²C der alten STM32 (inkl. F407) ist ziemlich vermurkst.
EEPROM braucht man doch eigentlich nicht, man kann auch den internen
Flash beschreiben, man muss sich aber eine Art Wear-Leveling basteln. Da
gibt's aber sogar eine Bibliothek von ST für
Christian J. schrieb:> Versuche mit malloc und calloc waren fruchtlos
Weil damit das Problem erst recht verschlimmert wird.
Ohne malloc wachsen beim AVR-GCC die Variablen von unten nach oben und
der Stack von oben nach unten. Wenn es dann kracht, dann geht es
garantiert nicht oder man war zu verschwenderisch mit Arraygrößen.
Mit malloc hat man dagegen immer die Arschkarte gezogen, denn es muß
dafür extra Speicher reserviert werden. Das ist dann quasi, wie eine
Festplatte partitionieren. Wie man es auch macht, man macht es falsch.
Malloc geht nur dann gut, wenn viel mehr als reichlich RAM verfügbar
ist.
Auf nem MC darf man RAM nicht mit der Schöpfkelle austeilen. Man sollte
ungefähr abschätzen, wieviel RAM ein Array auch wirklich benötigt.
Was man sich auf MCs unbedingt verkneifen sollte, sind unnütze
Umkopierorgien. Viele Variablen braucht man nach der Verarbeitung eh
nicht mehr, d.h. Umkopieren hat keinerlei Nutzen.
Niklas G. schrieb:> Ja das Hardware-I²C der alten STM32 (inkl. F407) ist ziemlich vermurkst.> EEPROM braucht man doch eigentlich nicht, man kann auch den internen> Flash beschreiben, man muss sich aber eine Art Wear-Leveling basteln. Da> gibt's aber sogar eine Bibliothek von ST für
Sicher, mag sein aber damit wollte ich mich nicht befassen. Habe das
BKPRAM benutzt und ne Kopfzelle drauf gelötet auf das Board. Hängt seit
2 Jahren an der Wand und läuft und läuft, nur die Sommerzeit Umstellung
habe ich nicht hingekriegt, rechne mit Unixtime und UTC, da das
einfacher ist.
Ich habe hier mal die für den 103 angehängt, die läuft aber, hat ewig
gedauert. Nur so alle 3-4 Wochen hängt es sich mal auf. Strom weg und
neu starten und alles wieder gut.
Christian J. schrieb:> for (uint16_t k = 0; k < strlen_P(source);k++) {> *(target + k) = pgm_read_byte_near(source + k);> *(target + k + 1) = '\0'; /* NUL Terminieren */> }
Terminieren muss man nur einmal, nicht nach jedem Zeichen. Zum zweiten
kann die Offset-Berechnung mit k mehr kosten als ein simples
Incrementieren des Pointers. Außerdem wird hier für jeden(!)
Schleifendurchlauf die String-Länge von source ermittel. Das kostet
unnötige CPU-Zeit - abgesehen von mehr Code durch die Offset-Berechnung.
Dieses sollte schneller gehen und auch weniger Code kosten:
1
unsignedcharch;
2
3
while((ch=pgm_read_byte_near(source))!='\0')
4
{
5
*target++=ch;
6
source++;
7
}
8
9
*target='\0';
Warum Du dafür aber eine eigene Funktion schreibst, ist mir
schleierhaft. Letztendlich macht diese Funktion nichts anderes als
strcpy_P().
Peter D. schrieb:> Mit malloc hat man dagegen immer die Arschkarte gezogen, denn es muß> dafür extra Speicher reserviert werden.
Den kenne ich, wenn ich das Linker File sehe aber das kenne ich eben
nicht. Ich arbeite oft damit auf dem ARM, tut es auch alles. Und const
... befördert Strings eben auch ins Flash. Was ich nicht weiss ob der
Initilizer der startup sie wieder ins Ram holt.
Frank M. schrieb:> Warum Du dafür aber eine eigene Funktion schreibst, ist mir> schleierhaft. Letztendlich macht diese Funktion nichts anderes als> strcpy_P().
Ist aus dem Arduino Forum geklaut...... wenn es im Internet steht muss
es ja richtig sein :-)
Christian J. schrieb:> Ist aus dem Arduino Forum geklaut......
Die Code-Qualität bestätigt das übliche Vorurteil, das den
Arduino-Programmiern anhaftet. Und dann wird dieser Mist auch noch
ungesehen kopiert.
Wenn schon klauen, dann auch verstehen. Da hat man wesentlich mehr von
;-)
Christian J. schrieb:> Den kenne ich, wenn ich das Linker File sehe aber das kenne ich eben> nicht.
Du siehst nur, wie groß der Heap insgesamt ist. Nicht, ob unter den
unzähligen möglichen Programmabläufen jemals der Heap überlaufen kann.
Das Linker-Script ist irgendwo in der Arduino-Installation zu finden...
Das sieht gut aus: arduino-1.8.8/hardware/tools/avr/avr/lib/ldscripts
Christian J. schrieb:> Ich arbeite oft damit auf dem ARM, tut es auch alles.
Bis es dann halt irgendwann nicht mehr tut...
Christian J. schrieb:> . Was ich nicht weiss ob der> Initilizer der startup sie wieder ins Ram holt.
Nein. Zeiger auf String-Literale zeigen auf ARM direkt ins Flash.
Niklas G. schrieb:> ein. Zeiger auf String-Literale zeigen auf ARM direkt ins Flash.
Führt zwar ein wenig weg aber darum schreibe ich die Dinge gern selbst,
damit ich auch weis was passiert. Bei diesem Arduino weiss ich das eben
nicht! Beim DIY Z80 mit dem sdcc habe ich das gemacht, wo der Heap hin
gehört, wo der Stack und wie sie sich nicht in die Quere kommen. Die
ganze Startup selbst geschrieben, ein gewisser Leo das aufwendige Linker
Script. Und da tut es das auch.
Aber ansonsten ist Arduino ein tolles System, gar keine Frage.
Christian J. schrieb:> damit ich auch weis was passiert
Der Sinn von Arduino ist, dass man sich damit eben nicht
beschäftigen muss. Es steht dir frei, das Teil direkt mit AVR-GCC
und/oder Atmel Studio zu programmieren...
Frank M. schrieb:> Die Code-Qualität bestätigt das übliche Vorurteil, das den> Arduino-Programmiern anhaftet. Und dann wird dieser Mist auch noch> ungesehen kopiert.
Frank.... ich (50, Jg. 1968) gehöre in meinem Team zu den besten
Programmierern. Nicht für CPUs (!), aber für Prüfautomaten, solche
großen Schränke für den Endtest bei automatischen fertigungslinien. C#
benutzen wir dafür mit Visual Studio und das HP Bus API für Oszis,
Relais Matrizzen, Voltmeter usw. Die Stationen laden sich übers
Firmennetz die Software, je nachdem was grad geprüft wird. COBOTS,
fahren bald auf kleinen Wagen von allein an die Arbeitsplätze, weichen
den Leuten aus und werkeln dann, rüsten ihre Werkzeuge selbst. Industrie
4.0 ist das Thema.
Trotzdem kommen auch die Quereinsteiger zu Ergebnissen, die grad von den
Schulen kommen, mit Arduino angefangen haben und die Summe aller Leute
(34 sind wir) im Team behebt auch die Fehler, so dass das Ganze
funktioniert. Einzel-Spitzenleistungen sind weniger gefragt. Du bist
"spitze", wenn Du andere zum Erfolg anleiten und motivieren kannst.
Christian J. schrieb:> Frank.... ich (50, Jg. 1968) gehöre in meinem Team zu den besten> Programmierern.
Dir ist hoffentlich klar, dass das nichts über dich aussagt.
Vielleicht ist auch euer Team sehr schlecht.
Toll ein anderer machts schrieb:> Dir ist hoffentlich klar, dass das nichts über dich aussagt.> Vielleicht ist auch euer Team sehr schlecht.
Sicher, darum fliegst du auch sicher mit Airbus Flugzeugen in den Urlaub
und die stürzen nicht ab, weil wir die technische Ausstattung
fehlerhaft gebaut haben..... aber vielleicht ist ja eine Tupolew besser.
Christian J. schrieb:> Apropos ARM, mit dem 407 habe ich vor 2 Jahren mal was gemacht... plus 3> weitere Displayanzeigen im Haus... , alles DIY :-) Nur dem EEPROM, da> hat nie hingenauen mit der eingebauten Technik. Jetzt Software Ic2. Hab> da Tage mit verbracht :-(
Mit welcher Lib oder GUI-Generator hast du den das Bund-Display zum
laufen gebracht?
Christian J. schrieb:> Sicher, darum fliegst du auch sicher mit Airbus Flugzeugen in den Urlaub> und die stürzen nicht ab, weil wir die technische Ausstattung> fehlerhaft gebaut haben.
Ich glaube du überschätzt dich und deine Arbeit etwas.
Deine Antwort bestätigt das wieder einmal.
Ich wer sonst schrieb:> Mit welcher Lib oder GUI-Generator hast du den das Bund-Display zum> laufen gebracht?
ILI9341 SPi Version für STM32F103, beim F407 sind die Prescaler anders
zu setzen und der APB ist auch ein anderer für die SPI.
Moin,
die obigen Sachen haben insgesa,t fast 400 Bytes an Eimsparungen
gebract, also fast 1/3 weniger Ram als vorher!
Mit Strings tue ich mich noch etwas schwer, auch mangels Beispielen.
Meine Lösung muss ja nicht die sein, die "optimal" ist.
Vorr: Der User - also ich erstmal - macht keine Eingabefehler! Die alle
abzufangen dürfte erstmal jedes Maß sprengen. Kurz muss es sein.
Zeitfenster: 8.45 bis 17.30 Uhr
Eingabe: window<spc>08,45,17,30 oder
window<spc>08.45,17.30
(<spc> = Leerzeichen)
ginge auch, da die Trenner nicht geprüft werden, Führende Null ist auch
Pflicht, sonst murks....
Gibt es auch elegantere Lösungen? Das unten sind immerhin rund 240 Bytes
Code ein paar Zahlen aus einem String zu ernten...
Du könntest eine Funktion bauen, die eine bis mehrere Zahlen "schluckt"
(bis ein Trennzeichen auftritt, das keine Zahl ist), und sowohl den
numerischen Wert als auch die Anzahl gelesener Zeichen zurückgibt.
Die Funktion besteht aus einer Schleife, die bei Auftreten einer Ziffer
den vorherigen Wert mit zehn multipliziert und die Ziffer addiert, und
abbricht, sobald das aktuelle Zeichen keine Ziffer mehr ist.
Damit bist Du die führenden Nullen los (und würdest aber auch den Fall
versehentlicher mehrfacher führender Nullen "überleben").
Diese Funktion rufst Du viermal nacheinander auf, begonnen mit Deinem
Zeichen nach dem ersten Leerzeichen (Hinweis: strchr dürfte hier
effektiver sein als strstr).
Christian J. schrieb:> Gibt es auch elegantere Lösungen? Das unten sind immerhin rund 240 Bytes> Code ein paar Zahlen aus einem String zu ernten...
Evtl. eine andere Menüführung? Freie Eingaben weglassen und nur mit
Cursor und +/- arbeiten. Dann entfällt das parsen, Fehleingaben und
Vertipper sind nicht möglich und die Werte die der Anwender sieht sind
nur nur Kopien vorhandener Variablen.
Christian J. schrieb:> ich schreibe auch if (a==true)> statt nur> das a, damit es leslicher ist.
Das sind dann aber unterschiedliche Aussagen.
(a!=false) wäre der Ersatz
Dirk B. schrieb:>> ich schreibe auch if (a==true)>> statt nur>> das a, damit es leslicher ist.>> Das sind dann aber unterschiedliche Aussagen.>> (a!=false) wäre der Ersatz
Was wieder beweist, wie sch... "C" ist. Sorry, muss sein, auch zu x-mas
Dirk B. schrieb:> Christian J. schrieb:>> ich schreibe auch if (a==true) statt nur>> das a, damit es leslicher ist.>> Das sind dann aber unterschiedliche Aussagen.>> (a!=false) wäre der Ersatz
Einmal das.
Zum anderen haben schon die Alten Griechen erkannt, wie blöd == true
ist. Man kann das nämlich endlos weiter treiben zur Sicherheit und
Klarheit,
If ((a==true)==true) {
Hallo,
das hier hat nix mit dem Thema zu tun, aber ich finde
den Link oben von A.K. (tote Pferd) super klasse!!!
und so real ;-)
Guten Rutsch Euch allen.
Rufus Τ. F. schrieb:> Diese Funktion rufst Du viermal nacheinander auf, begonnen mit Deinem> Zeichen nach dem ersten Leerzeichen (Hinweis: strchr dürfte hier> effektiver sein als strstr).
Naja, mit strtol klappt es auch, da fallen die führenden Nullen auch weg
und wenn nichts bei raus kommt ist es 0. Die Tests sind
vielversprechend, jetzt mal in Ruhe optimieren aus dem Test Bett heraus
ganz am Anfang des Programms.
Und bei einer sms kann man natürlich keine Führung einbauen.... der
ganze Spass wird eh langsam teuer, jeder versuch kostet 9 cent......
Bei der Portierung auf den Cortex wird es eh lustig mit den ganzen
Arduino speziellen Befehlen.....
Nochmal zum Thema "malloc":
Ich verstehe nicht wieso man sich damit Probleme rein holen soll. Ich
habe einiges ausprobiert und erstmal festgestellt, dass der 328P AVR es
erlaubt
200 "belegt" Einträge zu machen
und 860 Bytes Heap zur Verfügung stellt, von 2k.
Eine Fragmentierung kann es nicht geben, wenn man nur 1 Zeiger verwendet
und diesen auch sofort wieder frei gibt. Auch niccht bei 2,3 und mehr,
solange diese konsequent frei gegeben werden, bevor die Routine
verlassen wird.
Der Heap liegt ja unter dem Stack, d.h. der Stack kann rein wachsen,
wenn man ihn zu gross werden lässt.
Es funktioniert jedenfalls.... mit kleinen Größen, also derzeit maxiaml
80 Bytes.
Ich finde diese dynamische Sache nämlich eigentlich prima, früher oft
benutzt bei binären Bäumen in Pascal und C.
Gruss,
Christian
Christian J. schrieb:> Nochmal zum Thema "malloc":>> Ich verstehe nicht wieso man sich damit Probleme rein holen soll. Ich> habe einiges ausprobiert und erstmal festgestellt, dass der 328P AVR es> erlaubt>> 200 "belegt" Einträge zu machen> und 860 Bytes Heap zur Verfügung stellt, von 2k.>> Eine Fragmentierung kann es nicht geben, wenn man nur 1 Zeiger verwendet> und diesen auch sofort wieder frei gibt. Auch niccht bei 2,3 und mehr,> solange diese konsequent frei gegeben werden, bevor die Routine> verlassen wird.
Aber wozu dann malloc? Das braucht zusätzliche Rechenzeit, es produziert
Overhead für seine interne Verwaltung, und man braucht letztendlich auch
den zusätzlichen Pointer für den Zugriff. Weniger Speicher als wenn die
gleichen Daten auf dem Stack liegen würden, braucht es nicht.
> Es stellt sich nur noch die Frage, wo der Heap sich denn so rum lümmelt?> Vermutlich unter dem Stack, d.h. der Stack kann rein wachsen, wenn man> ihn zu gross werden lässt.
Ja.
Rolf M. schrieb:> Aber wozu dann malloc?
Weil es das gibt? ... ähm.... nee, natürlich nicht. Ich vermute eher das
das nur Sinn macht wenn man echt komplexe Datenbäume hat und viel
Speicher. Aber auf einem AVR mit 2kB..... naja.. der baum würde sehr
schnell über den kleinen Blumentopf hinaus wachsen wollen.
Christian J. schrieb:> Ich verstehe nicht wieso man sich damit Probleme rein holen soll.
Nun, man muß zur Compilezeit festlegen, wieviel RAM darf Malloc und
wieviel der Stack belegen. D.h. es kracht, sobald einer von beiden
ausgeht, obwohl noch RAM übrig sein kann.
Christian J. schrieb:> Auch niccht bei 2,3 und mehr,> solange diese konsequent frei gegeben werden, bevor die Routine> verlassen wird.
Dann nimmt man aber kein Malloc, sondern einfach lokale Variablen.
Malloc braucht man nur, wenn verschiedene Tasks die Variablen anlegen
und freigeben sollen. Z.B. der Ethernetinterrupt speichert ein
Datenpaket und die Mainloop parst es und gibt es wieder frei.
Hier ein Vorschlag, bei dem alles über den Command-String gesteuert
wird. Sehr flexibel ist das nicht, aber müsste recht klein sein. In
Assembler sollten es um die 60 Byte sein, bin gespannt was der Compiler
draus macht.
Naja, das sieht schon elegant aus. Meine Lösung ist eher so wie man sie
sich zuerst überlegt, ohne mit Bits zu basteln. Aber ich nehme den
gesamten Header mit, den so eine sms hat. Hat den Vorteil, dass die
führende Null egal ist. strtol verbraucht aber 1,2kb extra. p = ptr + 1;
schiebt den Zeiger über das Komma weg.
Was ist das?
while(in_ch * cmd_ch);
Christian J. schrieb:> while(in_ch * cmd_ch);
Das ergibt genau dann 0 (also false), wenn mindestens eine der Variablen
0 ist.
Im Endeffekt kodiert man im Command-String mit Platzhaltern, wo die
Zahlen hingeschrieben werden sollen. Mit 0x8n wird die Zahl in das n-te
Byte von buf addiert, mit 0xCn wird sie zusätzlich mit 10 multipliziert.
buf muss daher auch zwingend vorher 0 sein.
avr schrieb:> Christian J. schrieb:>> while(in_ch * cmd_ch);>> Das ergibt genau dann 0 (also false), wenn mindestens eine der Variablen> 0 ist.
Dann würde ich aber schreiben:
1
while(in_ch&&cmd_ch);
oder von mir aus auch ausführlich:
1
while(in_ch!=0&&cmd_ch!=0);
Aber nicht so verklausuliert über eine Multiplikation.
Rolf M. schrieb:> Aber nicht so verklausuliert über eine Multiplikation.
Die Frage ist, ob der Compiler daraus Multiplikation erzeugt und damit
die besseren Maschinencode für den avr. Ich vermute nicht. Da der Code
klein werden sollte, habe ich die Optimierung direkt eingesetzt.