ATmega328: Array-Daten gehen verloren

OP #6476849
Lesenswert?

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?
Gast #6476858
Lesenswert?

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.
Gast #6476861
Lesenswert?

Das
1
*BUF++ = NextChar;
 kommt mir komisch vor. Damit man es versteht, hätte ich es wie folgt 
gemacht (Wert an Adresse BUF schreiben, Adresse BUF erhöhen)

1
*BUF= NextChar;
2
BUF++;

Ansonsten die Startadresse als

1
GetSDString0(&RetSD[0]);

Übergeben.

Evtl noch die Zeichen zählen, dass du nicht über die Arraygrenzen 
schreibst.
Gast #6476893
Lesenswert?

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.
Gast #6476920
Lesenswert?

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.
#6476926
Lesenswert?

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.
#6476930
Lesenswert?

>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.
Gast #6476933
Lesenswert?

> 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".
OP #6477051
Lesenswert?

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
}
Gast #6477190
Lesenswert?

Wolfgang Z. schrieb:
> okay, aber wie kann ich das feststellen?

Es gibt zwei Zeiger, die sich aufeinander zu bewegen. Einer vom Stack 
und einer vom Heap. Wenn sie sich in der Mitte treffen, ist der Speicher 
voll.

Dort erklärt das jemand im Detail: 
https://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc
1
extern unsigned char __heap_start;
2
uint16_t momentan_frei = SP - (uint16_t) &__heap_start;

Das Ergebnis könntest du innerhalb jeder Prozedur ausgeben.
Moderator Persönliche Seite #6477200
Lesenswert?

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_t UART0_getc(void)    // Byte empfangen
2
{
3
  while (!(UCSR0A & (1<<RXC0)));  // warten bis Byte verfügbar
4
  return UDR0;      // Byte aus UDR zurückgegeben
5
}

Hast Du hier auch alle Warnungen, die der Compiler ausgibt, hier 
mitgeteilt?
Moderator Persönliche Seite #6477207
Lesenswert?

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:
1
NextChar = UART0_getc();
Gast #6477214
Lesenswert?

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?
OP #6477225
Lesenswert?

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.
Moderator Persönliche Seite #6477238
Lesenswert?

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
1
[c]C-Code[/c]
einzubetten.
#6477244
Lesenswert?

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
Gast #6477310
Lesenswert?

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....
Gast #6477317
Lesenswert?

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
uint8t empfange_SDString(void) {
2
extern volatile uint8t zeichenzaehler; // Zähler für empfangene Zeichen
3
extern volatile uint8_t RetSD[30];
4
uint8t *tempzeiger; // Hilfszeiger, wo das nächste empfangene Zeichen hinsoll
5
uint8t retval;
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
return retval;
20
}

Der Code der ISR wäre dann
1
extern volatile uint8t empfangsstring[30];
2
extern volatile uint8_t RetSD[30];
3
uint8t retval;
4

5
retval = empfange_SDString();
6
if (retval ) {
7
for (uint8t i = 0; i < retval; i++) {
8
empfangsstring[i] = RetSD[i];
9
}

Alles bisschen unsauber, aber zum testen sollte es tun.
OP #6477479
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren