3.7 🚧 I2C Schnittstelle

[MezData: Links-I2C] [Portexpander HLF 8574] [Temperatursensor LM75A] [Druck- und Temperatursensor BMP280] [MezData: Discovery-Room][stm32-I2C-Lib][Elektrische Gedanken]

Einlesen in die Theorie der Schnittstelle

  1. Mit 2 Leitungen viele Komponenten anschließen. Lesen: [Wikipedia: I2C]
  2. Wozu und wer hat I2C-Schnittstelle „erfunden“?
  3. Wie lauten die Bezeichnungen der Bus-Leitungen und wofür stehen die Abkürzungen?
  4. Zeichnen Sie ein Blockschaltbild mit einem Controller und zwei Targets.
  5. Erklären Sie OpenCollector- bzw. OpenDrain-Ausgänge und warum PullUp-Widerstände nötig sind.
  6. Was ist ein „Wired-AND“?
  7. Bei welchen Zustand von SCL werden die Daten von SDA übernommen?
  8. Arduino-Wire-Operationen
Befehl / BeispielBeschreibung
#include <Wire.h> // I2C-LibraryI2C Library laden
Wire.begin();
Wire.begin(SDA, SCL); // Alternative Leitungen festlegen
Schnittstelle starten
Wire.setClock(frequenz);Optional die Übertragungsfrequenz anpassen, voreingestellt sind 100000 Hz siehe [Arduino-Doku]
Schreiben in BausteinAdresse des Bausteins angeben und mit Wire.write() in einen Puffer schreiben, am Ende Daten absenden aus dem Puffer mit Wire.endTransmission() an den Baustein senden.
Wire.beginTransmission(0x27);Daten sollen zu Zieladresse 0x27 gesendet werden, der Datenpuffer wird geleert.
Der Datenpuffer ist normalerweise 32 Byte groß (Kann geändert werden)
Wire.write(0x00)0x00 in den Datenpuffer schreiben
Wire.write(„Hallo“)„Hallo“ in den Datenpuffer schreiben
Wire.write(data, length)Den Inhalt eines Datenfeldes in den Puffer schreiben.
error=Wire.endTransmission();Die Daten aus dem Puffer an die Zieladresse versenden und den Bus wieder frei geben.
Wenn error==0 hat ein Slave mit ACKs geantwortet und die Daten verarbeitet.
Wire.endTransmission(false);Die Daten aus dem Puffer an Zieladresse versenden und den Bus noch nicht frei geben.
(Weil danach gleich Daten gelesen werden sollen mit Restart)
Lesen aus BausteinDen Baustein adressieren und #Bytes lang Daten anfordern und in den Puffer schreiben.
Wire.requestFrom(adress,#Bytes)#Bytes werden vom Baustein mit adress angefordert und in den Puffer abgelegt. Beim letzten Byte wird ein NAK vom Master gesendet.
x=Wire.read()Ein Byte wird aus dem Puffer gelesen und der Pufferzeiger um 1 erniedrigt.
x=Wire.available()Gibt die Anzahl der noch zu lesenden Bytes im Puffer zurück.
Arduino I2C Befehle zum Schreiben und Lesen

Mit Portexpander HLF8574T zwei LED über I2C im Wechsel leuchten lassen

Die LED-Anoden sind VDD (5V) verbunden. P1 und P0 des PCF8574-Moduls sind über 330Ω Widerstände an die Kathoden angeschlossen. Somit leuchten die LED, wenn an den P-Ausgängen eine 0 anliegt. Hintergrund: Die Ausgänge des HLF8574T liefern nur kurz bei einem Schreibvorgang höhere Ströme und sonst nur 100µA bei log. 1 (siehe Datenblatt HLF8574T). An den Bus können mehrere Targets angeschlossen werden. Damit sie unterscheidbar sind bekommt jedes Target eine eigene Adresse [RN-Wissen: I2C-Adressierung]. Bei unserem Modul sind die Bit A6..A3 fest (0b0100000 = 0x20). Mit den Jumpern (die blauen Dinger unter A2) können die Bit A2..0 von uns eingestellt werden. Somit ist für uns die Targetadresse von 0b0100000 = 0x20 bis 0b0100111 = 0x27 wählbar. Zunächst sei sie 0x20.

Versuchsaufbau
Versuchsaufbau mit HLF8574T
#include <Wire.h> // I2C-Library
#define ADRESSE 0x20 // Adresse des Bausteins im I2C-Bus, siehe Vorgabe des Herstellers

void setup() {
  Wire.begin();  //Alternatives PinMapping: Wire.begin(SDA, SCL);
}

void loop() {
  Wire.beginTransmission(ADRESSE); // Vorbereitung der Übertragung
  Wire.write (0xfe); // 0b1111 1110 in Puffer schreiben: Rote LED soll leuchten
  Wire.endTransmission(); // Puffer an Slave senden und Bus freigeben
  delay(500);
  Wire.beginTransmission(ADRESSE); // Vorbereitung der Übertragung
  Wire.write (0xfd); // 0b1111 1101 in Puffer schreiben: Grüne LED soll leuchten
  Wire.endTransmission(); // Puffer an Slave senden und Bus freigeben
  delay(500);
}

Bei der Übertragung mit Oszilloskop zuschauen

I2C Oszillogramm rote LED leuchtet
I2C Oszillogramm rote LED leuchtet

Die Geschichte zu dem Bild: SCL und SDA sind high. Ein Master will senden und zieht SDA auf GND, dies ist die Startbedingung. Um A6 zu übertragen zieht der Master SCL auf GND und legt den Wert von A6 auf SDA. Dann lässt er SCL los und dessen Pegel geht durch den PullUp-Widerstand auf high, jetzt wissen die Slaves, dass das Datenbit auf SDA gültig ist und übernehmen A6. Das Spiel wiederholt sich bis A0 und R/!W-Bit. Wenn bei einem Slave die gewünschte Adresse mit der eingestellen Adresse übereinstimmt zieht er die SDA-Leitung auf GND und gibt sein Ack=OK: Ich bin bereit für Daten. Der Master sendet nun 8 Bit Daten, der Slave quittiert dies mit weiteren Ack=OK Antworten. Hier wurde nur ein Byte zum Slave übertragen. Am Ende lässt der Master SCL und dann SDA los, dies ist die Stoppbedingung, damit zeigt er das Ende der Übertragung an.
Im nächsten Bild wird die Übertragung für grüne LED leuchten lassen gezeigt.

I2C Oszillogramm grüne LED leuchtet
I2C Oszillogramm grüne LED leuchtet

Was passiert, wenn kein Slave antwortet? Im Programm die Adresse von 0x20 auf 0x21 geändert. Kein Modul fühlt sich nun angesprochen und gibt sein Ack=OK:

I2C Oszillogramm keiner fühlt sich angesprochen
I2C Oszillogramm keiner fühlt sich angesprochen

Wird nun am Modul A0 auf + gejumpert fühlt es sich wieder angesprochen:

Modul mit Adresse 0x21 fühlt sich angesprochen
Modul mit Adresse 0x21 fühlt sich angesprochen

I2C-LCD ansteuern

Auf unserem Multifunction-Board befindet sich ein LCD-Modul, das über I2C angesprochen wird, leider an PA11 und PA12, die kein I2C können, daher müssen Drahtbrücken gesetzt werden.

Wenn man das LCD-Modul umdreht findet sich ein Bekannter, der Portexpander HLF8574T. Was macht der da? Tatsächlich sind die meisten preiswerten LCD-Module mit dem Chip HD44780 ausgestattet und der braucht mindestens 6 Leitungen. Daher wird zum sparen gerne ein I2C-Port-Expander verwendet [BLIT2008-Board-LCD]. Beim LCD-Modul gibt es keine Jumper sondern Lötbrücken gegen GND. Sind sie alle offen ist die Target-Adresse 0x27.

Es gibt einen Unterschied bei der Festadresse zwischen HLF8574T und HLF8574A:
HLF8574T: 0x20 HLF8574A: 0x38

Um auf dem LCD was auszugeben gibt es praktische Librarys:

#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd (0x27); // LCD-Adresse auf 0x27
void setup() {
   lcd.begin(16, 2); // initialize the lcd
   lcd.clear();
   lcd.setBacklight(255);
   lcd.setCursor(0,0); // erstes Zeichen, erste Zeile
   lcd.print("MezMedia.de");
}

void loop() { 
  
}
I2C-LCD

Den I2C-Bus abscannen um alle Targets zu finden

Oft gibt es das Problem, dass man ein Target verbindet, es aber scheinbar nicht antwortet. Hier ein kurzes Programm, dass alle Adressen abscannt und alle Targets auflistet. Modifizierter Code von dieser Seite: [i2c_scanner]

#include <Wire.h>
void setup(){
  Wire.begin();
  Serial.begin(9600);
  Serial.println("\nI2C Scanner");
}
void loop(){
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ){
    Wire.beginTransmission(address); // Target anfunken
    error = Wire.endTransmission();  // hat es geantwortet?
    if (error == 0){ // kein Fehler also Antwort
      Serial.print("I2C Target gefunden bei Adresse 0x");
      if (address<16) Serial.print("0");
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4){ // Fehler
      Serial.print("Unbekannter Fehler bei Adresse 0x");
      if (address<16) Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) Serial.println("Keine I2C Targets gefunden\n");
  else Serial.println("fertig\n");
  delay(5000);
}

Temperatur mit LM75A messen und auf LCD ausgeben

Nun habe ich die Formelsammlung gelesen und darin den LM75 entdeckt, dabei soll ein Byte übertragen und als Antwort zwei Byte empfangen werden. Also so ein Modul, allerdings mit einen LM75A bestellt: [amazon]

Angeschossen, den Scanner angeworfen und unter Adresse 0x48 antwortet es.
Allerdings wenn man auf der Unterseite mit dem Finger über die Kontakte für die einstellbare Adresse kommt ändert sie sich, die Eingänge A2..A0 hingen in der „Luft“. Ok, mit dem Lötkolben Lötbrücken setzen. Aus Neugier habe ich geprüft, ob die VCC-Kontakte der Brücken mit VCC verbunden sind: Leider nein, untereinander haben sie zwar Kontakt, aber eine Verbindung zu VCC konnte ich nicht feststellen. Die GNDs sind miteinander verbunden. Falls eine andere Adresse als 0x48 eingestellt werden sollte würde es mich nicht wundern, wenn es spinnt…

Gut zu erkennen sind die PullUp-Widerstände von 10kΩ für SDA und SCL auf der Platine.

LM75A-Modul
LM75A-Modul
LM75A-Modul

Also den LM75A ohne Library selber auslesen können, damit wir uns intensiver mit I2C-Kommunikation beschäftigen -RTFM: [Temperatursensor LM75A]
In der Forsa wird der LM75 dokumentiert, der kann nur 9 Bit Auflösung der Temperatur, beim LM75A sind es 11 Bit.

Der LM75 hat interne Register?

Beim HLF8574 war es einfach, es gab nur ein 1 Byte Register, das gelesen oder beschrieben werden konnte: Target-Adresse auswählen und 1 Byte transferieren.
Beim LM75 gibt es 4 interne Register. Ein „Pointer“-Register merkt sich welches dieser Register gemeint ist. Beim Schreiben muss immer der Pointerwert für Zielregister 0..3 gesendet werden. Beim Lesen wird einfach das zuletzt eingestellte Register verwendet. Will man ein anderes Register lesen muss zuerst der Pointer geändert werden. Steht alles mit Beispielen in der Doku. Muss ich das alles wissen um einfach die Temperatur auslesen zu können? Nein!
Voreingestellt ist Register 0 und dort steht in zwei Bytes die Temperatur: Zwei Bytes lesen und gut ist. Hier die Kodierung der Temperatur in den zwei Bytes:

MSByteLSByte
Bit7654321076543210
LM75D8D7D6D5D4D3D2D1D0xxxxxxx
LM75AD10D9D8D7D6D5D4D3D2D1D0xxxxx
Temperaturwert LM75(A)

Es ist total einfach: Im MSByte steht die Temperatur mit Vorzeichen (Bit 7) in °C, will man es genauer haben kriegt man beim LM75 noch die halben Grade im LSByte und beim LM75A die achtel Grade geliefert. Hier mein Test-Programm:

#include <Wire.h> // I2C-Library
#include <LiquidCrystal_PCF8574.h> // LCD
#define ADRESSE 0x48  //0b01001000 LM75A Adresse
LiquidCrystal_PCF8574 lcd (0x27); 
signed char byteH;   // das MSB mit Vorzeichen (signed ist wichtig) in Grad C
unsigned char byteL; // das LSB die 8tel Grad ohne Vorzeichen
float temp;

void setup() {
  Serial.begin(9600);
  Wire.begin();  //Alternatives PinMapping: Wire.begin(SDA, SCL);
  lcd.begin(16, 2); // initialize the lcd
  lcd.clear();
  lcd.setBacklight(255);
  lcd.setCursor(0,0); // erstes Zeichen, erste Zeile
  lcd.print("LM75A TempSensor");
}

void loop() {
  Wire.requestFrom(ADRESSE, 2); // 2 Bytes von Modul in Pufferspeicher lesen
  byteH = Wire.read(); // Hole das 1. Byte aus Pufferspeicher
  byteL = Wire.read(); // Hole das 2. Byte aus Pufferspeicher
  //byteH=0b11111111; // zum Testen ob -0.125 laut Doku rauskommt
  //byteL=0b11100000;
  temp = ((byteH<<3) + (byteL>>5))*0.125; // LM75A mit 0.125 Grad Auflösung
  lcd.setCursor(0, 1);
  lcd.print(temp);
  delay(1000);
}
I2C Timing-Diagramm
I2C Timing-Diagramm

Welche Temperatur wird gemessen, wie warm ist gerade der Sensor?

Lösung

22,62 °C

Nach dem 2. Byte sendet der Master NAK, was könnte das bedeuten? An dieser Stelle wäre ein Studium des Datenblatts [Temperatursensor LM75A] ab Seite 11 dienlich.

🧑‍🔬 Wie funktioniert Wire.requestFrom(ADRESSE, 2)

Befehl / BeispielBeschreibung
Wire.requestFrom(ADRESSE, 2);2 Bytes vom Slave ADRESSE in den Puffer laden.
bla = Wire.read(); Übertrage Byte aus Pufferspeicher in bla und „entferne es aus dem Puffer“
Wire.available()Gibt die Anzahl noch lesbarer Bytes aus dem Pufferspeicher zurück.
Arduino I2C Befehle zum Schreiben auf Slave

Wofür ist Wire.available() nütze? Wire.requestFrom(Adr,#Bytes) adressiert den Baustein mit Adr und verlangt einfach #Bytes, d.h. er liest #Bytes oft von dem Baustein Bytes in einen Puffer (max. 32 Bytes). Nach dem letzten zu lesenden Byte sendet der Master ein NAK, damit weis der Slave, dass die Anforderung zu Ende ist. Nach dem die Daten im Puffer sind können sie mit Wire.read() dort abgeholt werden, dabei wird ein IndexZeiger erhöht, man bekommt immer das nächste Byte.
Wire.available() gibt die Anzahl der noch verfügbaren Bytes im Puffer zurück.

🧐 Was geschieht, wenn der LM75A mehr Bytes liefern soll als er kann?

Dafür ein Test mit Wire.requestFrom(ADRESSE, 3);

I2C Timing-Diagramm
I2C Timing-Diagramm
i2C Timing-Diagramm mit Analog Discovery Studio
i2C Timing-Diagramm mit Analog Discovery Studio

Nach dem Datenblatt kann der Baustein 2 Bytes lesend liefern. Nun wird von ihm verlangt 3 Bytes zu bringen, nach dem 2. Byte gibt der Master kein NAK für das Ende sondern ein ACK und liefert weiter Takte über SCK. Der Baustein hat aber nichts mehr zu sagen, er schweigt, zieht SDA nicht mehr auf GND, es finden sich dann 0xFF im Puffer. Wenn ich 10 Bytes vom Baustein mit Wire.requestFrom(Adr,10) lese sind auch 10 Bytes im Puffer, egal ob der Baustein nach Byte 2 nur noch schweigt und 0xFF liefert.
Fehler in der Arduino-Forsa: Wire.endTransmission() ist nach Wire.requestFrom(…) sinnlos, sogar falsch.

DS3231 Extremely Accurate I2C-Integrated RTC/TCXO/Crystal

[AZ-Delivery RTC DS3231 Modul🔗] beinhaltet [DS3231: Datenblatt 🔗] und EEPROM [AT24C32 🔗]

Überfliegen Sie das Datenblatt es DS3231 und beantworten Sie diese Fragen:

  1. Kann die Slave-Adresse des Bausteins geändert werden, wie lautet die Baustein-Basis-Adresse, wie ist der Subadressbereich?
  2. Wie viele interne Register hat der Baustein?
  3. Wie viele Bit haben die Register?
  4. Zählt der Register-Pointer bei Schreib und Lesezugriffen hoch, gibt es einen Wrap-Around (Sprung von maximaler Adresse nach 0)?
  5. Wie sieht der Transfer aus, wenn der Master zum Slave sendet?
  6. Wie sieht der Transfer aus, wenn Master vom Slave Daten abruft?
Lösung
  1. Nein, die Adresse ist fix und lautet 0x68
  2. Er hat (0x12+1) = 19 Register.
  3. Die Register haben 8 Bit.
  4. Der Adresspointer zählt bei Schreib- und Lesezugriffen hoch und springt nach Adresse 0x12 wieder auf 0.
  5. Figure 3: Slave-Adresse Write, Register-Pointer setzen, Daten senden, Busfreigabe.
  6. Figure 4: Slave-Adresse Read, Daten lesen (beginnend mit dem Register-Pointer, der dabei erhöht wird), letztes Lesen mit NAK, Busfreigabe.
    Figure 5: Slave-Adresse Write, Register-Pointer setzen, Restart, Slave-Adresse Read, Daten lesen, letztes Lesen mit NAK, Busfreigabe.

Testprogramm

#include <Wire.h> // I2C-Library
#define RTC 0x68 // DS3231

void setup() {
  Wire.begin();  //Alternatives PinMapping: Wire.begin(SDA, SCL);
  Wire.beginTransmission(RTC); // Übertragung zu RTC vorbereiten
  Wire.write(0x00); // Registerpointer auf 0 setzen
  Wire.write(0x00); // 0 Sekunden
  Wire.write(0x59); // 59 Minuten
  Wire.write(0x23); // 23 Uhr
  Wire.write(0x4);  // Donnerstag (1=Mo..7=So)
  Wire.write(0x15); // 15
  Wire.write(0x02); // 02
  Wire.write(0x24); // 2024
  Wire.endTransmission(); // Daten zum Slave übertragen
}

void loop() {
  Wire.beginTransmission(RTC); // Übertragung vorbereiten
  Wire.write(0x00); // Register-Pointer auf 0
  Wire.endTransmission(false); // übertragen ohne Busfreigabe
  Wire.requestFrom(RTC,7); // hol die 7 Bytes in den Puffer
  delay(1000);
}

Das Stellen der Uhr ist einfach, das Lesen der Uhrzeit hat mich etwas beschäftigt.
Um die Uhrzeit aus zu lesen wird der interne Register-Pointer im DS3231 auf 0 gesetzt mit

Wire.beginTransmission(RTC);  // vorbereiten
Wire.write(0x00); // Register-Pointer auf 0
Wire.endTransmission(false); // übertragen

und dann werden die Bytes mit
Wire.requestFrom(RTC,7)
angefordert, es werden folgend vom Slave Sekunden, Minuten usw. zum Master übertragen. Nach Vorgabe in der Doku muss nach dem Write der Bus nicht freigegeben werden, es kann gleich mit dem Request begonnen werden.

Falls nur die Uhrzeit gelesen werden soll reicht eine Übertragung der ersten 3 Register mit
Wire.requestFrom(RTC,3).

🧑‍🔬 Welchen Wert hat jetzt der Register-Pointer, wie könnte das ermittelt werden?

Wire.endTransmission() verstehen

Mit Wire.endTransmission() wird der Inhalt des Puffers an den Slave gesendet und der Bus wieder frei gegeben.
Die Freigabe des Busses kann mit endTransmission(false) vermieden werden (Restart), um gleich mit Wire.requestFrom(RTC,7) mit dem Lesen zu beginnen.
Siehe Wire-Arduino-Referenz 🔗

Wire.endTransmission()
Wire.endTransmission()
Wire.endTransmission(false)
Wire.endTransmission(false)

Aufgabe: Die gelesenen Daten auswerten mit Wire.read() und Ausgabe auf dem LCD. Ergänzen Sie den Code:

#include <Wire.h> // I2C-Library
#include <LiquidCrystal_PCF8574.h> // LCD
#define RTC 0x68 // DS3231
LiquidCrystal_PCF8574 lcd (0x27); 
signed char byteH;   // das MSB mit Vorzeichen (signed ist wichtig) in Grad C
unsigned char byteL; // das LSB die 8tel Grad ohne Vorzeichen
float temp;

void setup() {
  Wire.begin();  //Alternatives PinMapping: Wire.begin(SDA, SCL);
  lcd.begin(16, 2); // initialize the lcd
  lcd.clear();
  lcd.setBacklight(255);
  lcd.setCursor(0,0); // erstes Zeichen, erste Zeile
  Wire.beginTransmission(RTC); // Uhrzeit stellen
  Wire.write(0x00); // Registerpointer auf 0 setzen wegen Sekunden
  Wire.write(0x00); // 0 Sekunden
  Wire.write(0x23); // 23 Minuten
  Wire.write(0x16); // 16 Uhr
  Wire.write(0x4);  // Donnerstag (1=Mo..7=So)
  Wire.write(0x15); // 15
  Wire.write(0x02); // 02
  Wire.write(0x24); // 2024
  Wire.endTransmission(); 
}

void loop() {
  Wire.beginTransmission(RTC);
  Wire.write(0x00); // Registerpointer auf 0 setzen
  Wire.endTransmission(false); // übertragen ohne Busaufgabe
  Wire.requestFrom(RTC,3); // hol die 3 Bytes in den Puffer
  char sekunden = Wire.read();
  ...
  lcd.setCursor(0, 0);
  lcd.printf("%02x:%02x:%02x",stunden,minuten,sekunden); // Zeit ausgeben
  delay(1000);
}
Lösung
char minuten = Wire.read();
char stunden = Wire.read();

Aufgabe: Die Temperatur des Chips steht in 0x11 und 0x12 (Doku lesen). Ergänzen Sie das Programm um die Code-Sequenz um sie an Position (10,0) der LCD-Anzeige ausgeben zu können.

Lösung
Wire.beginTransmission(RTC);
Wire.write(0x11); // Registerpointer auf MSB Temperatur setzten
Wire.endTransmission(false);
Wire.requestFrom(RTC,2); // hol die 7 Bytes in den Puffer
byteH = Wire.read(); // Hole das 1. Byte aus Pufferspeicher
byteL = Wire.read(); // Hole das 2. Byte aus Pufferspeicher
temp = ((byteH<<2) | (byteL>>6))*0.25; // mit 0.25 Grad Auflösung
lcd.setCursor(10, 0);
lcd.print(temp);

EEPROM AT24C32

Netterweise ist in dem RTC-Modul noch ein EEPROM [AT24C32 🔗] verbaut. In diesem Baustein wollen wir nun eine Botschaft schreiben.
Überfliegen Sie das Datenblatt und beantworten Sie diese Fragen:

  1. Kann die Slave-Adresse des Bausteins geändert werden, wie lautet die Baustein-Basis-Adresse, wie ist der einstellbare Adressbereich?
  2. Wie viel Byte Speicher hat der Baustein und wie ist er organisiert?
Lösung
  1. Der Baustein hat 3 Pins zum Einstellen einer Sub-Adresse. Die Basisadresse ist 0x50 (Figure 1.) Der Adressbereich ist 0x50..0x57.
  2. 4Ki Byte Speicher, organisiert in 256 Seiten mit 32 Bytes.

🧑‍🔬 Das EEPROM hat es in sich: Beim Schreiben mehrerer Bytes muss beim Überschreiten der Seitengrenze (Seite ist 32Byte groß) beachtet werden, dass wieder bei Byte 0 der Seite weiter geschrieben wird. Beim Lesen mehrerer Bytes wird dagegen auf die nächste Seite gesprungen..
Wire.write(„Bla“): Hier ergibt sich das Problem, das die 0 am Ende des Strings nicht ins EEPROM geschrieben wird, somit weis man beim Lesen später nicht wann der String zu Ende ist. Abhilfe: Nach String schreiben noch Wire.write(0) schreiben..

Aufgabe: Erstellen Sie ein Programm. Schreiben Sie bei der Initialisierung „Ich beschreibe ein EEPROM“ ab Speicheradresse 0 in das EEPROM und geben Sie in der loop() den Text wieder auf der seriellen Schnittstelle aus. Der Baustein hat die I2C Adresse 0x57.

Lösungsvorschlag
#include <Wire.h> // I2C-Library
#define EEPROM 0x57 // AT24C32

void setup() {
  Serial.begin(9600);
  Wire.begin();  //Alternatives PinMapping: Wire.begin(SDA, SCL);
  char text[] = "Ich beschreibe ein EEPROM";
  Serial.printf("Textlaenge: %d\n",sizeof(text));
  Wire.beginTransmission(EEPROM); // Übertragung vorbereiten
  Wire.write(0x00); // Speicheradresse High
  Wire.write(0x00); // Speicheradresse Low
  Wire.write(text); // Text senden, die Null am Ende des Strings wird verschluckt
  Wire.write(0);    // Notwendig,damit String terminiert ist
  Wire.endTransmission(); // an Slave senden
}

void loop() {
  char botschaft[32]; // Textbotschaft
  int bIndex=0;       // Index für Botschaft
  char c;
  Wire.beginTransmission(EEPROM);
  Wire.write(0x00); // Speicheradresse High
  Wire.write(0x00); // Speicheradresse Low
  Wire.endTransmission(false);
  Wire.requestFrom(EEPROM,32); // hol 32 Bytes in den Puffer
  while(Wire.available()&&bIndex<sizeof(botschaft)){
    c=Wire.read();
    botschaft[bIndex]=c;
    bIndex++;
    if(c==0) break;
  }
  Serial.println(botschaft);
  Serial.println(bIndex);
  delay(5000);
}

Luftdruck und Temperatursensor BMP280

Siehe –>3.5 Sensoren. Der Lerneffekt bzgl. I2C-Bus ist dabei nicht besonders hoch, der Sensor ist komplex und es wird auch geraten die Library dafür zu verwenden.
Für ein Projekt, das sich mit dem Sensor beschäftigt aber brauchbar.

I2C-Schnittstelle Eckpunkte

  • Ist eine Serielle Schnittstelle
  • Arbeitet nach dem Master-Slave Prinzip
  • Haben einen gemeinsamen Takt (bzw. synchronisieren ihren Takt) – Standard-Übertragungsrate beimI2C-Bus ist 100kBit/s und maximale Übertragungsrate ist 3,4 Mbit/s
  • 1 Master kann bis zu 127 Slaves ansprechen
  • Halbduplex (bidirektional)
  • 2-Draht-Bus: SDA und SCL
  • Ablauf: Im Ruhezustand sind (SDA=1 und SCL=1)
    Start-Condition (SDA=0 und SCL=1) Geräteadresse (7 Bit) – Ack (1 Bit) – Daten (8-Bit) – Ack (1 Bit) – … – Stop-Condition (SDA und SCL auf High)

Fragen zu I2C

  1. Werden die Daten beim I2C-Bus in Halb- oder Vollduplex übertragen, erklären Sie den Unterschied beider Begriffe.
  2. Benennen Sie die Leitungen, die beim I2C-Bus verwendet werden und erklären Sie deren Funktion.
  3. Wofür sind PullUp-Widerstände erforderlich?
  4. Wie viele Teilnehmer kann der Bus bei 8 Bit-Adressierung inclusive der reservierten Adressen haben?
  5. Was versteht man unter Hersteller- und Subadresse?
  6. Wie wird unterschieden ob ein Teilnehmer lesen oder senden soll?
  7. Wird zuerst LSB oder MSB gesendet?

Aufgaben und abprüfbares Wissen

Sobald man alte und einfache Module verlässt wird es kompliziert, Details werden in die Nutzung von Library zu den Sensoren verlagert.
Falls man eigene Targets entwickelt siehe MezData: Discovery-Game sieht es schon spannender aus.

Schnipsel und ToDo

  • Grösse von PullUp und Serienwiderstand -> Link. Wann müssen wir uns um die PullUps selber kümmern?
  • ¡Selbst gebaute Targets siehe Discovery-Game! Event-Programmierung bei Bus-Action.
  • Alternative Ports für I2C verwenden.

14 Segementanzeige mit HT16K33

Synopsis: [https://www.amazon.de/dp/B08NP883DT] [Datenblatt] [An Introduction to 14-Segment LED Displays with the HT16K33 Driver] [A HT16K33 Display Library for easy printing to LED Modules] []

🤐 PICKIT SERIAL I2C DEMO BOARD

Habe ich nicht, ich frage mich, wozu ich diese Chips kaufen sollte, kommt aber wohl in Abi-Aufgaben dran.. [PICKIT SERIAL I2C DEMO BOARD 🔗] [UsersGuide 🔗]

[EEPROM 24LC02B 🔗] [Temperatursensor MCP9800 🔗] [12-Bit A/D MCP3221 🔗] [10-Bit D/A TC1321 🔗] [🔥 8-Bit I/O Expander MCP23008 🔗]

🪦 SAA 1064 ernsthaft?

In Abi-Aufgaben fand ich den SAA 1064. Der Chip ist sowas von out, ich finde kein Datenblatt mehr bei nxp.com!

Ein Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert