Arduino steuert verschiedene SPI devices an

Hallo bin mal wieder am grübeln und zwar,
versende ich über eine GUI die parameter an dem Arduino, die dann in einer SD -Karte abgespeichert werden, um SPI-Devices anzusteuern.
So das Problem ist, dass das SPI-Protokoll nicht standardisiert ist wie zb I2C, d.h jeder Hersteller legt selbständig fest, in welcher weiße er die Parameter sendet bzw. welche er sendet z.b (Register, Data) oder (Instruction, Register, Data). So nun meine Frage, gibt es eine Möglichkeit, das SPI variabel beim Arduino zu programmieren sodass, man man jedes SPI-Device ansteuern kann?

ein Bsp wäre das Board hier, da gibt es mehrere Devices, doch jeder hat eine andere Übertragungsstrukur:

Ja
Beim SPI schickst Du nur einzelne Bytes. Was Du sichickst mußt Du programmieren.
Grüße Uwe

Also gibt es dort keine Variable lösung, die für alle Devices funktionsfähig ist?
Also meinst du muss ich das hardcoden für jedes Gerät?

Ja, jedes Gerät hat sein Protokoll mit dem es mit Daten gefüttert werden will denn auch jedes Gerät / IC hat verschiedenen interne Register die bestimmte Daten bekommen müssen.
grüße Uwe

Ok, danke dir uwe.
So jetzt mal eine frage ich habe das ja senden mit dem I2C gemacht, nur je nachdem brauchst du manchmal einen register noch dazu vor dem Data-Byte und manchmal nicht. So nun habe ich mir das so einfallen lassen, dass ich mit einer checkbox in meiner gui das dritte feld , indem ich den wet geben kann aktiveren und deaktivieren kann.
So dann werden die datensätz so gespeichert, wenn es aktiviert ist z.b: 2;4;5
wenn ich das dritte feld deaktivere: 2;4

so nun möchte ich ja diese Daten an dem I2C-Device senden zum ansteuern, dass sieht folendermaßen aus:

while(Datei.available())
      {
        //parseInt() schreibt integer werte(Zahlen aus),eine zahlen werden ignoriert und gibt werte als int
        byte adr = byte(Datei.parseInt()); //parseInt gibt int zurück und wandle um in byte
        byte reg = byte(Datei.parseInt()); //wandelt reg  int in byte um
        byte data = byte(Datei.parseInt()); //wandelt data int in byte um
        
        //sende die ausgelesenen Daten über i2c an Slave Device
        Wire.begin();
        /*beginne Transaktion*/
        Wire.beginTransmission(adr); //übergebe slave adress von csv Datei
        /*sende register und data-byte*/
        Wire.write(reg); //ausgewähltes Register wo master schreibt
              
        Wire.write(data); //data-byte
        
              
        Wire.endTransmission();

so nun sende ich da ja den inhalt von data (3.feld in GUI->was durchaktivierung übertragen kann und bei deaktiverung nicht übertragen wird). Wie kann ich sagen, dass wenn der inhalt der Variable data nicht leer ist, dann soll er das mit Wire.write senden?

Hatte es folgendermaßen versucht:

if(data!="")
{
  Wire.write(data);
}
aber es geht nicht, wüsstest du wie man es macht?

Und danke sehr für deine geduld

"" ist ein leeres String-Literal. Byte ist einfach eine Zahl. Daher z.B. Byte != 0

Das ist aber auch eine Definitionssache. 0 kann ja auch ein gültiger Wert sein. Am besten du lässt dir über Serial mal ausgeben was ParseInt in diesem Fall zurück gibt. Darauf fragst du dann ab.

Wie könnte ich das machen?
Weil in der arduino ide kannst du nicht debuggen :frowning:

Serial ist der Debugger für arme Leute :slight_smile:

Siehe hier:

Serial.println() - Arduino Reference (Print Line. Wie Print aber der nächste Aufruf schreibt in neuer Zeile)

void setup()
{
    Serial.begin(9600);
}

void loop()
{
   Serial.println(data);
}

Dann unter "Tools" den SerialMonitor öffnen

kennst du vllt einen nderen Compiler , mit dem man arduino programmieren kann und auch debuggen?

Das hat nichts mit dem Compiler zu tun (der übersetzt nur den Sourcecode in Maschinencode) sondern ist bei embedded System normal. Einfach weil der Code auf einem komplett anderen System läuft.

Es gibt in-circuit debugger für AVR/ATemga Controller (das ist ein Kasten der zwischen PC und Controller gehängt wird), aber da kommt dir dann wahrscheinlich die Abstraktion durch den Arduino in die Quere. Da kenne ich mich aber auch nicht mit aus.

Serenifly:
"" ist ein leeres String-Literal. Byte ist einfach eine Zahl. Daher z.B. Byte != 0

Das ist aber auch eine Definitionssache. 0 kann ja auch ein gültiger Wert sein. Am besten du lässt dir über Serial mal ausgeben was ParseInt in diesem Fall zurück gibt. Darauf fragst du dann ab.

So hab es versucht, und falls die data variable leer ist, gibt er eine = dafür aus.
So problem ist, wie mache ich es, aber wenn ich wirklich eine 0 versende, wie kann ich das unterscheiden, ob jetzt die GUI eine 0 sendet oder nichts, da er ja bei beiden Varianten eine 0 ausgibt? :frowning:

Du könntest es so abspeichern, dass du den Datensatz mit einem speziellen Zeichen terminiert. z.B. Strichpunkte für das Ende des Datensatzes und Kommas zwischen den Werten: 1,2;1,2,3;4,5;4,5;

Dann kannst du das verwenden:

Und danach das (das funktioniert ja jetzt angeblich ohne memory leaks):
http://arduino.cc/de/Reference/StringObject

Leider gibt es da keine split Funktion :frowning: aber mit indexOf(), lastIndexOf() und subString() kommt man da auch hin.

z.B. sowas wie String substring = string.subString(0, string.indexOf(',')) gibt dir die Zahl vor dem ersten Komma zurück

String substring = string.subString(string.indexOf(',') + 1, string.length()) sollte die andere Hälfte des Strings sein mit dem du danach weiterarbeitest bis er vollständig zerlegt ist.

Und indexOf() gibt -1 zurück wenn das Zeichen nicht drin ist. So kannst du bei der zweiten Hälfte überprüfen ob es eine einzelne Zahl ist oder sowas wie "1,2". Wenn er etwas anderes zurückgibt musst du nochmal aufteilen.

Hi danke vielmals.
Hätte ich aber nicht das Problem, dass mir diese Funktion die einträge 1,2,3 nicht als integer(zahlen) sondern als Strings behandeln, und dann wenn ich zb hexwerte versende, die nicht den realen hexwerten sondern die im ascii code dargestellen zeichen beziehen`?

Sorry diesen Teil verstehe ich nicht:

z.B. sowas wie String substring = string.subString(0, string.indexOf(',')) gibt dir die Zahl vor dem ersten Komma zurück

String substring = string.subString(string.indexOf(',') + 1, string.length()) sollte die andere Hälfte des Strings sein mit dem du danach weiterarbeitest bis er vollständig zerlegt ist.

Und indexOf() gibt -1 zurück wenn das Zeichen nicht drin ist. So kannst du bei der zweiten Hälfte überprüfen ob es eine einzelne Zahl ist oder sowas wie "1,2". Wenn er etwas anderes zurückgibt musst du nochmal aufteilen.

Ja, da muss man noch einen Weg finden, dass wieder in Integer zu verwandeln. Das weiß ich jetzt auch nicht, aber irgendwie muss es gehen. In Standard-C gibt es dafür atoi(), aber das will natürlich einen c-String (Null-terminiertes char array). Man kann auch die ganze String-Manipulation in C machen, aber damit wollte ich dich nicht quälen.

Ich garantiere auch nicht dass das so 1:1 geht, da ich das nur so kurz hingeschmiert habe. Das musst du mit Serial überprüfen :slight_smile:

Schau dir dazu die generell die String Dokumentation an.

IndexOf gibt der den Index des ersten Auftauchens des Zeichens an. SubString gibt dir einen Teilstring zurück. Die erste Zeile ist daher der Teilstring von 0 bis zum ersten Komma. Die zweite Zeile ist von einem Zeichen nach dem ersten Komma bis zum Ende. Wenn man dann mit diesem zweiten Teilstring nochmal das gleiche macht kann man den String so immer weiter runter teilen.

Noch was: du kannst auch die Datensätze auch einfach konstant lang machen und schreibst z.B. ein "x" für deaktiviert". Dann kannst du immer bis zum nächsten Terminator lesen (Strichpunkt in deinem Fall). Das machst du in einer for-Schleife 3-mal. Und wertest aus was drin ist. Fertig.

Speicherplatz auf der SD-Karte ist völlig egal hier und wie man sieht es manchmal zu kompliziert da ein paar Bytes sparen zu wollen. :slight_smile: In so eine Falle bin ich bei meinem letztem Projekt auch getappt.

Ok, ich habs glaube ich gefunden:
http://arduino.cc/de/Reference/StringToCharArray

String str = "123";
char* cstring [str.length() + 1];
cstring[str.length()] = '\0';    \\ <---- ganz, ganz wichtig! C-strings sind Null-terminiert.
str.toCharArray(cstring, str.length());

Damit sollte man ein String Object in einen c-string umwandeln können und diesen dann mit atoi() in einen Integer:
http://www.cplusplus.com/reference/cstdlib/atoi/

int i = atoi(cstring);

Der Code ist nur zum Anreiz. Getest habe ich das nicht!

Ist sicher alles ein wenig kompliziert, aber das haben String-Operation so an sich.

EDIT:
Das ist die bessere Wahl:

Damit kannst du bis zu einem Terminator direkt in ein char-Array lesen (den Puffer kann man z.B. auf 5 setzen, für 4 Ziffern + Null)! Wenn du dann deine Datensätze konstant lang machst, kannst du das immer 3-mal machen. Und dann atoi(). Lediglich beim letzten mal musst du abfragen was wirklich drinsteht und ob umgewandelt werden soll.

Ich würde daher auf die "Optimierung" mit der variablen Länge der Datensätze verzichten. Das ist mir auch erst eingefallen, nachdem ich deinem Ansatz gefolgt bin. Du hast dann ein paar Byte mehr auf der SD-Karte, aber der Code ist kürzer und läuft auch wesentlich schneller. :slight_smile:

Abgesehen davon , dass man String Objekte nicht braucht, und alles einfacher ist, wenn man gleich mit char array arbeitet, ...

  • brauchst du nur einen char * cstring und nicht ein ganzes Array davon
  • ist es meist besser, den Speicherplatz nicht dynamisch auf dem Stack durch eine lokale Variable, sondern statisch zu belegen.
  • sowohl getByes wie toCharArray fügen eine Endekkennung an, und schneiden zu lange Strings ab, so dass die 0 noch Platz hat.
  • leider ist der Zugriff auf den internen buffer ( der auch eine Endekennung enthält ) protected.
    ( Wenn ich nicht prinzipiell gegen String wäre, könnte ich eine abgeleitete Klasse erfinden,
    die das umkopieren erspart und gleich Zugriff auf den buffer erlaubt... :wink: )

Sereniflys Beispiel sollte also z.B. so aussehen:

String str = "123";
static char cstring [10];  // Platz für max 9 Zeichen
str.toCharArray(cstring, 10);  // sizeof(cstring)   geht auch, statt 10 hier fest einzutragen

Serial.print(cstring);  // sendet 3 Zeichen :    '1' , '2' , '3'

Oops. :slight_smile:

Ja, das mit char-arrays ist klar besser. Auf die String-Objekte bin ich nur gekommen weil ich erst blind dem Modell gefolgt bin wo die Länge der Datensätze variabel ist. Das ist so ein typisches Beispiel wo man meint etwas zu sparen und sich dann später nur Ärger einhaltet weil die Behandlung der Daten wesentlich komplizierter wird (das hatte ich auch letztens wo ich meinte mehrere Variablen in verschiedene Nibbles und Bits eines Bytes packen zu müssen).

Das wäre natürlich auch so mit c-strings gegangen, aber der Code wäre dann noch viel weniger verständlich. Wie ich es dann fertig geschrieben hatte, ist mir eingefallen, dass es wesentlich einfacher ist immer drei Zeichen zu schreiben und einen deaktivierten Wert durch einen Buchstaben zu kennzeichnen.

Das die Strings gleich Null-terminiert sind war aus der Dokumentation nicht ersichtlich. Wäre sinnvoll dass mal reinzuschreiben :slight_smile:

Hallo und zwar geht es wegen dem ansteuern der SPI geräte, versuche immer noch eine universelle lösung bzw eine allgemeine lösung zu finden um verschiedene SPI-Devices anzusteuern, doch das problem ist, dass einige 8-bit werte senden, die anderen 12 bit und einge brauchen 2 parameter die anderen 3.
Kennt jemand von euch einen ausweg?

Bin langsam am verzweifeln.
Die Sache ist die, dass das SPI.Protokoll leider keinen standard hat und das für jedes gerät immer anders ist.

SPI-Schnittstelle ist schon Standardisiert (mehr oder weniger).
Was nicht Standart ist ist sind die Daten, die an die verschiedenen Devices gesendet werden. Wie kannst Du denken daß Die Ansteuerung 2 verschiedener Devices die gleichen Daten erfordert.
Wenn Du mit dieser Logik kommst dann ist weder USB noch RS232 noch Centronics, noch RS485 Standardisiert.

Es ist das gleich wie wenn Du in eine Pizzeria, in den Chinesen nebenan oder in eine Frittenbude gehst und darauf bestehst mit den gleichen Worten was zu essen bestellen zu können. Schon bem Sprudel ist Schluß.

Grüße Uwe