Datum:
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:
// Hauptprogramm // RX_BUF_SIZE = 128 while(1) { if(uartrx) { uartrx = 0; while(rx_rdp != rx_wrp) { mmc_write_byte(rx_buf[rx_rdp++]); rx_rdp &= RX_BUF_SIZE - 1; } } } |
ISR(SIG_UART_RECV)
{
if(state == STOP)
return;
if(uartrx && rx_wrp == rx_rdp)
{
gl_error = 1;
UCSRB &= ~(1 << RXCIE); // disable receiver interrupts
}
else
{
rx_buf[rx_wrp++] = UDR;
rx_wrp &= RX_BUF_SIZE - 1;
uartrx = 1;
}
}
|
void spi_init(void) { sbi(DDRB,PB2); sbi(DDRB,PB3); cbi(PORTB,PB5); sbi(DDRB,PB5); SPCR = ((1<<MSTR) | (1<<SPE) | (1<<SPI2X)); // enable SPI interface }; |
Gruß Moritz
Datum:
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
???
Datum:
Die Bedingung
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.
Datum:
Angehängte Dateien: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.
Datum:
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.
Datum:
Jim Meba schrieb: > Die Bedingung >
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.
Datum:
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.
Datum:
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.
Datum:
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.
Datum:
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! :)