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:> 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
volatileuint8_tcenti_seconds;
33
34
volatileuint8_tm_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
intmain(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
chars[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
charbuff[6];
94
uint16_tx,y,z;
95
96
uint8_ti;
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
@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.
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.