Hallo,
über die UART-Schnittstelle lese ich Daten von einem SD-Karten Modul
zurück,
dazu benutze ich diese Funktion:
void GetSDString0(uint8_t* BUF) // Bytes empfangen
{
uint8_t NextChar;
NextChar = UART0_getc(); // Warte auf das nächste Byte
while(NextChar != '.') // empfange Zeichen bis "." kommt
{
*BUF++ = NextChar;
NextChar = UART0_getc();
}
}
In einer Header-Datei steht:
uint8_t RetSD[30]; // Bytes vom SD-Card Modul
Funktions-Aufruf:
GetSDString0(RetSD);
Leider gehen die empfangenen Bytes verloren, sobald die Funktion
verlassen wird. In RetSD steht dann nur Müll.
Meine Frage: wie kann ich auf die Array-Daten von RetSD zugreifen?
Wolfgang Z. schrieb:> In einer Header-Datei steht:> uint8_t RetSD[30]; // Bytes vom SD-Card Modul>> Funktions-Aufruf:> GetSDString0(RetSD);
Frage dich bitte, ob hier ein Pointer steht und wie das überschreiben
des Arrays abgefangen wird. Bzw. der Füllgrad mitgeteilt wird.
für mich fehlen da noch vom Type size_t Angaben über die maximale Größe
und die Anzahl der geschriebenen Zeichen als Rückgabewert einer
Funktion.
Ich kenne die Funktion UART0_getc() nicht. Ich gehe mal davon aus, dass
die dafür gedacht ist, nach Empfang eines Bytes aufgerufen zu werden (im
Empfangsinterrupt zB). Wenn du die Funktion einfach in der
while-Schleife runterratterst und diese dann am Ende nur stumpf das
Empfangsregister ausliest, steht da freilich nur Unfug und du rauschst
auch mit absoluter Sicherheit über die Arraygrenzen hinweg.
>*BUF++ = NextChar;
und
>*BUF= NextChar;>BUF++;
sind identisch.
Würde mich nicht wundern, wenn der Compiler aus beidem das Gleiche
macht.
Bist Du Dir sicher, dass der Datentyp "uint8_t", praktisch für alles,
der Richtige ist? Theoretisch sollte es zwar gehen, aber...
UART0_getc() liest einfach nur den von der UART-Bibliothek mitgebrachten
Ringpuffer aus und setzt den Zeiger ein Element weiter. Im Highbyte
liefert die Funktion eine Fehlerinformation mit. Du musst also
auswerten, ob du überhaupt schon was empfangen hast, bevor du
UART0_getc() aufrufst. Das ufert in deiner Funktion zu einem Polling -
Massaker aus, oder du musst die umschreiben, dass du sie über mehrere
Zyklen verteilen kannst.
Alles in allem schätze ich, dass du dich erstmal bisschen mit der
Bibliothek auseinandersetzen solltest.
Nachtrag:
Natürlich MUSS sichergestellt sein, dass weniger (gleich) als 30 Zeichen
vor dem Punkt kommen. Sonst wird es spannend.
Wie Patrick geschrieben hat: Was passiert, wenn kein Zeichen empfangen
wurde, oder Du zu schnell abfragst (noch kein Zeichen da)? Ich kenne die
verwendete Funktion auch nicht.
>In einer Header-Datei steht:> uint8_t RetSD[30]; // Bytes vom SD-Card Modul
Wenn Du die Variablen-Definition in die Header-Datei schreibst, wird für
jede Datei, die die Header-Datei einbindet, eine Variable gleichen
Namens angelegt. Das erklärt den beobachteten Effekt.
Die Variablen-Definition gehört in die c-Datei und die Deklaration mit
einem vorangestellten "extern" in die Header-Datei. Dann sollte es
funktionieren.
> In einer Header-Datei steht:> uint8_t RetSD[30]; // Bytes vom SD-Card Modul
Ich vermute, du hast mehrere RetSD.
In die .h-Datei gehört nur die Deklaration (mit einem "extern" davor),
in einer .c-Datei dann die Definition so wie oben ohne "extern".
Danke für die zahlreichen Rückmeldungen dazu.
Habe jetzt folgende Änderungen vorgenommen:
1. alle Variablen Definitionen stehen jetzt in der C-Datei
2. in der h-Datei steht: extern void GetSDString0(uint8_t* BUF);
Leider ohne Erfolg!
Hier die Funktion zum Empfang von Zeichen (Bytes):
uint8_t UART0_getc(void) // Byte empfangen
{
while (!(UCSR0A & (1<<RXC0))); // warten bis Byte verfügbar
return UDR0; // Byte aus UDR zurückgegeben
}
Zur Kontrolle gebe ich den Array-Inhalt RetSD so über HTerm aus:
for(i = 1; i < 25; i++)
{
UART0_putc(RetSD[i]);
i++;
}
Vielleicht liegt der Fehler in der Ausgabe der Bytes?
void UART0_putc(uint8_t xB) // Byte senden
{
while (!(UCSR0A & (1<<UDRE0))); // warten bis Senden möglich
UDR0 = xB; // sende Byte
}
Es könnte auch ganz trivial an einem Stack-Überlauf liegen. Ausgelöst
durch zu viele verschachtelte Funktionsaufrufe bzw. Funktionen mit zu
vielen lokalen Daten.
Wolfgang Z. schrieb:> uint8_t UART0_getc(void) // Byte empfangen> {> while (!(UCSR0A & (1<<RXC0))); // warten bis Byte verfügbar return> UDR0; // Byte aus UDR zurückgegeben> }
Ist das richtig, dass "return UDR0" im Kommentar steht, oder ist das ein
Copy&Paste-Fehler?
Das muss so aussehen:
1
uint8_tUART0_getc(void)// Byte empfangen
2
{
3
while(!(UCSR0A&(1<<RXC0)));// warten bis Byte verfügbar
4
returnUDR0;// Byte aus UDR zurückgegeben
5
}
Hast Du hier auch alle Warnungen, die der Compiler ausgibt, hier
mitgeteilt?
Frank M. schrieb:> Ist das richtig, dass "return UDR0" im Kommentar steht, oder ist das ein> Copy&Paste-Fehler?
Der Kommentar stammt wohl von einem anderen µC Modell das nur einen UART
hat.
Stefan ⛄ F. schrieb:> Der Kommentar stammt wohl von einem anderen µC Modell das nur einen UART> hat.
Häh? die UART0_getc-Funktion ist definiert als uint8_t, da hat sie auch
gefälligst was zurückzuliefern. Tut sie aber nicht, da das
return-Statement auskommentiert ist.
Schließlich wird auch der Rückgabewert benutzt:
Frank M. schrieb:> die UART0_getc-Funktion ist definiert als uint8_t, da hat sie auch> gefälligst was zurückzuliefern. Tut sie aber nicht
Da steht doch "return UDR0", nicht auskommentiert:
Wolfgang Z. schrieb:> uint8_t UART0_getc(void) // Byte empfangen> {> while (!(UCSR0A & (1<<RXC0))); // warten bis Byte verfügbar> return UDR0; // Byte aus UDR zurückgegeben> }
In deinem Zitat fehlt jedoch ein Zeilenumbruch. Vielleicht ein
Darstellungsproblem im Web Browser?
Hallo Frank,
aber das ist nichts auskommentiert:
uint8_t UART0_getc(void) // Zeichen empfangen
{
while (!(UCSR0A & (1<<RXC0))); // warten bis Zeichen verfügbar
return UDR0; // Zeichen aus UDR0 zurückgegeben
}
Der Inhalt von UDR0 wird zurückgeliefert!
Der ATmega328 hat zwar nur einen UART, aber trotzdem heißt das Register
UDRn.
Stefan ⛄ F. schrieb:> In deinem Zitat fehlt jedoch ein Zeilenumbruch.
Als Moderator kann ich Beiträge editieren. Da ist kein Zeilenumbruch
drin. Aber so viele Leerzeichen, dass es in einer nicht-proportionalen
Schrift so aussieht, als ob das Return-Statement darunter stände. Steht
es aber nicht.
Deshalb fragte ich den TO, ob das ein Copy&Paste-Fehler sei.
Besser ist auch, C-Code in
Wolfgang Z. schrieb:> Der Inhalt von UDR0 wird zurückgeliefert!
Das sehe ich anders, siehe Screenshot. Und auch im Editor-Fenster sehe
ich keinen Zeilenumbruch.
Bitte verwende
Wolfgang Z. schrieb:> Also kein Speicher-Überlauf?
Zeig mal dein ganzes Programm. Normalerweise läuft der Speicher alleine
durch Funktionsverschachtelungen nicht so schnell über. Heap wirst du
auch keinen benutzen, und die 30 Byte für den Puffer sind unkritisch.
Was aber schnell blöd werden kann, ist sind Daten, bei denen der '.'
erst nach mehr als 30 Zeichen erkannt wird. Deine Empfangsroutine
schreibt ja fröhlich immer weiter in den Puffer, ohne Größenbegrenzung.
Oliver
Hallo Oliver,
das mit der Zeichen-Menge habe ich vorher geprüft.
Es werden nur Datei-Namen mit .txt zurückgeliefert und
für mich sind nur die Namen ohne .txt relevant.
Die max. Dateinamen-Länge beträgt 18 Zeichen, somit ist ein
Array-Überlauf nicht möglich.
Wolfgang
Wolfgang Z. schrieb:> Die max. Dateinamen-Länge beträgt 18 Zeichen, somit ist ein> Array-Überlauf nicht möglich.
Warum benutzt Du dann einen Buffer mit 30 Zeichen? Aus Angst?
Wolfgang Z. schrieb:> In RetSD steht dann nur Müll.
Warum uint8_t, wenn es sich doch um Text handelt?
Also char.
Oder?
Desweiteren vermisse ich immer noch die nul am Stringende.
So gibt es keine Information im Programm wie lang der String ist.
Neben anderem Ungemach, wundert mich nicht, dass da Mist ausgegeben
wird, wenn kein Programmteil weiß, wie lang/groß irgendwas ist.
Wolfgang Z. schrieb:> somit ist ein> Array-Überlauf nicht möglich.
Is klar....
Vielleicht versuchst du es mal, den Empfangsinterrupt zu nutzen? Oder
hast du den schon irgendwo drin und der schießt dir quer? Der würde vom
reinen Code dann ca so aussehen, zeichenzaehler muss natürlich mit 0
initialisiert werden:
1
uint8tempfange_SDString(void){
2
externvolatileuint8tzeichenzaehler;// Zähler für empfangene Zeichen
3
externvolatileuint8_tRetSD[30];
4
uint8t*tempzeiger;// Hilfszeiger, wo das nächste empfangene Zeichen hinsoll
5
uint8tretval;
6
7
tempzeiger=&RetSD[0]+zeichenzaehler;//Zeiger auf nächstes freies Zeichen ermitteln
8
*tempzeiger=UDR0;// Empfangsregister an freie Stelle kopieren
9
if(zeichenzaehler<29){// Überlauf abfangen
10
zeichenzaehler++;
11
}
12
if(*tempzeiger=='.'){// bei Empfang Stopzeichen Stringlänge zurückgeben
13
retval=zeichenzaehler;
14
zeichenzaehler=0;// Zeichenzähler zurücksetzen
15
}
16
else{
17
retval=0;// Null zurückgeben, wenn kein Stopzeichen empfangen
18
}
19
returnretval;
20
}
Der Code der ISR wäre dann
1
externvolatileuint8tempfangsstring[30];
2
externvolatileuint8_tRetSD[30];
3
uint8tretval;
4
5
retval=empfange_SDString();
6
if(retval){
7
for(uint8ti=0;i<retval;i++){
8
empfangsstring[i]=RetSD[i];
9
}
Alles bisschen unsauber, aber zum testen sollte es tun.
Wolfgang Z. schrieb:> Ich habe die Funktion eingesetzt und bekomme als Ergebnis eine 0.
Das ist aber ein seltsamer Zufall. Bis du sicher, dass du die richtige
Zahl ausgibst?
0 ist jedenfalls zu wenig. Damit kannst du keine einzige Funktion mehr
aufrufen, denn Funktionen brauchen Stack.
Hallo,
das Problem ist, dass das SD-Modul einen Mix aus ASCII und Bytes
zurück sendet.
Daher der Typ uint8_t, also kein normaler String, somit kann ich das
nul-Zeichen auch nicht anfügen.
Hallo an alle,
dank der vielen sehr nützlichen Hinweise, macht das Programm jetzt genau
das, was es machen sollte.
Vielen, vielen Dank für Eure Unterstützung!
Gruß
Wolfgang
Hallo Oliver,
die Lösung war eine Kombi aus verschiedenen Fehlern.
Dieser Hinweis von foobar
foobar schrieb:> Ich vermute, du hast mehrere RetSD.>> In die .h-Datei gehört nur die Deklaration (mit einem "extern" davor),> in einer .c-Datei dann die Definition so wie oben ohne "extern".
Und der Tip von Dirk B.
Und einen Fehler den ich selbst eingebaut und zuerst nicht gefunden
habe.