mikrocontroller.net

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


Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: holger (Gast)
Datum:

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

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

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.htm...

Autor: Martin M. (martin69)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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...

Autor: Martin M. (martin69)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
anbei der SRAM-Test

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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-Tu...
void strcpy_XRAM_P( char* XRAMAddr, const char* FlashAddr )
{
  char c;

  do {
    c = pgm_read_byte( FlashAddr++ );
    XRAM_write_byte( XRAMAddr++, c );
  } while( c != '\0' );
}

(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)

Autor: Martin M. (martin69)
Datum:

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

Autor: Boris Bukowski (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Martin M. (martin69)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Boris Bukowski (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Martin M. (martin69)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe auf dem Layout 2 Typen vorgesehen:

* XPort Direct+
http://www.lantronix.com/device-networking/embedde...

und den
* XPORT
http://www.lantronix.com/device-networking/embedde...


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.

Autor: Boris Bukowski (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich hab keine Doku über die Protokolle.

Boris

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei HTTP/1.1 fähigem Browser kann man sogar in "Teilen" alles 
rübersenden...
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.h...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.