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
Daher eben den Assembler-Code anschauen und sich das Raten ersparen.
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.
1 | /* Sende eine sms an das Ziel mit Text
|
2 | Mode FLASH = Text ist im Flash zu finden
|
3 | RAM = Text wird im RAM übergeben
|
4 |
|
5 | AT+CMGS="+85291234567"<CR>
|
6 | <CR><LF>><Space>
|
7 |
|
8 | */
|
9 | int SIM800_SendSMS(const char* text,int mode) |
10 | {
|
11 | char MsgToSend[160]; |
12 | |
13 | /* MsgToSend = MsgStatusLine + MsgContent */
|
14 | |
15 | /* Zeitstempel als Erstes vorbauen */
|
16 | if (flags.TimeStampIsValid ) { |
17 | char signal[6]; |
18 | switch (CSQuality) { |
19 | case 1: strcpy(signal,"-"); break; |
20 | case 2: strcpy(signal,"+"); break; |
21 | case 3: strcpy(signal,"++"); break; |
22 | case 4: strcpy(signal,"+++"); break; |
23 | default: strcpy(signal,"NA"); break; |
24 | }
|
25 | |
26 | /* Low Power oder Full Mode */
|
27 | char lowpower[6]; |
28 | if (flags.LowPowerMode) strcpy(lowpower,"Eco"); |
29 | else strcpy(lowpower,"Full"); |
30 | |
31 | // /* LEDs ein oder aus */
|
32 | char leds[8]; |
33 | if (flags.ShowLED) strcpy(leds,"LED"); |
34 | else strcpy(leds,"NoLED"); |
35 | |
36 | sprintf(MsgToSend,"%02u.%02u, %02u:%02u, %s, %s, %02u%%, %s\r\n", |
37 | timestamp.day, timestamp.month, timestamp.hour, |
38 | timestamp.min, lowpower, leds, BattProzent, signal); |
39 | } else |
40 | sprintf(MsgToSend,"--.--.--, --:--, %02u%%, ---\r\n",BattProzent); |
41 | |
42 | /* MsgContent existiert nur zwischen diesen {} */
|
43 | {
|
44 | /* SMS Text aus Progmem oder RAM lesen und verketten */
|
45 | char MsgContent[MAX_MSG_LENGTH+2]; |
46 | if (mode == FLASH) |
47 | ReadFromFlash(MsgContent,text); |
48 | else { |
49 | if (strlen(text) > sizeof(MsgToSend)) { |
50 | debugln(F("Text zu lang!")); |
51 | return 0; |
52 | }
|
53 | strcpy(MsgContent,text); |
54 | }
|
55 | |
56 | /* Hauptstring zusammen fügen */
|
57 | strcat(MsgToSend,MsgContent); |
58 | }
|
59 | |
60 | /* In MsgToSend steht jetzt, was gesendet werden soll, entweder ein
|
61 | String aus dem RAM oder ein indirekter Inhalt aus dem Flash */
|
62 | |
63 | debugln(F("----------------- SMS ---------------------")); |
64 | debugln(MsgToSend); |
65 | debugln(F("-------------------------------------------")); |
66 | |
67 | /* Pending Alarme löschen, da False Alarms möglich */
|
68 | cli(); |
69 | flags.RadarAlarmHold = false; |
70 | /* GSM bleibt an, alle Timer zurücksetzen */
|
71 | ResetAllWDTTimers(); |
72 | sei(); |
73 | |
74 | #ifdef SEND_SMS
|
75 | /* Empfangsbereitschaft des GSM prüfen */
|
76 | if (!SIM800_SendAT("AT\r\n","OK")) |
77 | return 0; |
78 | |
79 | /* Text Mode sms */
|
80 | SIM800_SendAT("AT+CMGF=1\r\n","OK"); |
81 | |
82 | /*AT Command und Zielnummer senden */
|
83 | char atcmd[40]; |
84 | strcpy(atcmd,"AT+CMGS=\""); |
85 | strcat(atcmd,admin_number); |
86 | strcat(atcmd,"\"\r\n"); |
87 | serialSIM800.print(atcmd); |
88 | serialSIM800.flush(); |
89 | |
90 | /* Warte bis "\r\n> " empfangen wird */
|
91 | SIM800_GetChars(atcmd); |
92 | if (!(strstr(atcmd,"\r\n>"))) |
93 | return 0; |
94 | |
95 | /* Cntrl-Z als Schlusszeichen */
|
96 | serialSIM800.println(char(26)); |
97 | |
98 | /*Bis zu 60s kann es dauern, bis mit CMGS:<zeichenzahl> geantwortet wird */
|
99 | while (1) { |
100 | SIM800_GetChars(atcmd); |
101 | if (strstr(atcmd,"GMGS"))) |
102 | return 1; |
103 | }
|
104 | #endif
|
105 | |
106 | return 1; |
107 | }
|
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
Naja, global weiss man wenigstens wo man liegt. Es wird knapp :-)
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 | const char * signal; |
2 | |
3 | switch (CSQuality) { |
4 | case 1: signal = "-"; break; |
5 | case 2: signal = "+"; break; |
6 | case 3: signal = "++"; break; |
7 | case 4: signal = "+++"; break; |
8 | default: signal = "NA"; break; |
9 | }
|
> Du kannst statt sprintf itoa und strcpy nutzen.
Jepp. Das hier:
1 | sprintf(MsgToSend,"%02u.%02u, %02u:%02u, %s, %s, %02u%%, %s\r\n", |
2 | timestamp.day, timestamp.month, timestamp.hour, |
3 | timestamp.min, lowpower, leds, BattProzent, signal); |
könnte man so umschreiben. Aber auch das hier:
1 | {
|
2 | /* SMS Text aus Progmem oder RAM lesen und verketten */
|
3 | char MsgContent[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 | return 0; |
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 | return 0; |
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:
1 | if (strlen(text) + strlen(MsgToSend) >= sizeof(MsgToSend)) { |
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.
:
Bearbeitet durch Moderator
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.
:
Bearbeitet durch User
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 | const char flash_sysdown[] PROGMEM = "Shutdown!"; |
2 | const char flash_alarm_off[] PROGMEM = "Alarm ist jetzt AUS!"; |
3 | const char flash_alarm_on[] PROGMEM = "Alarm ist jetzt scharf!"; |
4 | const char flash_lowpower_on[] PROGMEM = "Lowpower Mode aktiv!"; |
5 | const char flash_lowpower_off[] PROGMEM = "Fullpower Mode aktiv!"; |
6 | |
7 | ....
|
8 | |
9 | /* Kopiert einen Text aus dem Flash ins Ram */
|
10 | void ReadFromFlash(char* target, const char* source) |
11 | {
|
12 | for (uint16_t k = 0; k < strlen_P(source);k++) { |
13 | *(target + k) = pgm_read_byte_near(source + k); |
14 | *(target + k + 1) = '\0'; /* NUL Terminieren */ |
15 | }
|
16 | }
|
:
Bearbeitet durch User
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.
Frank M. schrieb: > if (strlen(text) + strlen(MsgToSend) >= sizeof(MsgToSend)) { Danke für den Hinweis auf den Fehler!
Probier mal statt:
1 | strcpy(leds,"NoLED"); |
dass hier:
1 | strcpy_P(leds,PSTR("NoLED")); |
PS: Oder was Rufus schreibt.
:
Bearbeitet durch User
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.... !
:
Bearbeitet durch User
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
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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 | char MsgContent[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 | char MsgToSend[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.
Auch das hier:
1 | char atcmd[40]; |
2 | strcpy(atcmd,"AT+CMGS=\""); |
3 | strcat(atcmd,admin_number); |
4 | strcat(atcmd,"\"\r\n"); |
5 | serialSIM800.print(atcmd); |
6 | serialSIM800.flush(); |
kann man so schreiben:
1 | serialSIM800.print("AT+CMGS=\""); |
2 | serialSIM800.print(admin_number); |
3 | serialSIM800.print("\"\r\n"); |
4 | serialSIM800.flush(); |
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.
:
Bearbeitet durch Moderator
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.....
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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...
:
Bearbeitet durch User
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....
1 | int SIM800_SendFlashAT(const char* atc,const char* token) |
2 | {
|
3 | char res[128]; |
4 | char *kopie; |
5 | |
6 | /* Den Flash String ins Ram holen */
|
7 | kopie = (char*)malloc(strlen_P(atc)+1 * sizeof(char); |
8 | strcpy_P(kopie,atc); |
9 | |
10 | /* SIM800 Serial Buffer leeren */
|
11 | while (serialSIM800.available()) |
12 | serialSIM800.read(); |
13 | |
14 | /* Sende AT Command */
|
15 | serialSIM800.print(kopie); |
16 | serialSIM800.flush(); |
17 | free(kopie); |
18 | |
19 | SIM800_GetChars(res); |
20 | |
21 | //debug(atc);
|
22 | // debug(res);
|
23 | |
24 | if (strstr(res, token)) /* OK pruefen */ |
25 | return 1; |
26 | |
27 | return 0; |
28 | }
|
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"));
:
Bearbeitet durch User
Hm, versuch's mal so:
1 | serialSIM800.print(reinterpret_cast<const __FlashStringHelper *> (atc)); |
Kenne mich mit Arduino auch nicht so gut aus. Das sind so die Gründe warum ich ARM bevorzuge :-)
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 :-(
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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 | unsigned char ch; |
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().
:
Bearbeitet durch Moderator
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 :-)
:
Bearbeitet durch User
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 ;-)
:
Bearbeitet durch Moderator
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.
:
Bearbeitet durch User
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.
1 | ;/////////////////////////////////////////////////////////// |
2 | ; Anordung der Segmente fuer den Linker, ab hier nur relative |
3 | ; Adressen |
4 | |
5 | .area _CODE |
6 | .area _INITIALIZER |
7 | .area _HOME |
8 | .area _GSINIT |
9 | .area _GSFINAL |
10 | .area _DATA |
11 | .area _INITIALIZED |
12 | .area _BSEG |
13 | .area _BSS |
14 | .area _HEAP |
15 | |
16 | ; --- Defintion der Heap Groesse |
17 | __sdcc_heap_start:: |
18 | .ds heap_size |
19 | |
20 | .area _HEAP_END |
21 | __sdcc_heap_end:: |
22 | .ds 1 |
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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
Beitrag #5668371 wurde vom Autor gelöscht.
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.
:
Bearbeitet durch User
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...
1 | /* Start, Ende Uhrzeit */
|
2 | typedef struct { |
3 | byte hour; |
4 | byte min; |
5 | } zeit_t; |
6 | |
7 | int ParseWindow(char* buf, zeit_t* Start, zeit_t* End) |
8 | { /* window_08,45,17,30 */ |
9 | |
10 | char* p = buf; |
11 | |
12 | /* Check cmd */
|
13 | if (!(strstr_P(buf,PSTR("window")))) |
14 | return 0; |
15 | |
16 | /* Suche erstes Leerzeichen */
|
17 | p = strstr(buf," "); |
18 | p++; |
19 | Start->hour = ((p[0]-'0') * 10) + (p[1]-'0'); |
20 | Start->min = ((p[3]-'0') * 10) + (p[4]-'0'); |
21 | End->hour = ((p[6]-'0') * 10) + (p[7]-'0'); |
22 | End->min = ((p[9]-'0') * 10) + (p[10]-'0'); |
23 | |
24 | return 1; |
25 | }
|
:
Bearbeitet durch User
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
:
Bearbeitet durch User
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.....
1 | const char* cmd = "window"; |
2 | char* p; |
3 | |
4 | /* Schlüsselwort suchen */
|
5 | debugln(F("Suche window")); |
6 | if (!(p = strstr(satz,cmd))) |
7 | return; |
8 | p = p + strlen(cmd); |
9 | |
10 | char *ptr; |
11 | unsigned long zahl; |
12 | zahl = strtol(p,&ptr,10); |
13 | debugln(zahl); |
14 | p = ptr + 1; |
:
Bearbeitet durch User
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
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
1 | // Aufruf
|
2 | Parse("window 18.45,19.53","window \xC0\x80.\xC1\x81,\xC2\x82.\xC3\x83", buf); |
3 | |
4 | int Parse(char* input, const char* command, uint8_t* buf) |
5 | {
|
6 | uint8_t cmd_ch, in_ch; |
7 | do
|
8 | {
|
9 | in_ch = *input++; |
10 | cmd_ch = *command++; |
11 | |
12 | if(cmd_ch & 0x80) |
13 | {
|
14 | in_ch -= '0'; |
15 | if(cmd_ch & 0x40) |
16 | in_ch *= 10; |
17 | buf[cmd_ch & 0x3F] += in_ch; |
18 | }
|
19 | else if(in_ch != cmd_ch) |
20 | return 0; |
21 | }
|
22 | while(in_ch * cmd_ch); |
23 | return 1; |
24 | }
|
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);
1 | int ParseWindow(char* buf, zeit_t* Start, zeit_t* End) |
2 | {
|
3 | /* window_08,45,17,30 */
|
4 | |
5 | /* Schlüsselwort suchen */
|
6 | char* p; |
7 | if (!(p = strstr_P(buf,PSTR("window")))) |
8 | return 0; |
9 | p = p + strlen_P(PSTR("window")); |
10 | |
11 | /* Zahlen extrahieren */
|
12 | char *ptr; |
13 | byte res[4]; |
14 | for (int i = 0; i < 4; i++) { |
15 | res[i] = strtol(p,&ptr,10); |
16 | p = ptr + 1; |
17 | }
|
18 | |
19 | /* Bereichsprüfungen */
|
20 | if ((res[0] > 23) || (res[2] > 23)) |
21 | return 0; |
22 | if ((res[1] > 59) || (res[3] > 59)) |
23 | return 0; |
24 | |
25 | /* Zuweisung */
|
26 | Start->hour = res[0]; |
27 | Start->min = res[1]; |
28 | End->hour = res[2]; |
29 | End->min = res[3]; |
30 | |
31 | return 1; |
32 | }
|
:
Bearbeitet durch User
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.