Version Anpassungen
1.0 (2022-11-02) Initiale Version
1.1 (2022-11-04) Fehler beim Einlesen von mehreren Bytes behoben
1.2 (2022-11-07) writePacket: Parameter sendStop hinzugefügt
readFromReagister: Parameter repeatedStart hinzugefügt
1.3 (2022-11-13) Bei Fehlern fehlten an einigen Stellen die Stopp-Bedingung
1.4 (2022-11-13) Die Erzeugung von Stopp-Bedingungen fehlte an einigen Stellen
1.5 (2022-11-14) Erweiterte, aber inkompatible Version: UrsArduinoSoftI2cEx
- Methoden geben einen Fehlercode zurück.
- Timeout für SCL HIGH (keine Kontrolle über SCL Signal).

Motivation

Der beim Standard Arduino genutzte Prozessor besitzt eine Hardware-I²C-Schnitstelle, die über das Arduino-Framework einfach angesteuert werden kann. Die I²C-Schnitstelle erlaubt es, den Arduino als Master oder als Slave zu betreiben. Bei einem Projekt war es jedoch eine Schnittstelle als Slave zu betreiben und eine weitere als Master. Die hier beschrieben Arduino-Bibliothek implementiert einen Software-I²C-Master.

In­halts­ver­zeich­nis

UrsArduinoSoftI2c

Verwendung

Beispiel 1:

Beispiel 2:

Erweiterungen

Download


UrsArduinoSoftI2c

Ideengeber für diese Bibliothek war das Projekt Software I2C Library for AVR MCUs von ExtremeElectronics. Leider ist die dort dargestellte Lösung nicht fehlerfrei und lässt sich auch schlecht in einer Arduino-Umgebung verwenden.

Der besondere Trick ist die Art, wie die Bus-Signale generiert werden. I²C erwartet Open-Collector-Ausgänge (OC). Im Ruhezustand werden die Ausgänge nicht angesteuert. Die Pullup-Widerstände halten die Signalleitungen auf High-Pegel. Nur, wenn der Pegel auf Low gezogen werden muss, ist eine Aktion notwendig. Hierdurch ist es möglich, dass sowohl der Master als der der Slave bei Bedarf die Kontrolle über die Leitungen übernehmen kann. Das I²C-Protokoll ist hierauf aufgebaut.

Die AVR MCUs haben aber keine OC-Pins. Sie haben Tri-State-Ausgänge. Jedoch kann man über den High-Impedance-Zustand (Z) das "Loslassen" der Signalleitung ebenfalls erreichen. Dazu wird das entsprechende Bit im PORT-Register (Output-Latch) mit Low belegt. Das Belegen und Loslassen der Signalleitung erfolgt durch Umschalten des entsprechenden Bits im DDR-Register (Data Direction):

Verwendung

In der Bibliothek wird die Klasse UrsArduinoSoftI2cClass definiert. Wie bei Arduino üblich wird eine Instanz der Klasse vordefiniert: SoftI2c. Es ist aber ohne weiteres möglich, weitere Instanzen der Klasse anzulegen.

Die Klasse besitzt die folgenden öffentliche Methoden. writeToRegister und readFromRegister vereinfachen den Datenaustausch mit einem Slave, der eine Registerstruktur besitzt.

Methode Verwendung Beispiel
void begin(
    uint8_t sda,
    uint8_t scl)
Initialisiert die Instanz und muss aufgerufen werden, bevor andere Methoden der Klassen verwendet werden.
sda: Pin-Nummer des SDA-Pins.
scl: Pin-Nummer des SCL-Pins.

Hinweis: Die Pin-Nummern werden nicht geprüft. Werden ungültige Angaben gemacht, kann dies auch Auswirkungen auf andere Komponenten des Programms haben.
SoftI2c.begin(8, 9);
uint8_t writePacket(
    uint8_t   deviceAdr,
    uint8_t* pData,
    uint8_t   len,
    bool sendStop)
Sendet ein Datenpaket an den Slave.
deviceAdr: Adresse des Slaves [0..127].
pData: Zeiger auf das Byte-Feld mit den zu sendenden Daten.
len: Anzahl der zu sendenden Datenbytes.
sendStop: Am Ende der Übertragung wird eine Stopp-Bedingung gesendet. Die Voreinstellung ist true.
uint8_t rtc =
   SoftI2c.writePacket (

      0x30, writeBuffer,
      sizeof(writeBuffer));
uint8_t readPacket(
    uint8_t   deviceAdr,
    uint8_t* pData,
    uint8_t   len)
Fordert ein Datenpaket vom Slave an.
deviceAdr: Adresse des Slaves [0..127].
pData: Zeiger auf das Byte-Feld für die zu lesenden Daten.
len: Anzahl der zu lesenden Datenbytes.
uint8_t rtc =
   SoftI2c.readPacket (

      0x30, readBuffer,
      sizeof(readBuffer));
uint8_t writeToRegister(
    uint8_t   deviceAdr,
    uint8_t   regIndex,
    uint8_t* pData,
    int8_t   len)
Sendet ein Datenpaket an einen Slave mit Registerstruktur.
deviceAdr: Adresse des Slaves [0..127].
regIndex: Registerindex, ab dem geschrieben werden soll.
pData: Zeiger auf das Byte-Feld mit den zu sendenden Daten.
len: Anzahl der zu sendenden Datenbytes.
uint8_t rtc =
   SoftI2c.writeToRegister (

      0x30, 0x02, writeBuffer,
      sizeof(writeBuffer));
uint8_t readFromRegister(
    uint8_t   deviceAdr,
    uint8_t   regIndex,
    uint8_t* pData,
    uint8_t   len,
    bool repeatedStart)
Sendet ein Datenpaket an einen Slave mit Registerstruktur.
deviceAdr: Adresse des Slaves [0..127].
regIndex: Registerindex, ab dem gelesen werden soll.
pData: Zeiger auf das Byte-Feld für die zu lesenden Daten.
len: Anzahl der zu lesenden Datenbytes.
repeatedStart: Zwischen dem Schreiben des Registerindex und dem Lesen der Daten wird anstatt einer Stopp-Bedingung ein Repeated Start gesendet. Dies verhindert, dass ein anderer Master in der Zwischenzeit den Bus übernehmen kann. Die Voreinstellung ist true.
uint8_t rtc =
   SoftI2c.readFromRegister (

      0x30, 0x02, readBuffer,
      sizeof(readBuffer));

Beispiel 1:

#include <UrsArduinoSoftI2c.h>

const uint8_t sda = 8; // SDA pin is pin 8
const uint8_t scl = 9; // SCL pin is pin 9
const uint8_t devAdr = 0x30; // Device Address

void setup() {
   Serial.begin(9600);
   Serial.println("I2C-Test");

   SoftI2c.begin(sda, scl);
}

uint8_t writeBuffer[] = { 15, 16, 17, 18 };
uint8_t readBuffer[4];

void loop() {
   uint8_t rtc = SoftI2c.writePacket(devAdr, writeBuffer, sizeof(writeBuffer));
   if (rtc == sizeof(writeBuffer))
      Serial.println("write ok");
   else
      Serial.println("write error");


   rtc = SoftI2c.readPacket(devAdr, readBuffer, sizeof(readBuffer));
   if (rtc == sizeof(readBuffer))
      Serial.println("read ok");
   else
      Serial.println("read error");

   Serial.println();
   delay(1000);
}

Beispiel 2:

Das zweite Beispiel ist etwas komplexer. Der Arduino kommuniziert mit einem I²C-Slave auf einem ATtiny. Auf dem ATtiny ist das Beispiel aus I²C-Slave: Sklave sucht Meister, Betriebsmodus Register programmiert.

Erweiterungen

Alle Felder und Methoden der Klasse sind als public oder protected deklariert, stehen also Ableitungen der Klasse komplett zur Verfügung.

Bei der Portierung auf andere Prozessor-Familien müssen nur die Methoden sdaLow, sdaHigh, sclLow und  sclHigh angepasst werden. Voraussetzung ist, dass die MCU ein Output-Latch besitzt, dass seinen Wert behält, wenn der Pin-Modus umgeschaltet wird und den Pin auf LOW treibt, wenn er in den Output-Modus geschaltet wird.

Download

Das ZIP-Archiv für Bibliothek UrsArduinoSoftI2c zum Download. Die entpackten Dateien ins Verzeichnis <user>\Documents\Arduino\libraries kopieren (siehe Installing Additional Arduino Libraries).

Das Archiv enthält die Bibliotheksdateien