I2C vs SPI (Verständnisproblem)

Hallo an alle Forummitglieder.

Ich habe mal eine Frage bezüglich der Schnittstellen I²C und SPI.

Bei I²C:
Taktleitung: SCL
Datenleitung: SDA (Eingang und Ausgang)
Adresse: Basisadresse + 3 Adresspins

Bei SPI:
Taktleitung: SCK
Datenleitung Input: SI
Datenleitung Output: SO
Und CS zum Auswählen des Bausteins wenn der Pin 0 ist

Jetzt das für mich kuriose:
Die Portexpander MCP23017 und MCP23S17 sind sich ja sehr ähnlich, abgesehen von der Schnittstelle.
Im gemeinsamen Datenblatt
MCP23017/MCP23S17
sehe ich aber, dass ich beim MCP23S17 (SPI) auch wieder 3 Adresspins vorhanden sind.

Ich dachte jetzt eigendlich das die Ansprache des Bausteins alleinig durch das setzen des CS auf 0 erfolgt. Wofür sind dann die Pins A0 bis A2 dann noch gut?
Im Konfigurationsregister (0x05) kann man durch setzen des Bits 3 die Adresspins ein- oder ausschalten.
Dann müsste er ja doppelt adressierbar sein, was in meinen Augen keinen Sinn ergibt.

Wo steh ich jetzt auf den Schlauch?

Wie sieht dann der Softwareseitige Unterschied beim Lesen und Schreiben der Register der beiden Portexpander aus?

Diese Bausteine haben anscheinend ein etwas anderes SPI Protokoll als man denkt. CS wird auch benötigt, aber man muss genau wie bei I2C die Adresse des Bausteins senden

Heiny:
Wie sieht dann der Softwareseitige Unterschied beim Lesen und Schreiben der Register der beiden Portexpander aus?

SPI.transfer() sendet und empfängt gleichzeitig ein Byte, während es bei I2C unterschiedliche Methoden für Senden und Empfang gibt.

Man muss hier aber bei SPI in einem Bit des Adress-Bytes angeben ob man sendet oder liest.

The SPI write operation is started by lowering CS. The Write command (slave address with R/W bit cleared) is then clocked into the device. The opcode is followed by an address and at least one data byte

The SPI read operation is started by lowering CS. The SPI read command (slave address with R/W bit set) is then clocked into the device. The opcode is followed by an address, with at least one data byte being clocked out of the device.

Ist also praktisch identisch zu I2C. Ich nehme an die unterschiedlichen Protokolle gibt es nur um verschiedene Hardware zu unterstützten, aber man sich das Leben einfacher (d.h. billiger) gemacht in dem man die Interna der zwei Versionen soweit wie möglich gleich gehalten hat. So ist nur der Transceiver Teil anders, aber die Dekodierung der Daten ist identisch.

Jetzt mal angenommen ich schalte die Adresspins ab, und lasse sie "In der Luft hägen" somit haben sie ja jetzt keinen definierten Zustand mehr und werden ja auch nicht mehr benötigt
Wie muss dann die zu sendende Adresse aussehen oder muss ich dann keine Adresse mehr senden?
Wie siehts dann mit den Lesen und Schreiben aus? Da R/W-Bit genau wie die Adressbits ja Teil des Geräte Operationscodes sind.

Das Adresse-Byte brauchst du so oder so. Das sieht ja so aus:
0 | 1 | 0 | 0 | A2 | A1 | A0 | R/W

Wenn du die Adress-Pins deaktivierst sind sie Null. Das steht auch Seite 17:

If disabled (HAEN = 0), the device’s hardware address is A2 = A1 = A0 = 0

Du kannst die Pins aber auch dann nicht einfach offen lassen!

The address pins (A2, A1 and A0) must be externally biased, regardless of the HAEN bit value.

Also, so oder so auf Masse legen

Also im Endefekt lohnt es sich nur die Adresse abzuschalten wenn ich mehr als einen Portexpander verwende.
Dann kann ich alle Adresspins auf GND legen.

Für einen müsste ja dann der Quelltext so aussehen:

#include "SPI.h"
#define address 0B01000000
#define GPIOB   0x13
#define IODIRB  0x01
#define CS 2

char bitArray[] = {0B01111111, 0B10111111, 0B11011111, 0B11101111, 0B11110111, 0B11111011, 0B11111101, 0B11111110};

void setup() {
  pinMode(CS, OUTPUT);
  digitalWrite(CS, LOW);
  SPI.begin();
  SPI.transfer(address);
  SPI.transfer(IODIRB);
  SPI.transfer(0x00);
  delay(1000);
}

void loop() {
  for(int i = 0; i <= 7; i++) {
    SPI.transfer(address);
    SPI.transfer(GPIOB);
    SPI.transfer(bitArray[i]);
    delay(500);
  }
}

Habe ich das jetzt richtig verstanden?

Theoretisch. Sicher bin ich mir da aber auch nicht. Habe mit dem Ding noch nichts gemacht.

Das Array sollte man als unsigned char definieren. Geht zwar so oder so, aber unsigned ist sauberer.

EDIT:
Nicht ganz. Du musst CS nach dem Schreiben wieder High setzen. Am besten du machst dir eine Funktion:

void sendByte(byte register, byte data)
{
   digitalWrite(CS, LOW);
   SPI.transfer(ADDRESS);   //das sollte auch groß geschrieben werden
   SPI.transfer(register);
   SPI.transfer(data);
   digitalWrite(CS, HIGH);
}

Heiny:
Jetzt mal angenommen ich schalte die Adresspins ab, und lasse sie "In der Luft hägen" somit haben sie ja jetzt keinen definierten Zustand mehr und werden ja auch nicht mehr benötigt ...

Unbeschaltete Eingänge sind immer eine sehr schlechte Idee. Diese Können Störungen einfangen und zufällig 1 oder 0 pigen annehmen. Somit ist der Baustein nicht mehr adressierbar.

Zu I2C:
Die Adresse des Bausteins wird nicht immer mittels 3 Adresspins eingestellt. Es kommt auf den Baustein an. Es gibt solche mit 3 Adresspins, solche mit 2 oder 1 und solche ohne. Es gibt auch solche da kann man die Adresse über SW programmieren. Klarheit schafft nur das Datenblatt.
Es sind ca 100 der 127 möglichen Adressen zulässig. Einige sind reserviert. Die Übertragungsgeschwindigkeit ist 100, 400 kHz oder 3,4MHz. Bei jedem Senden oder Empfangen muß der Baustein angesprochen werden. Das bedeutet ein Verlust an Bandbreite. Alle Bausteien sind paralell an den SDA und SCL angeschlossen. Limit an der max Distanz des Busses ist die Leitungskapazität bzw Der Strom der von den Eingängen benötigt wird.

Zu SPI.
Die Busfrequenz ist höher. Allerdings braucht jeder Baustein seine CS Signal. Dadurch entfällt der Bandbreitenverlust bei der Auswahl des angesprochenen Bausteins.

Zum Portexpander wurd bereits alles gesagt.
Grüße Uwe

Das ist die Theorie zu SPI und I2C.

In diesem Fall muss man auch bei SPI die Adresse senden (und das R/W beim Lesen setzen). Liegt wie gesagt vielleicht daran, dass sich der Chip Designer die Arbeit sparen wollte da einen anderen Befehlsdekoder zu entwickeln.

Gut dann ist ja alles klar.
Ist ja doch nicht so kompliziert wie ich anfangs dachte.

Vielen Dank euch beiden.

Grüße Stefan