Forum: Mikrocontroller und Digitale Elektronik AtMega128: Längenbegrenzung bei "strcat"?


von Martin M. (martin69)


Lesenswert?

Hallo,

ich bastle mir im AVR einen großen String zusammen (HTML-Datei mit 
dynamischem Inhalt). Hierzu benutze ich "strcpy" und "strcat".

Der lange String ist wie folgt definiert:
unsigned char ucsHTML_string[10000];

Das Prgramm spinnt, wenn der Zielstring ca. 3000 Zeichen übersteigt (das 
Programm macht nicht mehr, was es soll). Da der String ja 10.000 Byte 
groß ist, kann es kein Überlauf sein.

Gibt es bei "strcat" eine Längenbegrenzung? Falls nicht, dann muß ich 
weiter suchen, an was es liegt....

Hier meine Tools / Hardware:
* avr-gcc (GCC) 3.4.6
* ATMega128
* externes RAM, Typ IDT71024 (128k*8), A16 ist mit Prozessorport immer 
auf gleichem Pegel

Gruß
Martin

von holger (Gast)


Lesenswert?

>Der lange String ist wie folgt definiert:
>unsigned char ucsHTML_string[10000];

ATMega128 hat 4kB Speicher. Was ist an deinem
Array also falsch?

von holger (Gast)


Lesenswert?

Nachtrag:

>>Der lange String ist wie folgt definiert:
>>unsigned char ucsHTML_string[10000];

>ATMega128 hat 4kB Speicher. Was ist an deinem
>Array also falsch?

Er hat 4kB RAM. Und da wird dein String per strcat
reinkopiert.

von Martin M. (martin69)


Lesenswert?

an dem ATMega 128 ist ein externes RAM dran (128k*8). Daher hab ich 
nicht nur 4k RAM.... Oder geht das mit dem externen RAM nicht mit 
Arrays?

von holger (Gast)


Lesenswert?

>* externes RAM, Typ IDT71024 (128k*8), A16 ist mit Prozessorport immer
>auf gleichem Pegel

Upps, hatte ich übersehen :(
Vieleicht kopiert strcat ja nicht in das externe RAM.

Schnell weg!

von Karl H. (kbuchegg)


Lesenswert?

Martin M. wrote:
> an dem ATMega 128 ist ein externes RAM dran (128k*8). Daher hab ich
> nicht nur 4k RAM.... Oder geht das mit dem externen RAM nicht mit
> Arrays?


Weiß dein Compiler vom externen Array?

von Simon K. (simon) Benutzerseite


Lesenswert?

holger wrote:
>>* externes RAM, Typ IDT71024 (128k*8), A16 ist mit Prozessorport immer
>>auf gleichem Pegel
>
> Upps, hatte ich übersehen :(
> Vieleicht kopiert strcat ja nicht in das externe RAM.
>
> Schnell weg!

Um den externen Speicher als normalen Speicher anzusprechen benötigst du 
in C ein angepasstes Startup Script. Zumindest muss der RAM Controller 
initialisiert werden, bevor das programm startet. Außerdem muss dies dem 
Compiler mitgeteilt werden.

Ich glaube aber auch nicht, dass strcat das Mittel der Wahl ist. 
Immerhin muss der bei jedem Aufruf erneut das Ende des Strings finden.

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ext_ram

von Martin M. (martin69)


Angehängte Dateien:

Lesenswert?

ziemlich am Anfang des Main-Programms rufe ich folgendes Unterprogramm 
auf, mit dem das externe SRAM freigegeben wird:

void enable_external_SRAM(void)
  {
  DDRG=0xFF;    // set io-pins on PortG
  PORTG=0xFF;   // RAM /WR,/RD,ALE,CE und Bank Select

  MCUCR|=1<<SRE; // Enable external RAM
  return;
  }

Das Makefile habe ich im Dateianhang.

So richtig klar ist mir die Sache allerdings nicht. Bin auch kein 
Software-Experte und kenne mich daher nicht sonderlich gut in der 
Matherie aus. Z.B. verwende ich kein malloc(), der RAM-Test mit einem 
eigenen Unterprogramm funktioniert problemlos. Den RAM-Test schicke ich 
in der nächsten Message als Anhang...

von Martin M. (martin69)


Angehängte Dateien:

Lesenswert?

anbei der SRAM-Test

von Martin M. (martin69)


Lesenswert?

@Simon:

> Um den externen Speicher als normalen Speicher
> anzusprechen benötigst du in C ein angepasstes Startup Script.

....Wie geht denn das? Kannst Du mir da Tipps geben?


> Ich glaube aber auch nicht, dass strcat das Mittel der
> Wahl ist. Immerhin muss der bei jedem Aufruf erneut das
> Ende des Strings finden.

.... wenn es etwas länger dauert ist das kein Problem bei meiner 
Anwendung. Ich muß vor dem Senden des HTML-Strings die Länge wissen und 
es ist am einfachsten, wenn ich einen langen String erzeuge (die gesamte 
HTML-Daten) und dann mit strlen() die Länge abfrage. Ohne externes RAM 
ist das Berechnen der Länge ein ziemlich großer Aufwand, da der 
HTML-Code je nach Status unterschiedlich lag ist.

von Karl H. (kbuchegg)


Lesenswert?

Martin M. wrote:
> ziemlich am Anfang des Main-Programms rufe ich folgendes Unterprogramm
> auf, mit dem das externe SRAM freigegeben wird:
>
> void enable_external_SRAM(void)
>   {
>   DDRG=0xFF;    // set io-pins on PortG
>   PORTG=0xFF;   // RAM /WR,/RD,ALE,CE und Bank Select
>
>   MCUCR|=1<<SRE; // Enable external RAM
>   return;
>   }
>

Das ist schön.
Nur leider hilft das in deinem Fall nicht.

Dein Compiler weiß nichts davon, dass es da noch irgendwo ein externes 
SRAM gibt. Also hat er die Variablen auch nicht dorthin verfrachtet, 
sondern die liegen alle im internen SRAM.

Aus Sicht der Compilers, hat dein Prozessor nur 4KB zur Verfügung.


Lass es mich mal so sagen: Du kannst an dein Auto einen ganzen 
Tanklastzug mit Benzin hinten dran hängen. Solange du keine Leitung von 
diesem Tank zum Motor legst, wird dir trotzdem nach 600km der Sprit 
ausgehen.

Dir fehlt diese Leitung. Dein Compiler weiß nichts vom externen SRAM und 
benutzt es daher auch nicht.

von Karl H. (kbuchegg)


Lesenswert?

Martin M. wrote:
> @Simon:
>
>> Um den externen Speicher als normalen Speicher
>> anzusprechen benötigst du in C ein angepasstes Startup Script.
>
> ....Wie geht denn das? Kannst Du mir da Tipps geben?

Steht alles im Link, den Simon weiter oben gepostet hat.

Aber ich geh mit Simon konform. Wenn du den Speicher nur brauchst, um 
den String zusammenzubauen, dann ist es wahrscheinlich Overkill sich da 
durchzuarbeiten.

Schreib dir ein eigenes strcat/strlen, welches mit deinem externen 
Speicher umgehen kann und fertig. Schreib dir als erstes eine Funktion, 
welche ein beliebiges Byte aus dem externen SRAM lesen/schreiben kann. 
In einem gewissen Sinne würde ich dieses externe SRAM wie eine einzige 
Variable auffassen, für die es spezielle Funktionen zur Manipulation 
gibt. Und genau diese Funktionen schreibst du dir jetzt erst mal.

von Martin M. (martin69)


Lesenswert?

das mit der eigenen Funktion könnte ich so machen, daran hatte ich am 
Anfang auch schon mal gedacht. Aber ich war der Meinung, daß es für mich 
einfacher ist, diese den C-Compiler machen zu lassen....

von Karl H. (kbuchegg)


Lesenswert?

Der Memory Tester hätte dich schon stutzig machen müssen.

Aber so schlimm ist das ganze dann auch wieder nicht.

Das einzige Problem ist die RAM-Bank Umschaltung.
Wenn du mit nur einer RAM-Bank auskommst, könntest du sogar die 
originalen str... Funktionen benutzen. Du benutzt dann einfach alles 
über XRAMSTART als eine spezielle String Variable die genau an dieser 
Adresse liegt und fertig. Genauso wie es der Memory Tester auch macht.
Brauchst du allerdings beide RAM-Bänke wirds ein klein wenig 
aufwändiger. Allerdings kannst du vieles davon dann durch eine spezielle 
Append Funktion, die sich nicht mehr das Stringende suchen muss, wieder 
abfangen.

Aber probehalber kannst du ja mal folgendes machen
1
unsigned char* ucsHTML_string = (unsigned char*)XRAMSTART;

Das meiste in deinem Programm sollte eigentlich weiterhin genauso 
kompilieren wie vorher. Und deine maximal mögliche Stringlänge sollte 
jetzt eigentlich deutlich höher sein. Allerdings benutzt du damit nur 
eine RAM-Bank. Aber vielleicht reichen dir ja ~60kB als maximale 
Stringlänge schon.

von Karl H. (kbuchegg)


Lesenswert?

Imi Übrigen:

> Ich muß vor dem Senden des HTML-Strings die Länge wissen und
> es ist am einfachsten, wenn ich einen langen String erzeuge
> (die gesamte HTML-Daten) und dann mit strlen() die Länge abfrage.

Das ist nicht unbedingt am Einfachsten.
Um die Länge zu kennen, musst du den kompletten String nicht generieren. 
Du musst nur die Längen der Einzelteile addieren. Aber dazu muss der 
Komplettstring nicht als solcher existieren.

von Sven P. (Gast)


Lesenswert?

Simon K. wrote:
> Ich glaube aber auch nicht, dass strcat das Mittel der Wahl ist.
> Immerhin muss der bei jedem Aufruf erneut das Ende des Strings finden.

Wobei man das noch elegant umgehen kann. Im Zweifelsfall, indem man 
einen Zeiger auf das abschließende Null-Zeichen benutzt und den händisch 
mitverschiebt. Zwar eine Addition mehr, aber besser, als immer von 
Anfang an durch den String zu latschen.
Irgendwo gabs auch mal einen strcat-Verschnitt, der direkt als 
Rückgabewert einen Zeiger auf das Stringende lieferte.

von Peter (Gast)


Lesenswert?

Warum muss der der Kompette HTML string überhaupt im speicher sein? wenn 
du ihn bloss zusammenbaust um ihn dann zu senden dann kannst du die 
kleinen stückchen auch sofort senden. Der Browser warten schon bis alles 
da ist.

von Karl H. (kbuchegg)


Lesenswert?

Peter wrote:
> Warum muss der der Kompette HTML string überhaupt im speicher sein?

Weil sie vorher die Stringlänge kennen muss.
Ich vermute mal, dass der HTML String nicht direkt zum Browser geht, 
sondern über eine Zwischenstation. Und das Protokoll dorthin sieht vor, 
dass zuerst die Datenlänge und dann die Daten selbst gesendet werden.

Aber ich geb dir vom Prinzip her recht. Der komplette String muss nie 
tatsächlich existieren. Muss man halt den String im Grunde genommen 
2-mal zusammenbauen: Einmal um die Länge zu ermitteln und einmal um die 
Einzelteile zu verschicken. Mit ein bischen C-Trickserei rund um 
Funktionspointer kann man das ganze so hinkriegen, dass eine einzige 
HTML-Generierroutine beides bei 2 Aufrufen erledigen kann. Einmal zeigt 
ein Funktionspointer auf eine Funktion, welche die Einzellängen addiert 
und das andere mal zeigt er auf eine Funktion, welche die Einzelteile 
verschickt. Dadurch bleibt das ganze auch wartbar und man läuft nicht 
Gefahr, dass die Zählroutine was anderes zählt als die Verschickroutine 
dann tatsächlich auf den Weg bringt. Und das beste am Ganzen: Die Größe 
des HTML Strings ist überhaupt nicht mehr durch den vorhandenen lokalen 
Speicher begrenzt. Lediglich die Einzelteile müssen in den Speicher 
passen.

von Martin M. (martin69)


Lesenswert?

Am Anfang habe ich die Länge per Hand zusammen gezählt (etliche Zeilen 
mit strlen() ). Das ist aber ein ziemlicher Programmieraufwand und 
ziemlich fehlerbehaftet. Wenn ich etwas in der HTML-Datei hinzufüge, muß 
ich immer an 2 Stellen herum basteln. Das war bisher ein ziemliches 
Gefiesel....

Mit dem externen RAM will ich mir den Aufwand sparen:
1. String zusammen basteln
2. Länge mit strlen() erzeugen
3. HTTP-Header mit der Länge generieren und senden
4. HTML-String senden
5. fertig

Die Länge benötigt der Browser (HTTP-Header). Wenn man eine zu geringe 
Länge anbibt, dann wartet der Browser unendlich lange auf den Rest (der 
nie kommt). Wenn man mehr schickt als die Länge im Header, dann gibts 
auch Probleme. Es scheint auch irgend eine Möglichkeit zu geben ohne die 
Angabe der Länge, aber das ist keine "saubere" Sache.

In der Zwischenzeit habe ich Routinen erstellt:
* strcpy_XRAM
* strcat_XRAM
* strlen_XRAM

Geht auch prima mit Texten vom internen RAM. Nur habe ich ein neues 
Problem: 80...90% meiner Texte sind fest und stehen daher im Flash. Wie 
mache ich da eine Zuweisung von einer Speicherstelle im Flash in das 
externe RAM????

Den String vom Flash mit "strcpy_P" zuvor in einen temporären String ins 
RAM zu legen ist ja ziemlich umständlich. Aber wenn es nicht anders 
geht, müßte ich es halt so machen....

von Martin M. (martin69)


Lesenswert?

@Karl heinz Buchegger (kbuchegg) (Moderator)

das mit dem...

unsigned char* ucsHTML_string = (unsigned char*)XRAMSTART;

... werde ich mal probieren. Klingt irgendwie plausibel (obwohl ich 
nicht allzuviel Ahnung von der Programmierung habe).

von Karl H. (kbuchegg)


Lesenswert?

Martin M. wrote:
> Am Anfang habe ich die Länge per Hand zusammen gezählt (etliche Zeilen
> mit strlen() ). Das ist aber ein ziemlicher Programmieraufwand und
> ziemlich fehlerbehaftet. Wenn ich etwas in der HTML-Datei hinzufüge, muß
> ich immer an 2 Stellen herum basteln. Das war bisher ein ziemliches
> Gefiesel....

:-)
Das kann man in C auch so machen, dass man nur an einer Stelle verändern 
muss. Zugegeben: ist ein bischen trickreich, aber machbar.
:-)


> Den String vom Flash mit "strcpy_P" zuvor in einen temporären String ins
> RAM zu legen ist ja ziemlich umständlich. Aber wenn es nicht anders
> geht, müßte ich es halt so machen....

Es geht immer auch anders.
Die Frage ist immer nur, ob der Aufwand dafür steht :-)

Nichts und niemand hindert dich daran, eine Funktion strcpy_XRAM_P zu 
schreiben. Ist doch nur ein 5-Zeiler.

von Peter (Gast)


Lesenswert?

Ich muss noch mal nachfragen, die Länge ist kein Pflichfeld in HTTP, du 
darfst sie nicht falsch machen das ist klar - lass sie doch einfach weg. 
Sobalt die verbindung geschlossen wurde zeig der Browser die webseite 
an.

von Martin M. (martin69)


Lesenswert?

> Nichts und niemand hindert dich daran, eine Funktion
> strcpy_XRAM_P zu schreiben. Ist doch nur ein 5-Zeiler.

sorry, daß ich mich vielleicht etwas blöde anstelle, aber wie kopiere 
ich Bytes vom Flash? Wenn ich irgend eine Adresse angebe, dann meint der 
Compiler doch immer, daß es eine im RAM ist, oder? Wie sage ich dem 
Compiler, daß es eine Adresse vom Flash ist?

von Simon K. (simon) Benutzerseite


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

Martin M. wrote:
>> Nichts und niemand hindert dich daran, eine Funktion
>> strcpy_XRAM_P zu schreiben. Ist doch nur ein 5-Zeiler.
>
> sorry, daß ich mich vielleicht etwas blöde anstelle, aber wie kopiere
> ich Bytes vom Flash? Wenn ich irgend eine Adresse angebe, dann meint der
> Compiler doch immer, daß es eine im RAM ist, oder? Wie sage ich dem
> Compiler, daß es eine Adresse vom Flash ist?

pgm_read_byte()

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29
1
void strcpy_XRAM_P( char* XRAMAddr, const char* FlashAddr )
2
{
3
  char c;
4
5
  do {
6
    c = pgm_read_byte( FlashAddr++ );
7
    XRAM_write_byte( XRAMAddr++, c );
8
  } while( c != '\0' );
9
}

(Oder was du dann auch immer für einen Datentyp für die Adressangabe ins 
XRAM genommen hast. Pointer wird zu klein sein (bei 128KB) wird wohl auf 
long hinausgelaufen sein)

von Martin M. (martin69)


Lesenswert?

Danke! Ich werde es später ausprobieren. Jetzt ist erst mal verspätetes 
Mittagessen dran.

von Boris Bukowski (Gast)


Lesenswert?

Hi,

du mußt tatsächlich nicht die Länge angeben.

HTTP/1.0 200 OK
Content-Type: text/html

<html><body><h1>It works!</h1></body></html>

Einfach die strings rausschieben und am Ende die Connection zu machenh. 
man kann zwar alles andere implementieren, muß man aber nicht :)

Boris

von Martin M. (martin69)


Angehängte Dateien:

Lesenswert?

@Karl heinz Buchegger

das mit dem ...
unsigned char* ucsHTML_string = (unsigned char*)XRAMSTART;
... war ein super Tipp!! Das finde ich besser, als eigenen strcat, ... 
zu programmieren.

Im Anhang ein Beispiel, wie das nun aussieht.

Nochmals vielen Dank an alle, die sich meinem Problem gewidmet haben.


Gruß
Martin

von Martin M. (martin69)


Lesenswert?

> Einfach die strings rausschieben und am Ende die Connection zu machenh.

jetzt ist es eigentlich schon nicht mehr interessant für mich, aber 
trotzdem bin ich neugierig. Wie macht man eine Connection zu? Ich 
benutze ein XPORT zum Verbinden von Ethernet und AVR.

von Boris Bukowski (Gast)


Lesenswert?

Ich habe bisher nur unter Linux/Unix TCP Server Programmiert. Wenn du 
mir einen Link zu deinem TCP Stack oder der Server Software gibst, kann 
ich dir zeigen wie du den Code stark vereinfachen kannst.

Boris

von Martin M. (martin69)


Lesenswert?

ich habe auf dem Layout 2 Typen vorgesehen:

* XPort Direct+
http://www.lantronix.com/device-networking/embedded-device-servers/xport-direct-plus.html

und den
* XPORT
http://www.lantronix.com/device-networking/embedded-device-servers/xport.html


Die Server-Funktionalität im XPORT´s nutze ich jedoch nicht. Da hätte 
man mit Java was programmieren müssen, das war mir dann doch zu viel 
Aufwand, mich da einzuarbeiten. Ich sende vom AVR die HTML´s an den 
XPORT und der wandelt diese dann als Ethernet-Signale.

von Boris Bukowski (Gast)


Lesenswert?

Hi,

ich hab keine Doku über die Protokolle.

Boris

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Bei HTTP/1.1 fähigem Browser kann man sogar in "Teilen" alles 
rübersenden...
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.