Liebe Freunde, Ich habe einen Data-Logger mit ATmega328/P gebaut, welcher einen ADXL345 über TWI/i2c ausliest und die Messwerte auf eine SD-Karte schreibt. Der ATmega läuft mit externen 8 Mhz Quarz ohne Prescaler. Problem: der Data-Logger schaft gerade einmal ca. 6 Samples pro Sekunde. Ist das was man erwarten kann von diesem Setup, oder sollte deutlich mehr möglich sein? Wäre mega froh um jeden Hinweis! Habe irgendwie die TWI/i2c Verbindung in Verdacht, aber weiss leider nicht wie ich weiter vorgehen kann um das Problem zu lösen. Viele Grüsse Niels
Nils schrieb: > Wäre mega froh um jeden Hinweis! Gehe systematisch vor. Stelle fest, wo der Flaschenhals ist?
Nils schrieb: > Ist das was man erwarten kann von diesem Setup Von welchem Setup? Du hast ja praktisch nichts gezeigt, wie sollen wir davon eine Ahnung haben? Nils schrieb: > aber weiss leider nicht wie ich weiter vorgehen kann Das Allermindeste wäre aufzuzeigen wie dein zeilicher Ablauf aussieht, Programm-Sourcecode wäre besser. Asl sicher sehe ich an dass man bei der SD-Karte viel falsch machen kann dass es (nicht) schnell funktioniert. Zweifel bestehen auch ob man überhaupt sampeln kann und gleichzeitig auf die Karte schreiben ohne dass das System ins Stolpern kommt. Kommt halt auf die Sample-Rate an.
Nils schrieb: > der Data-Logger schaft gerade einmal ca. 6 Samples pro Sekunde. Laut Datenblatt kann er von 0,1-3,2kHz Daten ausspucken. Deine 6Hz sind vermutlich schlechter Software geschuldet
@Nils (Gast) >Problem: der Data-Logger schaft gerade einmal ca. 6 Samples pro Sekunde. >Ist das was man erwarten kann von diesem Setup, Nö. > oder sollte deutlich mehr möglich sein? Deutlich mehr. Ich hab mal einen DMX-Rekorder gebaut, der hat mit 22kB/s von/auf SD-Karte gelesen/geschrieben und nebenbei DMX512 mit voller Bandbreite abgearbeitet. >Wäre mega froh um jeden Hinweis! Habe irgendwie die TWI/i2c Verbindung >in Verdacht, aber weiss leider nicht wie ich weiter vorgehen kann um das >Problem zu lösen. Mit einer systematischen Fehlersuche. Man muss schrittweise das Programm vereinfachen und sehen, was dabei passiert. Nimm einfach mal die Sensorauswertung raus und schreibe konstante Testdaten auf deine SD-Karte. Vermutlich hast du viel zuviele Warteschleifen etc. drin, die dir alles tierisch ausbremsen. Oder du öffnest und schließt deine Datei nach jedem Zugriff, was auch reichlich unsinnig ist.
@Nebel Stocherer (Gast) >Zweifel bestehen auch ob man überhaupt sampeln kann und >gleichzeitig auf die Karte schreiben ohne dass das System >ins Stolpern kommt. Kommt halt auf die Sample-Rate an. Das kann man, wenn man weiß wie. Die zeitkritische Datenerfassung läuft per Interrupt, der Schreibvorgang auf SD-Karte als normale Funktion. Über einen FIFO werden die Daten gepuffert. Der SD-Zugriff per SPI kann beliebig unterbrochen werden, da kommt nix aus dem Takt.
Vielen Dank schon mal für die Antworten, mich hat erstmal interessiert ob es überhaupt möglich ist schneller zu sampeln. Ausserdem ist der Code nicht sehr schön geschrieben. Hier nun der Code mit welchem ich den ADXL345 auslese und die Messwerte auf die UART schreibe, ohne den SD-Card Teil. Damit erreiche ich ca. 16 Hz, also auch nicht sehr viel. Mit einem Timer des ATmega wird alle 1/16 Sekunde ein Flag gesetzt. Ist das Flag nach der ADXL Messung nicht gesetzt, bedeutet dass dass das Sample zu langsam verarbeitet wurde und das Programm geht in einen Fehlerzustand. Was wäre der nächste Schritt bei der Fehlersuche?
1 | #include <stdint.h> |
2 | #include <avr/io.h> |
3 | #include <avr/interrupt.h> |
4 | #include <stdlib.h> |
5 | |
6 | #include "uart.h" |
7 | #include "i2cmaster.h" |
8 | |
9 | #ifndef F_CPU
|
10 | #define F_CPU 8000000UL
|
11 | #endif
|
12 | |
13 | #define BAUD 9600L
|
14 | |
15 | #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
|
16 | #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))
|
17 | #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)
|
18 | |
19 | #if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))
|
20 | #error systematic Baud rate error >1%
|
21 | #endif
|
22 | |
23 | //ADXL345 definitions
|
24 | #define ADXL345_W 0x3A
|
25 | #define ADXL345_R 0x3B
|
26 | |
27 | //programm state definitions
|
28 | #define DO_MEASUREMENT 1
|
29 | #define MEASUREMENT 2
|
30 | #define MEASUREMENT_DONE 0
|
31 | |
32 | volatile uint8_t centi_seconds; |
33 | |
34 | volatile uint8_t m_flag; // indicates whether a measurement should be done |
35 | |
36 | ISR(TIMER1_COMPA_vect) { |
37 | |
38 | if(centi_seconds < 100){ |
39 | centi_seconds++; |
40 | }
|
41 | else{ |
42 | centi_seconds = 0; |
43 | }
|
44 | |
45 | if(centi_seconds % 6 == 0){ // approx. 16.7 Hz |
46 | m_flag = DO_MEASUREMENT; |
47 | }
|
48 | }
|
49 | |
50 | |
51 | int main(void) |
52 | {
|
53 | |
54 | // set prescaler to 1
|
55 | CLKPR = 0x80; |
56 | CLKPR = 0x00; |
57 | |
58 | uart_init(); |
59 | uart_puts_p(PSTR("STARTING ADXL345 TEST\n")); |
60 | |
61 | // LED setup
|
62 | DDRB = (1<<PORTB1); // Port B1 as output |
63 | PORTB = (0<<PORTB1); // LED off |
64 | |
65 | // configure timer1 (16bit) for generating 10ms clock signal
|
66 | OCR1A = (78-1); // 100.16 Hz |
67 | |
68 | TCCR1B = (1<<WGM12)|(1<<CS12)|(0<<CS11)|(1<<CS10); // CTC mode and division by 1024 |
69 | TIMSK1 = 1<<OCIE1A; |
70 | |
71 | centi_seconds = 0; |
72 | //seconds = 0;
|
73 | |
74 | sei(); |
75 | |
76 | char s[1]; //buffer |
77 | |
78 | uart_puts_p(PSTR("INITIALIZING I2C LIBRARY\n")); |
79 | |
80 | i2c_init(); // initialize I2C library |
81 | |
82 | uart_puts_p(PSTR("SETTING DEVICE ADDRESS AND WRITE MODE\n")); |
83 | i2c_start_wait(ADXL345_W); // set device address and write mode |
84 | uart_puts_p(PSTR("WRITE ADDRESS\n")); |
85 | i2c_write(0x2D); // write address = POWER_CTL of ADXL |
86 | uart_puts_p(PSTR("SET MEASUREMENT BIT ON D3\n")); |
87 | i2c_write((1<<3)); // Set the measure bit on D3 |
88 | i2c_stop(); // set stop conditon = release bus |
89 | |
90 | |
91 | uart_puts_p(PSTR("ENTERING MEASUREMENT LOOP\n")); |
92 | |
93 | char buff[6]; |
94 | uint16_t x, y, z; |
95 | |
96 | uint8_t i; |
97 | |
98 | while(1){ |
99 | |
100 | if(m_flag == DO_MEASUREMENT){ |
101 | |
102 | m_flag = MEASUREMENT; |
103 | |
104 | i2c_start_wait(ADXL345_W); // set device address and write mode |
105 | i2c_write(0x32); // write address = 32 |
106 | i2c_rep_start(ADXL345_R); // set device address and read mode |
107 | |
108 | // multi byte read from sensor
|
109 | for(i=0; i< 5; i++){ |
110 | buff[i] = i2c_readAck(); |
111 | }
|
112 | buff[5] = i2c_readNak(); |
113 | |
114 | i2c_stop(); |
115 | x = (((int)buff[1]) << 8) | buff[0]; |
116 | y = (((int)buff[3])<< 8) | buff[2]; |
117 | z = (((int)buff[5]) << 8) | buff[4]; |
118 | |
119 | itoa(x, s, 10); |
120 | uart_puts(s); |
121 | uart_puts_p(PSTR(" \t")); |
122 | |
123 | itoa(y, s, 10); |
124 | uart_puts(s); |
125 | uart_puts_p(PSTR(" \t")); |
126 | |
127 | itoa(z, s, 10); |
128 | uart_puts(s); |
129 | |
130 | uart_puts_p(PSTR("\n")); |
131 | |
132 | if (m_flag == DO_MEASUREMENT) { |
133 | uart_puts_p(PSTR("\nFatal error -- unhandled tasks detected -- terminating.\n")); |
134 | PORTB = (1<<PORTB1); |
135 | while(1); |
136 | }
|
137 | m_flag = MEASUREMENT_DONE; // indicate that the measurement task has been completed |
138 | }
|
139 | |
140 | }
|
141 | |
142 | return 0; // never reached |
143 | }
|
@Niels (Gast) >nicht sehr schön geschrieben. Hier nun der Code mit welchem ich den >ADXL345 auslese und die Messwerte auf die UART schreibe, ohne den >SD-Card Teil. Damit erreiche ich ca. 16 Hz, also auch nicht sehr viel. Bei 9600 Baud? Schon mal über die Übertragungszeiten nachgedacht? Und nein, ich glaube dein UART läuft NICHT per FIFO und Interrupt, sondern einfachst per Polling und Warten. 1 Zeichen = 1ms 16 Hz == 62,5ms ~62 Zeichen. >Was wäre der nächste Schritt bei der Fehlersuche? Die UART-Ausgaben auf ein Minimum reduzieren, nämlich bestenfalls 1 Zeichen ala '.', damit man was sieht, die restlichen uart_put_s auskommentieren. Die itoa-Funktionen kann man erstmal drinlassen. >if(centi_seconds % 6 == 0){ Sowas macht man nicht, denn das braucht eine echte Division, die eher aufwändig ist. Wenn du das haben willst, nimm einen einfachen Zähler von 0-5. Das ist aber eher Nebensache und nicht der Grund für dein langsames Programm.
Kleiner Tipp zur Optimierung, auch wenn das gerade nicht dein
Hauptproblem ist:
> uart_puts_p(PSTR("\n"));
Das ist am Ende weniger effizient, als uart_put("\n") weil der lesende
Zugriff auf den Flash Speicher wesentlich aufwändiger ist, als
Lesezugriffe auf Strings im RAM. Ich benutze PSTR() daher nur für
längere Strings.
Wenn du später wieder die Schreibzugriffe auf die SD Karte einbaust,
dann bedenke, dass SD Karten Blockweise arbeiten. Einzelne Bytes zu
schreiben wäre ganz schlecht. Du musst außerdem in den Pausen, wo die SD
Karte beschäftigt ist, Daten in einem RAM Puffer sammeln. Da sind 100ms
durchaus normal.
@ Stefanus F. (stefanus) >Das ist am Ende weniger effizient, als uart_put("\n") weil der lesende >Zugriff auf den Flash Speicher wesentlich aufwändiger ist, als >Lesezugriffe auf Strings im RAM. Hör auf so einen Käse zu erzählen!!! Ein lds kostet 2 Takte, ein lpm 3. Das fällt beim Rest der Verarbeitung praktisch keine Mikrosekunde ins Gewicht! Mann O Mann! Immer diese "Expertentips" 8-( >Wenn du später wieder die Schreibzugriffe auf die SD Karte einbaust, >dann bedenke, dass SD Karten Blockweise arbeiten. Einzelne Bytes zu >schreiben wäre ganz schlecht. Macht sowieso keiner, jede gescheite Lib hat mindesten einen Puffer für 1 Sektor.
Merci für den Tipp mit der UART Falk! Habe den UART Code raus genommen, da lief es tatsächlich mit 100Hz. (Das weiß ich, weil über eine LED signalisiert wird, wenn die Sampling Rate nicht erreicht wird). Für das Schreiben auf die SD-Karte verwende ich FatFS (http://elm-chan.org/fsw/ff/00index_e.html) zusätzlich etwa folgenden Code:
1 | #include "ff.h" |
1 | f_mount(0, &Fatfs); |
1 | f_write(&File1, s, strlen(s), &bw); |
Das ist aber offenbar wieder viel zu langsam. Wahrscheinlich wegen dem was Stefanus gesagt hat. Selbst wenn ich nur ein einzelnes Zeichen schreibe, werden keine 100Hz erreicht. Wie weiter? Ist FaFS die richtige library? Welche Library wäre ggfs. besser geeignet? Angenommen ich würde die Daten nicht direkt auf die SD schreiben sondern zwischenspeichern bis etwas mehr Daten zusammengekommen sind, dann hätte ich ja immer noch das Problem, dass die Zeit zwischen zwei Samples nicht ausreicht um die Daten auf die SD-Karte zu schreiben. Wie löst man dieses Problem?
Niels schrieb: > Angenommen ich würde die Daten nicht direkt auf die SD schreiben sondern > zwischenspeichern bis etwas mehr Daten zusammengekommen sind, dann hätte > ich ja immer noch das Problem, dass die Zeit zwischen zwei Samples nicht > ausreicht um die Daten auf die SD-Karte zu schreiben. Wie löst man > dieses Problem? Welches Problem ? Auch mit SPI und Mega328P schafft man zwischen 170KB und 220KB beim schreiben auf die SD-Karte. Selbst bei nur 170KB brauchst du weniger als 3ms pro Sektor (512Byt). Das reicht locker für 250 Samples pro sekunde und da bleibt sogar genügend Zeit für Ausgabe der Daten über USART übrig - (natürlich nicht mit 9600B, sondern mit 115KB). P.S. Es war mir sowieso niemals klar, warum Leute denken, daß es bei 9600B weniger Fehler gibt und alles zuverlässiger läuft als bei 115KB - ich spreche von USART.
:
Bearbeitet durch User
Niels schrieb: > Selbst wenn ich nur ein einzelnes Zeichen > schreibe, werden keine 100Hz erreicht. Das wäre auch der ungünstigste Fall... Der beste Fall wäre das Schreiben von 512 Bytes am Stück, und der Offset im File muss auch durch 512 teilbar sein. Dann muss nämlich der Datensektor nicht von der Karte gelesen werden. Aber eine SD Karte kann beim Schreiben auch schon mal für 300-500ms abtauchen. Daher müsste man den I²C Sensor im Interrupt auslesen und dort auch puffern.
Marc V. schrieb: > Es war mir sowieso niemals klar, warum Leute denken, daß es bei 9600B > weniger Fehler gibt und alles zuverlässiger läuft als bei 115KB Weil die Atmega nur 1MHz per default takten, erreicht man die 115200 Baud einfach nicht. Und 9600 ist die nächsthäufigste Baudrate, deren Teiler bei 1 MHz noch halbwegs aufgeht.
Jim M. schrieb: > Weil die Atmega nur 1MHz per default takten, erreicht man die 115200 > Baud einfach nicht. Den Flash-Speicher vom ATmega lässt du doch auch nicht so, wie er vom Hersteller kommt. Was spricht also dagegen, beim Programmieren auch den Taktteiler abzuschalten?
@ Niels (Gast) >Merci für den Tipp mit der UART Falk! Habe den UART Code raus genommen, >da lief es tatsächlich mit 100Hz. (Das weiß ich, weil über eine LED >signalisiert wird, wenn die Sampling Rate nicht erreicht wird). Hast du ein Oszi oder Logic Analyzer? Da kann man das genau messen. Am Anfang der Sequenz ein Pin auf HIGH setzen, am Ende auf LOW. >f_mount(0, &Fatfs); >f_write(&File1, s, strlen(s), &bw); Klar. >Das ist aber offenbar wieder viel zu langsam. Wahrscheinlich wegen dem >was Stefanus gesagt hat. NEIN! > Selbst wenn ich nur ein einzelnes Zeichen > schreibe, werden keine 100Hz erreicht. Dann stimmt was nicht. Zeig uns deinen echten, vollständigen Code als Anhang. >Ist FaFS die richtige library? Ja, die ist sehr gut. > Welche Library wäre ggfs. besser >geeignet? Brauchst du nicht. >Angenommen ich würde die Daten nicht direkt auf die SD schreiben sondern >zwischenspeichern bis etwas mehr Daten zusammengekommen sind, Kann man machen, ist hier aber nicht das Thema. > dann hätte >ich ja immer noch das Problem, dass die Zeit zwischen zwei Samples nicht >ausreicht um die Daten auf die SD-Karte zu schreiben. Wie löst man >dieses Problem? Mit einem FIFO, denn den braucht man beim Schreiben von zeitkritischen Echtzeitdaten IMMER, denn hier hat der Stafan ausnahmsweise mal recht. EineSD-Karte kann beim Schreiben ab und an Pausen einlegen, welche bei 100ms und mehr liegen. Deine Daten mußt du in einer ISR lesen und in einen Software-FIFO schreiben. Im normalen Hauptprogramm mußt du den FIFO periodisch prüfen und wenn genügend Daten drinstehen diese auf SD-Karte schreiben.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.