Forum: Mikrocontroller und Digitale Elektronik [atmega8] UART-Daten auf SD-Karte schreiben


von Moritz P. (moritz_p)


Lesenswert?

Guten Abend...

Projekte zu GPS-Loggern gibt es hier mittlerweile ja zu genüge, aber 
irgendwie will es bei mir nicht so funktionieren und ich kann auch in 
den anderen Threads nichts finden, was mein Problem lösen könnte.

Also die Daten kommen an der UART-Schnittstelle an und sollen dann 
einfach ohne Verarbeitung per SPI auf die SD-Karte (FAT-16, Blockgröße 
16kB, wenn man Windows trauen kann) geschrieben werden.
Den Quellcode dazu habe ich mir bei einem der anderen Projekte 
rausgesucht und im Prinzip funktioniert auch alles, doch es scheint ein 
Problem mit der Schreibgeschwindigkeit zu geben.

Die Eingangsdaten werden per UART-Interrupt abgefangen und dann erst 
einmal in einem Array (128 Byte) zwischengespeichert. Immer wenn dort 
Daten verfügbar sind, macht sich das Hauptprogramm an die Arbeit, die 
Daten dann aus dem Buffer auf die SD-Karte zu schreiben. Das Problem 
ist, dass irgendwann immer der Buffer komplett voll ist und dann das 
Programm einen Fehler meldet und abbricht, was ja auch sinnvoll, aber 
nicht erwünscht ist.

Der Mirkocontroller läuft per externem Quarz auf 7,3728 MHz, das SPI ist 
auf Master und die Hälfte (ein Viertel) der Taktfrequenz eingestellt. 
Das GPS-Modul liefert im Moment etwa 70 Bytes an Daten pro Sekunde und 
der Fehler tritt aber nicht immer an der gleichen Stelle auf, sondern 
manchmal sehr schnell, manchmal aber auch erst nach mehreren Minuten.

Sind das alle wichtigen Informationen?
Woran kann das liegen? Eigentlich müsste es doch möglich sein 70 Bytes/s 
zu schreiben und bei den anderen scheint es ja auch zu funktionieren.

Zum Schluss noch die (hoffentlich) relevanten Codestellen:
1
// Hauptprogramm
2
// RX_BUF_SIZE = 128
3
while(1)
4
{
5
   if(uartrx)
6
   {
7
      uartrx = 0;
8
9
      while(rx_rdp != rx_wrp)
10
      {
11
         mmc_write_byte(rx_buf[rx_rdp++]);
12
         rx_rdp &= RX_BUF_SIZE - 1;
13
      }
14
   }
15
}
1
ISR(SIG_UART_RECV)
2
{
3
   if(state == STOP)
4
      return;
5
6
   if(uartrx && rx_wrp == rx_rdp)
7
   {
8
      gl_error = 1;
9
      UCSRB &= ~(1 << RXCIE); // disable receiver interrupts
10
   }
11
   else
12
   {
13
      rx_buf[rx_wrp++] = UDR;
14
      rx_wrp &= RX_BUF_SIZE - 1;
15
      uartrx = 1;
16
   }
17
}
1
void spi_init(void)
2
{
3
   sbi(DDRB,PB2);
4
   sbi(DDRB,PB3);
5
   cbi(PORTB,PB5);
6
   sbi(DDRB,PB5);
7
8
   SPCR = ((1<<MSTR) | (1<<SPE) | (1<<SPI2X));  // enable SPI interface
9
};

Gruß
Moritz

von Stefan ++ (Gast)


Lesenswert?

Hallo,

deine Beschreibung ist äusserst spärlich und am Code sieht man auch 
nicht wirklich wie du die Daten auf die SD-Karte schreibst.
Nichts zu sehen von einem File-System und den entsprechenden 
Zugriffs-Methoden !!!

Ich kann nur hoffen das dein

> mmc_write_byte(rx_buf[rx_rdp++]);

nicht wegen jedem Byte immer wieder einen ganzen Sektor schreiben muss 
???

von Jim M. (turboj)


Lesenswert?

Die Bedingung
1
rx_wrp == rx_rdp

bezeichnet "Ringpuffer leer" und nicht "Ringpuffer voll". Das mit dem 
"uartrx" solltest Du ganz weglassen, das erzeugt ein fieses Race 
zwischen Interrupt und Hauptschleife.

Aber auch der Test mit "rx_rdp" im Interrupt kann in die Hose gehen, 
denn der Zugriff könnte - je nachdem was der Compiler für Code aufsetzt 
- nicht atomar sein: Du inkrementierst zuerst und maskierst im 2. 
Schritt. Ich würde auch diesen Test weglassen und einfach den Ringpuffer 
überlaufen lassen. Daten wegschmeissen musst Du ja sowieso.

> Eigentlich müsste es doch möglich sein 70 Bytes/s
> zu schreiben

Die Daten kommen bei GPS "am Stück". Wie hoch ist Deine Baudrate, 4800 
Baud?
Das sollte eine SD eigentlich spielend schaffen, aber die brauchen 
gelegentlich mal 300ms für das Wegschreiben eines Blocks. Dann würde es 
mit Deinem 128 Byte Puffer u.U. eng werden, wenn z.B. noch ein FAT 
Sektor dazu kommt.

von Moritz P. (moritz_p)


Angehängte Dateien:

Lesenswert?

Mh...ja, vergessen.
Also Dateisystem ist eben FAT-16, die Methoden zum schreiben sind nicht 
von mir, sondern aus einem der anderen Projekte, bzw. wohl ursprünglich 
mal von Mr. Data.
Im Anhang ist jetzt die Datei, die die Methoden für die SD-Karte und das 
Dateisystem beinhaltet.

Und nein, es wird nicht immer ein ganzer Sektor für jedes Byte 
geschrieben, sondern immer erst, wenn 512 Bytes zusammen sind.
Achja, die Speicherkarte hat 1GB Speicher und ist auch nicht voll.

Baudrate liegt bei 9600.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Jim Meba schrieb:
> Das sollte eine SD eigentlich spielend schaffen, aber die brauchen
> gelegentlich mal 300ms für das Wegschreiben eines Blocks. Dann würde es
> mit Deinem 128 Byte Puffer u.U. eng werden, wenn z.B. noch ein FAT
> Sektor dazu kommt.

Ich tipp ebenfalls auf einen Puffer-Überlauf bzw. eine Überholung, es 
ist ja ein Ringpuffer. Erhöhe doch mal versuchsweise auf 512 Bytes. Wenn 
der Speicher knapp wird, steig um auf den ATmega328, der hat 2 KB SRAM.

von Moritz P. (moritz_p)


Lesenswert?

Jim Meba schrieb:
> Die Bedingung
>
1
rx_wrp == rx_rdp
>
> bezeichnet "Ringpuffer leer" und nicht "Ringpuffer voll".

Aber der Fall würde doch dann eintreten, wenn der Schreibpointer den 
Readpointer im Prinzip überrunden würde - wie es Markus auch geschrieben 
hat - und somit der ganze Puffer vollgeschrieben ist. Nennt man es dann 
trotzdem "der Ringpuffer ist leer"?

Jim Meba schrieb:
> Das mit dem
> "uartrx" solltest Du ganz weglassen, das erzeugt ein fieses Race
> zwischen Interrupt und Hauptschleife.

Ohne das gibt es keine Möglichkeit zu prüfen, ob der Puffer irgendwann 
mal übergelaufen ist, oder? Naja, könnte ich mit leben. Dann muss man 
später eben die ungültigen Stelle rausfiltern.

Jim Meba schrieb:
> Aber auch der Test mit "rx_rdp" im Interrupt kann in die Hose gehen,
> denn der Zugriff könnte - je nachdem was der Compiler für Code aufsetzt
> - nicht atomar sein: Du inkrementierst zuerst und maskierst im 2.
> Schritt. Ich würde auch diesen Test weglassen und einfach den Ringpuffer
> überlaufen lassen. Daten wegschmeissen musst Du ja sowieso.

Das Inkrementieren und maskieren bezieht sich dann auf den Teil im 
Hauptprogramm bzw. dann letztendlich auch auf den Writepointer im 
Interrupt, oder?

Ohne jegliche Maske/Überprüfung ist man auf Puffergrößen beschränkt, die 
einer Zweierpotenz entsprechen, oder? Ansonsten könnte man theoretisch 
ja auch mit Modulo eine beliebige Größe angeben.

Markus W. schrieb:
> Erhöhe doch mal versuchsweise auf 512 Bytes. Wenn
> der Speicher knapp wird, steig um auf den ATmega328, der hat 2 KB SRAM.

512 Bytes passen nicht rein. Und dann bleibt als nächst kleineres ja nur 
noch 256 Bytes. Werde ich mal probieren.
Der ATmega328 ist ein wenig schwer zu bekommen, aber da finde ich sicher 
auch noch eine andere Alternative, auch wenn ich dann die Platine 
vielleicht noch mal neu machen muss.

von Korbi (Gast)


Lesenswert?

Moritz P. schrieb:
> Der ATmega328 ist ein wenig schwer zu bekommen, aber da finde ich sicher
> auch noch eine andere Alternative, auch wenn ich dann die Platine
> vielleicht noch mal neu machen muss.

Das könnte ich jetzt nicht behaupten. Der ATmega328 ist oft sogar 
billiger als der ATmega8 (bzw. ATmega88):
https://guloshop.de/shop/Mikrocontroller:::3.html

Bin mir jetzt nicht zu 100 Prozent sicher, aber die Pinbelegung müsste 
doch die gleiche bleiben, du brauchst also keine neue Platine zu bauen.

von Moritz P. (moritz_p)


Lesenswert?

Den ATmega8 kann ich aber bei Reichelt persönlich abholen, da spare ich 
mir die Versandkosten. ;) Und den 328 hat es da nicht.
Von der Pinbelegung ist der 328 wirklich gleich, aber um genau zu sein 
habe ich den ATmega8L verbaut, da dann alles mit 3,3V laufen kann. Das 
würde mit dem ATmega328 nicht gehen.

edit:// Ich muss mich wohl korrigieren. Der ATmega328 scheint laut 
Datenblatt auch mit 3,3V zu laufen...

Aber bisher scheint es mit dem etwas größeren Puffer (256 Bytes) auch so 
zu gehen. Nächstes mal kommt dann aber auf jeden Fall ein größerer µC 
zum Einsatz.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Moritz P. schrieb:
> Den ATmega8 kann ich aber bei Reichelt persönlich abholen, da spare ich
> mir die Versandkosten. ;) Und den 328 hat es da nicht.

Das ist natürlich praktisch, wenn Reichelt gleich nebenan hat. Wollen 
wir Wohnort tauschen? :-)
Aber wenn ich jetzt richtig gerechnet habe, kämst du im Versand ab zwei 
ATmega328 trotz Versandkosten schon billiger als bei zwei im Laden 
gekauften ATmega168.

> edit:// Ich muss mich wohl korrigieren. Der ATmega328 scheint laut
> Datenblatt auch mit 3,3V zu laufen...

Ja, der läuft sogar ab 1,8 Volt! Die Typen mit der 8 hinten (88, 168, 
328) haben eine modernere Architektur, sind aber trotzdem pinkompatibel 
zu Urgesteinen wie dem ATmega8. Echt praktisch.

> Aber bisher scheint es mit dem etwas größeren Puffer (256 Bytes) auch so
> zu gehen. Nächstes mal kommt dann aber auf jeden Fall ein größerer µC
> zum Einsatz.

Freut mich, dass es nun geht! Was den Ringpuffer-Überlauf betrifft, 
vielleicht kannst du ihn trotzdem abfragen, wenn du den Indexvergleich 
so machst, dass die Fehlermeldung schon dann kommt, wenn nur noch ein 
Byte frei ist.

Zum Thema Puffergröße: Du hast Recht, am einfachsten ist es, wenn du 
Zweierpotenzen verwendest. Falls nicht, dann besser nicht Modulo 
verwenden, das schluckt wahrscheinlich arg viel Rechenzeit. Besser, man 
fragt aufs Pufferende ab und setzt den Zeiger immer dann zurück, wenn er 
am Ende angelangt ist.

von Moritz P. (moritz_p)


Lesenswert?

Och nö, ich bleibe lieber hier wohnen. Oder hast du etwas anderes als 
Reichelt zu bieten, was mich überzeugen könnte? ;)

Solange es jetzt ohne Fehler läuft, bleibe ich erst einmal beim ATmega8. 
Wenn es aber dann doch immer wieder Probleme gibt, werde ich mal 
schauen, ob ich dann nicht doch die 328'er bestelle. Der ATmega168 hätte 
ja sowieso auch nur 1kB RAM.

Das Modulo recht langsam ist, habe ich mir schon fast gedacht. Naja, 
vielleicht klappt es ja auch auf Dauer mit den 256 Bytes, dann muss ich 
mir darüber keine weiteren Gedanken machen. :)

Nochmal viel Dank für die Tipps! :)

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.