Hallo Leute,
derzeit habe ich folgendes Problem an meinem Projekt:
Hier ein kurzer Überblick:
Ich verwende den Arduino MINI ATMega328 und will einer Funktion im
Hauptprogramm einen String übergeben damit dann im Unterprogramm etwas
passiert.
Genau genommen soll dieser String dann über eine SoftwareSerial
Schnittstelle ( in meinem Fall heißt sie Scada ) an ein anderes
Computerprogramm (Scada Programm ) geschickt werden.
Da mein System schon nahezu fertig programmmiert ist, und das ganze
Projekt fast 3000 Codezeilen verschlang, bin ich mit dem verfügbaren RAM
derzeit nur mehr auf 358byte (freeMemory() - liefert mir das zurück )
Ich habe den Code so umgesetzt.
1
voidloop()
2
{
3
//do some other work here
4
sendToScada("this text should be send to a scada system");
5
}
6
7
8
9
voidsendToScada(char*sMessageToSend)
10
{
11
Scada.listen();
12
Scada.print("V");// Messageheader
13
for(inti=0;i<strlen(sMessageToSend);i++)
14
Scada.print(sMessageToSend[i]);
15
Scada.print("\n");
16
XBee.listen();//set to other serial active
17
}
Was mache ich da falsch? Mit dieser Funktion scheitre ich, denn nach
einpaar aufrufen startet mein Arduino neu, d.h ich vermute dass der
Speicher rasant gegen 0 geht und dann einen Neustart verursacht.
lg
Dieter
Hallo,
mit deinen String-Operationen wird kein zusätzlicher Speicher
"verbraucht". Es handelt sich ja nur um die drei Konstanten, und die
brauchen nur so viele Bytes wie sie lang sind (zuzüglich jeweils ein
Byte für den Terminator).
Bleiben als mögliche Speicherfresser "nur" noch deine Prozeduren
Scada.listen(), Scada.print() und XBee.listen().
Kannst du davon den Quellcode posten?
Hallo Markus,
so ich habe nun von der SoftwareSerial Lib die Quellcodes durchforstet.
Ich habe nun folgende Funktion gefunden, welche durch Scada.print
aufgerufen wird.
1
size_tSoftwareSerial::write(uint8_tb)
2
{
3
if(_tx_delay==0){
4
setWriteError();
5
return0;
6
}
7
8
uint8_toldSREG=SREG;
9
cli();// turn off interrupts for a clean txmit
10
11
// Write the start bit
12
tx_pin_write(_inverse_logic?HIGH:LOW);
13
tunedDelay(_tx_delay+XMIT_START_ADJUSTMENT);
14
15
// Write each of the 8 bits
16
if(_inverse_logic)
17
{
18
for(bytemask=0x01;mask;mask<<=1)
19
{
20
if(b&mask)// choose bit
21
tx_pin_write(LOW);// send 1
22
else
23
tx_pin_write(HIGH);// send 0
24
25
tunedDelay(_tx_delay);
26
}
27
28
tx_pin_write(LOW);// restore pin to natural state
29
}
30
else
31
{
32
for(bytemask=0x01;mask;mask<<=1)
33
{
34
if(b&mask)// choose bit
35
tx_pin_write(HIGH);// send 1
36
else
37
tx_pin_write(LOW);// send 0
38
39
tunedDelay(_tx_delay);
40
}
41
42
tx_pin_write(HIGH);// restore pin to natural state
Timbus schrieb:> terminierungszeichen im char-array ist nen hit ... sonst nimmt er> einfach den restlichen speicher bis er ein \0 fidnet
Also müsste ich das String Argument wie folgt in meien Funktion
einbetten
1
sendToScada("this text should be send to a scada system\0");
Zeig doch endlich mal deine Scada.print()!
Eigentlich müsste der Compiler dir haufenweise Warnungen um die Ohren
knallen. Mal rufst du Scada.print() mit nem String als Parameter auf,
dann wieder mit nem einzelnen Char...
> sendToScada("this text should be send to a scada system\0");
Wenn Du Speicherprobleme hast, solltest Du solche Spielereien lassen.
Es geht, es hat nämlich zur Folge, dass zwei Null-Zeichen am Ende des
Strings stehen.
strlen () macht beim ersten Feierabend, weshalb kein Fehler auftritt.
Wenn Dein Empfänger ein Windows-System ist, so pass mit dem "\n" auf.
Manche Programme stehen explizit auf Wagenrücklauf + Neue Zeile,
andernfalls gibt's Kuddel-Muddel.
amateur schrieb:> @Uwe>> "V" und "\n" sind echte Strings.
Das ist mir schon klar (abgesehen davon dass es in C weder Strings noch
Arrays gibt, sondern nur syntax sugar für Pointer-Arithmetik, aber das
wird jetzt philosophisch...).
Meine Bemerkung zielt darauf, dass bei
Scada.print(sMessageToSend[i]);
mit einem char und bei
Scada.print("\n");
mit einem string aufgerufen wird.
Ich hab das Programm mal eben durch den GCC geschoben (geht nur, wenn
man sämtliche Warnungen deaktiviert), in zwei Varianten:
a) Scada.print(char c);
Effekt: der "this text..." String wird korrekt ausgegeben, statt dem "V"
und "\n" kommt jeweils ein Zeichen Müll. Liegt daran, dass die Funktion
das untere Byte des Zeigers auf den "V" bzw. "\n"-Sting ausgibt.
b) Scada.print(char * str);
Effekt: das "V" wird korrekt ausgegeben, danach stürzt das Programm ab.
Ist auch einleuchtend: die Funktion interpretiert das übergebene char
als einen Zeiger, folgt ihm ins Nirvana und verursacht eine
Schutzverletzung.
Uwe ... schrieb:> Meine Bemerkung zielt darauf, dass bei> Scada.print(sMessageToSend[i]);> mit einem char und bei> Scada.print("\n");> mit einem string aufgerufen wird.
Ja, wirkt wirklich komisch. Aber vielleicht gibt es zwei
print()-Prozeduren, eine für char und eine für const char* ? Gehts um
C++? Genaueres würde man wahrscheinlich nur in der Doku oder im
Quellcode zu diesem print() finden...
Werf die dynamische Speicherverwaltung via Malloc raus und verwende
statische Ramverwaltung.
Dann kann die der Linker auch eine Ramausnutzung anzeigen die verwendbar
ist.
Aufpassen musst du dann nur noch mit dem Stack.
Meiner Meinung nach gehört diese dynamische Speicherverwaltung für µC
verboten. En µC hat eben nicht unbegrenztes RAM wie ein PC sondern nur
begrenzte Ressourcen.
Da sollte ein Programmierer auf dem µC schon wissen und sehen können
was wirklich läuft.
Die dynamische Speicherverwaltung versteckt Bereichsüberschreitungen die
zu den schönsten Effekten führen die niemand da suchen geht wo sie
wirklich herkommen.
Uwe ... schrieb:> Zeig doch endlich mal deine Scada.print()!
Sorry, im Angang findet ihr die Files zur SoftwareSerial Lib
Leider finde ich dort keine Funktion print() sondern nur write()
Scada habe ich mein SoftwaerSerial Objekt genannt.
Dieter Sch schrieb:> sendToScada("this text should be send to a scada system");
Bedenke, daß auf AVRs (und dazu gehören Arduinos halt auch) auch
Stringkonstanten im RAM abgelegt werden.
Um das zu umgehen, ist das pgmspace.h-Gehampel erforderlich, was der
Harvard-Architektur der AVRs zu verdanken ist.
Rufus Τ. Firefly schrieb:>> Um das zu umgehen, ist das pgmspace.h-Gehampel erforderlich
Ok, und was heißt das nun konkret für mich?
Wie optimiere ich meinen Code?
danke
lg
Ganz einfach: mach um deinen String einfach ein F() rum, dann wird er im
Flash gespeichert. (Funktioniert nur mit Arduino)
Beispiel ganz unten:
http://playground.arduino.cc/Learning/Memory
Nils Z. schrieb:> Ganz einfach: mach um deinen String einfach ein F() rum, dann wird er im> Flash gespeichert. (Funktioniert nur mit Arduino)
und so solls funktionieren?
Sorry ich kanns im Moment nicht testen....
1
sendToScada(F("this text should be send to a scada system"));
>sendToScada(F("this text should be send to a scada system"));
2
>
Hab mir schnell ArduionoIDE runtergeladen und den Sketch wie oben von
mir geschrieben angepasst.
Dabei bekomm ich leider diese Fehlermeldung:
error: cannot convert 'const __FlashStringHelper*' to 'char*' for
argument '1' to 'void sendToScada(char*)'
noch eine 2-te Funktion schreiben, die mit Strings aus dem Flash umgehen
kann.
Den String im Flash zu belassen, ist nur die halbe Miete. Du brauchst
auch Funktionen, die den dadurch sich verändernden Speicherzugriff
berücksichtigen.
Belegt für den String doppelt Speicher, nämlich einmal im
Programmspeicher (Flash) und dann nochmal im RAM. Anders kann es auch
nicht gehen, denn puts erwartet einen Pointer auf ein char-array.
Normale Pointer adressieren immer Daten im RAM. Beim Programmstart
werden alle hardcodierten Zeichenketten ins RAM kopiert noch bevor die
main() Funktion beginnt.
Die Adressierung (Pointer-Werte) beginnen sowohl beim RAM als auch beim
Programmspeicher mit 0. Einem Zeiger sieht man daher nicht an, auf
welchen Speicher er zeigt. Normalerweise geht man vom RAM aus.
Sparen kann man so:
1
puts_P(PSTR("Hallo"));
PSTR sagt dem Compiler, dass dieser String NICHT ins RAM kopiert werden
soll. Da puts jedoch nicht aus dem Programmspeicher lesen kann (da er
über andere Maschinen-Befehle angesprochen wird), gibt es die spezielle
Variante puts_P(). Die avr Library enthält für fast alle String
Funktionen eine _P Variante. Diese Varianten intepretieren Pointer als
Zeiger auf den Programmspeicher. Sie können jedoch das normale RAM nicht
adressieren.
Etwas Tricky wird's bei printf_P. Da steht der Formatier-String (z.B.
"%s") im Programmspeicher, aber die Variable (s) muss im RAM stehen.
Leider gibt es keine Variante von printf, wo die Variablen im
Programmspeicher liegen können.
Wenn man Strings an ein anderes Gerät sendet (z.B. seriell) ist es
hilfreich, zwei Varianten der send() Funktion zu haben, nämlich eine für
Strings im RAM und eine für Strings im Programmspeicher.
Karl Heinz Buchegger schrieb:> noch eine 2-te Funktion schreiben, die mit Strings aus dem Flash umgehen> kann.> Den String im Flash zu belassen, ist nur die halbe Miete. Du brauchst> auch Funktionen, die den dadurch sich verändernden Speicherzugriff> berücksichtigen.
Nach allem was ich gegoogelt habe, müsste die 2.te Funktion ca. so
aussehen
Jungs, bitte bedenkt, dass wir hier auf Arduino und C++ sind.
Da gibt es andere Kapselungen für Flash-Zugriffe.
Wenn schon, dann müssen wir mit dem System arbeiten und uns daran
orientieren, wie die Arduino Basis-Dinge funktionieren. Es bringt
nichts, hier unsere gewohnten Konzepte wie P_STR einzuführen. Wenn der
Arduino Weg darin besteht, eine String-Konstante in eine Klasse namens
'__FlashStringHelper' einzupacken, damit man nicht irrtümlich einen
Flash-Pointer an einen normalen Pointer zuweisen kann, dann ist das eben
so.
Svenska schrieb:> dann kümmert sich der Compiler um den Rest.
Naja, um Teile davon.
Automagisch zwischen puts und puts_P entscheiden tut der Compiler dann
trotzdem nicht.
Svenska schrieb:> Alternativ unterstützen moderne GCC-Versionen eine Spezifizierung> "__flash", dann kümmert sich der Compiler um den Rest.
Arduino = C++ = kein __flash