I2C Slave Ausgabe

Hallo Community,
ich habe eine I2C Übertragung von einen TI MSP430F5529 (Master) zu einen Arduino Nano (Slave) aufgesetzt. Zu übertragen sind die Werte von 9 Variablen. Der Master Code ist soweit fertig und die richtigen Werte werden übertragen. (Zu sehen mit einen Logic Analyser) Als Slave Code habe ich den Beispiel Code der Wire.h library verwendet:

#include <Wire.h>

void setup() {
  Wire.begin(12);                // join i2c bus with address
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
  //  char c = Wire.read(); // receive byte as a character
  //  Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

Ich bekomme auch nach jedem Byte ein ACK. Jetzt zu meiner Frage:
Wie kann ich die einzelnen Werte auslesen und jeden Wert in eine einzelne Variable schreiben?
Mit dem seriellen Monitor bekomme ich auch keine Werte anzeigt…
Danke :slight_smile:

Du wirfst alle Zeichen weg bis auf das letzte Zeichen.
Dann weist Du einem int das gelesende letzte Zeichen zu.

Bist Du sicher, dass Du das so willst?

Wie sieht denn Deine Sendung aus? Wie soll der Slave erkennen, was wozu gehört?
Da fehlen noch einige Infos von Dir, bevor wir dazu etwas Sinnvolles sagen können.

Gruß Tommy

available() liefert > 0 , bis du die Daten liest.
Das hast du aber auskommentiert.

Die Auskommentierung habe ich übersehen. Also klassische Endlosschleife, wenn > 1 Zeichen da ist.

Gruß Tommy

Tommy56:
Du wirfst alle Zeichen weg bis auf das letzte Zeichen.
Dann weist Du einem int das gelesende letzte Zeichen zu.

Bist Du sicher, dass Du das so willst?

Wie sieht denn Deine Sendung aus? Wie soll der Slave erkennen, was wozu gehört?
Da fehlen noch einige Infos von Dir, bevor wir dazu etwas Sinnvolles sagen können.

Gruß Tommy

nein, bei dem example code habe ich nur die Adresse geändert…
Ich will jedes Byte einer Variable zuweisen. Bzw mit dem seriellen Monitor überprüfen, ob die richtigen Werte “ankommen”…
Kann man Wire.read so verwenden:

int x1 = Wire.read();
int x2 = Wire.read();

Bzw mit dem seriellen Monitor überprüfen, ob die richtigen Werte "ankommen"...

void receiveEvent(int howMany)

Wird im Interrupt Kontext aufgerufen.

Ein Serial.println(x) kann dann zur totalen Blockade führen.

combie:
Wird im Interrupt Kontext aufgerufen.

Ein Serial.println(x) kann dann zur totalen Blockade führen.

ok , ist gelöscht.
Und wie kann ich je ein Byte in eine Variable schreiben?

Du könnest eine direkte Zuweisung machen, wie von Dir vorgesehen (besser byte, als int) ansonsten könntest Du auch ein Array benutzen.
Wie willst Du aber sicher stellen, dass nix verloren geht und Du immer vom Anfang an liest?

Da solltest Du Dir noch ein Protokoll für die Anfangserkennung (und evt. auch fürs Ende) überlegen.

Gruß Tommy

Was meinst du mit direkter Zuweisung, bzw. hättest du die Syntax für mich?
Brauche ich eine Anfangs/End-Erkennung, wenn ich immer genau neun Byte übertrage? Zwischen der Übertragung habe ich einen delay von 1s (Master-seitig).

Der Slave muß wissen, was der Master sendet. Neun Werte kann man auf unterschiedliche Art und Weise übertragen.

Nach dem Start Statement und der Adresse werden immer genau 9 Bytes übertragen. Danach das Stop Statement, 1s delay und die aktualisierten Werte erneut gesendet....

Dann mache zuerst eine Erkennung auf das Startstatement, dann lies die 9 Bytes und dann prüfe das Stopstatement.
Da Du mit den Infos nicht raus rückst, kann man Dir auch nicht konkreter helfen.

Gruß Tommy

noname1111:
(Zu sehen mit einen Logic Analyser)

noname1111:
Nach dem Start Statement und der Adresse werden immer genau 9 Bytes übertragen. Danach das Stop Statement, 1s delay und die aktualisierten Werte erneut gesendet…

Tommy56:
Da Du mit den Infos nicht raus rückst, kann man Dir auch nicht konkreter helfen.

Ich habe den Eindruck, Ihr redet aneinander vorbei. Was man mit dem Logic Analyser sieht1), wird zu Teilen von der I2C-Hardware, wenn man sie nutzt, und auch der Bibliothek übernommen. Da muß man sich als C-Programmierer nicht im Detail drum kümmern.

Anm.:

  • Was noname1111 sieht, dürfte aussehen wie das Bild bei Sending data. Danke Nick Gammon!

Ok, unter Statement verstehe ich ein Kennbyte/eine Bytefolge.
Dann ist das doch eigentlich ganz einfach:

byte frage[10]; 
...

// Empfangs-Callback
void receiveEvent(int wieviel) {
  for(int i=0; i<wieviel; i++) {
    frage[i] = Wire.read();
  }
}

void setup() {
  ...
  Wire.onReceive(receiveEvent);
}

Dann stehen die Bytes in frage mit 0 <= x <10, wenn es 9 Bytes sind.

Gruß Tommy

volatile byte frage[10];

Und das auslesen von "frage" sollte dann auch atomic erfolgen.

Tommy56:
Ok, unter Statement verstehe ich ein Kennbyte/eine Bytefolge.

Der erste Schritt ist halt, den Begrifflichkeiten die selbe Bedeutung zuzuordnen.

Tommy56:
Dann ist das doch eigentlich ganz einfach:

Basierend auf der Doku von Nick Gammon habe ich mal einen Master- und einen Slave-Sketch geschrieben. Ich hoffe, atomic genügend berüchsichtigt zu haben.

Master:

#include <Wire.h>

const byte SLAVE_ADDRESS = 42;
byte verschiebung;

void setup ()
{
  Wire.begin ();
}  // end of setup

void loop ()
{
  Wire.beginTransmission (SLAVE_ADDRESS);
  for (byte x = 2; x <= 10; x++)
  {
    Wire.write (x + verschiebung);
  }  // end of for loop
  Wire.endTransmission();
  verschiebung++;
  delay (200);
}  // end of loop

Slave:

#include <Wire.h>

const byte MY_ADDRESS = 42, ANZAHL_WERTE = 9;
volatile byte werte[ANZAHL_WERTE];
volatile bool merker = false;

void setup ()
{
  Serial.begin(9600);
  Wire.begin (MY_ADDRESS);
  Wire.onReceive (receiveEvent);
}  // end of setup

void loop()
{
  if (merker)
  {
    merker = false;
    for (int j = 0; j < ANZAHL_WERTE; j++)
    {
      Serial.print(werte[j], HEX);
      Serial.print(' ');
      werte[j] = 0;
    }
    Serial.println();
  }
}

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
  if (howMany == ANZAHL_WERTE)
  {
    for (int j = 0; j < howMany; j++)
    {
      werte[j] = Wire.read(); // receive byte as a byte
      merker = true;
    }
  }
}  // end of receiveEvent

Ausgabe:

0 1 2 3 4 5 6 7 8 
1 2 3 4 5 6 7 8 9 
2 3 4 5 6 7 8 9 A 
3 4 5 6 7 8 9 A B 
4 5 6 7 8 9 A B C 
5 6 7 8 9 A B C D 
6 7 8 9 A B C D E 
7 8 9 A B C D E F 
8 9 A B C D E F 10 
9 A B C D E F 10 11 
A B C D E F 10 11 12 
B C D E F 10 11 12 13 
C D E F 10 11 12 13 14 
D E F 10 11 12 13 14 15 
E F 10 11 12 13 14 15 16 
F 10 11 12 13 14 15 16 17 
10 11 12 13 14 15 16 17 18 
11 12 13 14 15 16 17 18 19 
12 13 14 15 16 17 18 19 1A 
13 14 15 16 17 18 19 1A 1B 
14 15 16 17 18 19 1A 1B 1C 
15 16 17 18 19 1A 1B 1C 1D 
16 17 18 19 1A 1B 1C 1D 1E

Ob das mit einen TI MSP430F5529 zusammen harmoniert, weiß ich natürlich nicht :slight_smile:

Du liest im receiveEvent nur wenn if (howMany == ANZAHL_WERTE) ist.
Da haben wir jetzt 2 Varianten. howMany ist nach meiner Info immer die komplette Anzahl der übermittelten Bytes nach dem Ende der Übertragung. Damit sollte dann beim richtigen Senden die Abfrage obsolet sein.
Ist die Sendeanzahl nicht 9 (falsches Senden) - was passiert dann mit den nicht abgeholten Bytes?

Sollte man das dann sicherheitshalber nicht so machen?

uint8_t empfangeneBytes;
...
void receiveEvent (int howMany)
{
    empfangeneBytes = howMany;
    for (int j = 0; j < howMany; j++)
    {
      werte[j] = Wire.read(); // receive byte as a byte
    }
}  // end of receiveEvent

und im Loop auswerten, ob die Byteanzahl richtig ist, das Ergebnis also verwertbar? Jedenfalls wäre dann die Queue geleert.

Gruß Tommy

Tommy56:
was passiert dann mit den nicht abgeholten Bytes?

Möglicherweise blockieren sie den Ablauf. Danke für den Hinweis!

Tommy56:
… und im Loop auswerten, ob die Byteanzahl richtig ist, …

Das ist m. E. zu spät, da dann das Feld werte bereits übergelaufen ist. Das möchte ich verhindern.

Daher mein Vorschlag:

void receiveEvent (int howMany)
{
  for (int j = 0; j < howMany; j++)
  {
    byte wert = Wire.read(); // receive byte as a byte
    if (howMany == ANZAHL_WERTE)
    {
      werte[j] = wert;
      merker = true;
    }
  }
}  // end of receiveEvent

Ok, alles wird gelesen, auch wenn zu viele kommen, wird das weg geworfen. Passt.
Man könnte auch alles < ANZAHL_WERTE speichern und den Rest weg werfen.
Da wäre dann wieder die Anzahl sinnvoll zur Auswertung.
Aber egal, der TO hat damit genug Futter. Was er letztendlich will, muss er entscheiden.

Gruß Tommy

Erstmal danke für die vielen Antworten.

agmue:
Möglicherweise blockieren sie den Ablauf. Danke für den Hinweis!
Das ist m. E. zu spät, da dann das Feld werte bereits übergelaufen ist. Das möchte ich verhindern.

Daher mein Vorschlag:

void receiveEvent (int howMany)

{
  for (int j = 0; j < howMany; j++)
  {
    byte wert = Wire.read(); // receive byte as a byte
    if (howMany == ANZAHL_WERTE)
    {
      werte[j] = wert;
      merker = true;
    }
  }
}  // end of receiveEvent

Wie kann ich den Code verstehen? Für “ANZAHL_WERTE” die Zahl 9 einsetzen? Wenn 9 Bytes empfangen werden, wird jedes Byte in das Array geschrieben? Was soll ich mit der Variable merker anfangen?
Sorry für meine Unwissenheit…