HMC5883L auslesen Rohdaten auf SD Karte speichern

Hallo an alle,

ich möchte den HMC5883L über den I2C im "continuous" Mode bei 75Hz Output Rate auslesen und die 6 Bytes auf eine SD Karte speichern.

75Hz würde heißen das der Sensor alle 13,33ms einen neuen Wert ausgibt, oder?

Um diese Werte auszulesen und wegzuschreiben, soll ich eine "blink without delay" Funktion nutzen die alle 13ms aufgerufen wird? Oder gäbe es einen besseren oder eleganteren Weg.

Reicht die Zeit zum lesen und schreiben aus?

Danke für Eure Hilfe

Wegen des (hoffentlich vorhandenen) Wear-Leveling der SD Karte macht es Sinn eine Queue einzurichten.
Auch wird das schreiben in Sektor großen Blöcken Sinn manchen.

soll ich eine "blink without delay" Funktion nutzen

Sicherlich kein Fehler.

Da sowohl SPI als auch I2C in Hardware gegossen sind, ist schon eine gewisse Nebenläufigkeit gegeben. Leider wird das nicht zu 100% von den betreffenden Libs unterstützt/durchgereicht.

Da könnte Nacharbeit nötig sein. Ist das getan, reicht die Zeit tausendfach.

Auch wird das schreiben in Sektor großen Blöcken Sinn machen

Das macht, soweit ich mich richtig erinnere, die normale SD Library sowieso schon. (Wenn du 512 Byte einen Sektor nennst.) Was dazu führt, dass das Schreiben von 6 Byte sehr unterschiedlich lang dauern kann.

Das macht, soweit ich mich richtig erinnere, die normale SD Library sowieso schon.

Sicherlich!
Aber das ist Teil des Problems.

Sowohl die SD Lib, als auch Wire sind blockierend.
Kommen sich also derbe ins Gehege, wenn die 13,33ms eingehalten werden wollen.

#include <SPI.h>
#include <Wire.h>
#include <SD.h>

#define HMC5883L_ADDR 0x1E

const int chipSelect = 4;
File dataFile;

int x,y,z; 


void setup()
{
  Serial.begin(115200);
	if (!SD.begin(chipSelect)) {
	Serial.println("Card failed, or not present");
	return;
}
Serial.println("card initialized.");
	
	Wire.begin();

      Wire.beginTransmission(HMC5883L_ADDR); //open communication with HMC5883
      Wire.write(0x00);	//select Configregister A
	  Wire.write(0x18); //0 Sampels, Normal Measurement Mode
	  Wire.endTransmission();
	 
	  Wire.beginTransmission(HMC5883L_ADDR);
	  Wire.write(0x01);	//select Configregister B
	  Wire.write(0x20); //+/- 1,3GA
	  Wire.endTransmission();
	  
	  Wire.beginTransmission(HMC5883L_ADDR);
	  Wire.write(0x02); //select mode register
      Wire.write(0x00); //continuous measurement mode
      Wire.endTransmission();
  
 Serial.end();

}


void loop()
{
	
	 Wire.beginTransmission(HMC5883L_ADDR);
	 Wire.write(0x03); //select register 3, X MSB register
	 Wire.endTransmission();

	 //Read data from each axis, 2 registers per axis
	 Wire.requestFrom(HMC5883L_ADDR, 6);
	 if(6<=Wire.available())
		 {
		 x = Wire.read()<<8; //X msb
		 x |= Wire.read(); //X lsb
		 z = Wire.read()<<8; //Z msb
		 z |= Wire.read(); //Z lsb
		 y = Wire.read()<<8; //Y msb
		 y |= Wire.read(); //Y lsb
		 } 


	String dataString = "";
	dataString += millis();
	dataString += ",";
	dataString += x;
	dataString += ",";
	dataString += y;
	dataString += ",";
	dataString += z;


	  dataFile = SD.open("dataY.txt", FILE_WRITE);

	if (dataFile) 
		{
		dataFile.println(dataString);
		dataFile.close();

		}
	
}

Damit komme ich auf ca. 20 ms zwischen den einzelnen Punkten und bestätigt eure Meinung. Wo kann ich den Hebel ansetzen?

Vielen Dank

  1. open/close nur einmal machen , dann hast du nur ca. alle 20 Zeilen mal deine 20 ms Schreibpause
  2. Binär schreiben. Damit brauchst du statt ca 25 Zeichen/Zeile nur 10 byte/record → Alle 50 Sätze eine Schreibpause

(Wenn das nicht reicht, würde ich mir Wire.h ansehen, ob das nicht in einer Timer-ISR oder so geht)
Du willst ja nicht so schnell wie möglich, sondern nur alle 13,3 ms etwas machen.

75Hz würde heißen dass der Sensor alle 13,33ms einen neuen Wert ausgibt, oder?

Soviel ich das verstehe: alle 13,3 ms ändern sich die Register, und wenn du sie synchron abfragst, kriegst du sie alle. Wenn du öfters abfragst, kriegst du welche mehrfach, wenn du seltener frags, werden sie ungelesen überschrieben. I2C ist Master-Slave, dein Kompass ist immer der Slave, der Arduino-Master fragt wann er will.

[Nachtrag]:
3. nimm Serial statt SD Karte und speichere die Daten im PC.
Bei 115200 brauchst du – nicht blockierend – ca 2 ms / Zeile a 25 Zeichen.
D.h. wenn du nur alle 3 ms eine Zeile sendest, musst du nie warten bis was fertig gesendet wurde, da du 64 Byte Puffer hast.

michael_x:
[Nachtrag]:
3. nimm Serial statt SD Karte und speichere die Daten im PC.
Bei 115200 brauchst du -- nicht blockierend -- ca 2 ms / Zeile a 25 Zeichen.
D.h. wenn du nur alle 3 ms eine Zeile sendest, musst du nie warten bis was fertig gesendet wurde, da du 64 Byte Puffer hast.

Gute Idee, aber ich befürchte das es eine mobile Anwendung sein soll. Mal sehen was er meint.

sdFat bietet Optionen um das ganz schnell zu machen. Wenn man das aber schon mit der normalen SD Lib nicht hinbekommt, dann wird das da erst recht nichts. Die Library hat nicht die Arduino Komfort Funktionen und ist auf dieser low level Ebene etwas kryptisch. Beispiele dazu sind aber dabei.

Das Problem dass man am Ende des Blocks eine Verzögerung hat existiert da aber auch

Wo kann ich den Hebel ansetzen?

Wenigstens eine der Komponenten Nebenläufig machen.

Da SD + FAT doch recht komplex ist, würde ich vermutlich zuerst versuchen Wire ersetzen.

Gute Idee, aber ich befürchte das es eine mobile Anwendung sein soll. Mal sehen was er meint.

Ich wäre auch mit dem PC zufrieden. Wäre die Ausgabe der Werte dann über Serial.print()?

Wäre die Ausgabe der Werte dann über Serial.print()?

Ja, klar. Das ist schnell genug.
Deine 75 Hz Daten gehen locker bei 115200.
Hat den Vorteil, dass du es zum Test einfach im SerialMonitor anschauen kannst (Wenn du weniger sendest :wink: ).

Zum Speichern kann man z.B. HTerm oder so nehmen, damit kannst du alles in einer Datei mitloggen.