I²C-Master als VHDL Code

I²C-Master als VHDL Code

Erstellt von Scott Larson, Senior Application Engineer @ Digi-Key

zuletzt geändert am 18. Dezember 2020

- Code herunterladen - Eigenschaften - Einführung - Hintergrund - Theorie der Funktionsweise - Port-Beschreibungen - Einstellen der seriellen Taktfrequenz - Transaktionen - Beispiel-Transaktion - Beispiel Anwenderlogik zur Steuerung des I²C-Masters - Fehler quittieren - Taktdehnung - Rücksetzen - Fazit - Zusätzliche Informationen - Verwandte Themen - Kontakt

Code herunterladen

Version 2.2: i2c_master.vhd
Kleiner SDA-Fehler am Ende der Transaktion behoben (eingeführt in Version 2.1)

Version 2.1: i2c_master_v2_1.vhd
Gated Clock durch Clock Enable ersetzt
Timing von SCL während Start- und Stop-Bedingungen angepaßt (Dank an Steffen Mauch, Student an der Universität Mannheim, Deutschland für den Vorschlag dieser Verbesserungen).

Version 2.0: i2c_master_v2_0.vhd
Es wurde die Möglichkeit hinzugefügt, sich mit verschiedenen Slaves in der gleichen Transaktion zu verbinden
Korrigierter ack_error-Bug, bei dem ack_error im Fehlerfall ‘Z’ statt ‘1’ wurde
Das Timing, wann das ack_error-Signal gelöscht wird, wurde korrigiert

Version 1.0: i2c_master_v1_0.vhd
Erste öffentliche Veröffentlichung

Merkmale

  • VHDL-Quellcode eines Inter-Integrated-Circuit (I²C, I2C oder IIC) Master-Bausteins
  • Entspricht der NXP UM10204 I²C-Bus-Spezifikation für Single-Master-Busse
  • Benutzerdefinierbarer Systemtakt
  • Benutzerdefinierbare serielle I²C-Taktfrequenz
  • Erzeugt Start-, Stop-, Repeated Start- und Acknowledge-Bedingungen
  • Verwendet 7-Bit-Slave-Adressierung
  • Kompatibel mit Clock-Stretching durch Slaves
  • Nicht empfohlen für Multi-Master-Busse (keine Arbitrierung oder Synchronisation)
  • Benachrichtigt Anwenderlogik über Slave-Acknowledge-Fehler

Einführung

Hier wird eine I²C-Master-Komponente für Single-Master-Busse beschrieben, die in VHDL für den Einsatz in CPLDs und FPGAs geschrieben wurde. Der Baustein liest von und schreibt in die Anwenderlogik über eine parallele Schnittstelle. Sie wurde mit Quartus II, Version 11.1, entwickelt. Der Ressourcenbedarf hängt von der Implementierung ab. Abbildung 1 zeigt ein typisches Beispiel für die Integration des I²C-Masters in ein System. Ein Design, das diesen I²C-Master zur Erstellung einer SPI-zu-I²C-Brücke verwendet, ist hier verfügbar.

image

Bild 1. Beispiel-Implementierung

Hintergrund

Der I²C-Bus ist eine 2-Draht, halb-duplex Datenverbindung, die von Philips (jetzt NXP) erfunden und spezifiziert wurde. Die beiden Leitungen des I²C-Busses, SDA und SCL, sind bidirektional und Open-Drain, die durch Widerstände hochgezogen werden. SCL ist eine serielle Taktleitung, und SDA ist eine serielle Datenleitung. Geräte auf dem Bus ziehen eine Leitung auf Masse, um eine logische Null zu senden, und lassen eine Leitung frei (lassen sie schwebend), um eine logische Eins zu senden.

Weitere Informationen finden Sie in der I²C-Spezifikation, die unten im Abschnitt “Zusätzliche Informationen / Additional Information” beigefügt ist. Sie erklärt das Protokoll im Detail, die elektrischen Spezifikationen, die Dimensionierung der Pull-Up-Widerstände usw.

Theorie der Funktionsweise

Der I²C-Master verwendet die in Abbildung 2 dargestellte Zustandsmaschine, um das I²C-Bus-Protokoll zu implementieren. Beim Einschalten geht der Baustein sofort in den Bereitschaftszustand. Er wartet in diesem Zustand, bis das ena-Signal einen Befehl einrastet. Der start-Zustand erzeugt die Startbedingung auf dem I²C-Bus und der command-Zustand kommuniziert die Adresse und den rw-Befehl auf den Bus. Der Zustand slv_ack1 erfaßt und verifiziert dann die Quittung des Slaves. Abhängig vom rw-Befehl schreibt die Komponente dann entweder Daten in den Slave (Zustand wr) oder empfängt Daten vom Slave (Zustand rd). Nach Abschluß des Vorgangs erfaßt und verifiziert der Master die Antwort des Slaves (Zustand slv_ack2), wenn er schreibt, oder gibt seine eigene Antwort (Zustand mstr_ack), wenn er liest. Wenn das ena-Signal einen weiteren Befehl einrastet, fährt der Master sofort mit einem weiteren Schreib- (wr-Zustand) oder Lesebefehl (rd-Zustand) fort, wenn der Befehl mit dem vorherigen Befehl identisch ist. Wenn es sich um einen anderen Befehl handelt (z. B. ein Lesen nach einem Schreiben oder ein Schreiben nach einem Lesen oder eine neue Slave-Adresse), dann gibt der Master einen wiederholten Start (start state) gemäß der I²C-Spezifikation aus. Sobald der Master einen Lese- oder Schreibvorgang abgeschlossen hat und das ena-Signal nicht in einem neuen Befehl einrastet, erzeugt der Master die Stop-Bedingung (Stop-Status) und kehrt in den Ready-Status zurück.

Abbildung 2. I²C-Master-Zustandsmaschine

Das Timing für die Zustandsmaschine ergibt sich aus den GENERIC-Parametern input_clk und bus_clk, die im Abschnitt “Einstellen der seriellen Taktfrequenz” weiter unten beschrieben sind. Ein Zähler generiert den Datentakt, mit dem die Zustandsmaschine läuft, ebenso wie scl selbst.

Port-Beschreibungen

Tabelle 1 beschreibt die Ports des I²C-Masters.

Port Width Mode Data Type Interface Description
clk 1 in standard logic user logic System clock.
reset_n 1 in standard logic user logic Asynchronous active low reset.
ena 1 in standard logic user logic 0: no transaction is initiated.
1: latches in addr, rw and data_wr to initiate a transaction. If ena is high at the conclusion of a transaction (i.e. when busy goes low) then a new address, read/write command, and data are latched in to continue the transaction.
addr 7 in standard logic vector user logic Address of target slave.
rw 1 in standard logic user logic 0: write command.
1: read command.
data_wr 8 in standard logic vector user logic Data to transmit if rw = 0 (write).
data_rd 8 out standard logic vector user logic Data received if rw = 1 (read).
busy 1 out standard logic user logic 0: I²C master is idle and last read data is available on data_rd.
1: command has been latched in and transaction is in progress.
ack_error 1 buffer standard logic user logic 0: no acknowledge errors.
1: at least one acknowledge error occurred during the transaction. ack_error clears itself at the beginning of each transaction.
sda 1 inout standard logic slave device(s) Serial data line of I²C bus.
scl 1 inout standard logic slave device(s) Serial clock line of I²C bus.

Einstellen der Geschwindigkeit des seriellen Taktes

Die Komponente leitet den seriellen Takt scl von zwei in der ENTITY deklarierten GENERIC-Parametern ab, input_clk und bus_clk. Der Parameter input_clk muß auf die Eingangssystemtaktfrequenz clk in Hz eingestellt werden. Die Standardeinstellung im Beispielcode ist 50 MHz (die Frequenz, mit der die Komponente simuliert und getestet wurde). Der Parameter bus_clk muss auf die gewünschte Frequenz des seriellen Taktes scl eingestellt werden. Die Voreinstellung im Beispielcode ist 400 kHz, was der Fast-Mode-Bitrate in der I²C-Spezifikation entspricht.

Transaktionen

Ein niedriger Logikpegel am Busy-Ausgang zeigt an, daß die Komponente bereit ist, einen Befehl zu akzeptieren. Um eine Transaktion zu initiieren, legt die Anwenderlogik die gewünschte Slave-Adresse, den rw-Befehl und die Schreibdaten an die Ports addr, rw bzw. data_wr an und aktiviert das ena-Signal. Der Befehl wird nicht mit dem folgenden Systemtakt clk eingetaktet. Vielmehr generiert der Baustein bereits intern das I²C-Timing und taktet den Befehl basierend auf diesem Timing ein. Daher sollte die Anwenderlogik auf die Bestätigung des Busy-Signals warten, um zu erkennen, wann diese Eingänge in den I²C-Master eingeklinkt werden.

Der I²C-Master führt dann den Befehl aus. Nach Abschluß des Befehls wird das Busy-Signal deaktiviert, um anzuzeigen, daß alle gelesenen Daten am data_rd-Port verfügbar sind und alle Fehler am ack_error-Port gekennzeichnet werden.

Während einer Transaktion können mehrere Lese- und Schreibvorgänge (und beliebige Kombinationen davon) ausgeführt werden. Wenn das ena-Signal am Ende des aktuellen Befehls anliegt, speichert der I²C-Master die neuen Werte von addr, rw und data_wr und führt den neuen Befehl sofort aus. Das Busy-Signal wird noch für einen scl-Zyklus deassert, um anzuzeigen, daß alle Lesedaten auf data_rd verfügbar sind, und es wird erneut deassert, um anzuzeigen, daß der neue Befehl eingeklinkt ist und ausgeführt wird.

Beispieltransaktion

Abbildung 3 zeigt das Timing-Diagramm für eine typische Transaktion. Die Benutzerlogik präsentiert die Adresse “1010101”, den rw-Befehl ‘0’, der ein Schreiben anzeigt, und die Schreibdaten “10011001”. Sie setzt das ena-Signal ein, um diese Werte zu speichern. Sobald das Busy-Signal anliegt, gibt die Anwenderlogik einen neuen Befehl aus. Die Adresse bleibt “1010101”, aber der folgende Befehl ist ein Lesebefehl (rw = ‘1’). Ena bleibt durchgeschaltet. Wenn der I²C-Master den ersten Befehl beendet hat, deassert er das Busy-Signal, um dessen Beendigung anzuzeigen. Da ena aktiviert ist, speichert der I²C-Master den neuen Befehl und setzt das Busy-Signal wieder auf. Sobald das Busy-Signal wieder anliegt, erkennt die Anwenderlogik, daß der zweite Befehl ausgeführt wird und deassert das ena-Signal, um die auf diesen Befehl folgende Transaktion zu beenden. Der I²C-Master beendet die Ausführung des Lesebefehls, gibt die gelesenen Daten (“11001100”) auf dem data_rd-Port aus und deassert wieder das Busy-Signal.

image

Abbildung 3. Typisches Transaktions-Timing-Diagramm

Um eine Transaktion mit einem neuen Befehl fortzusetzen, müssen das ena-Signal und die neuen Eingänge spätestens mit dem letzten Datenbit des aktuellen Befehls anliegen. Es wird daher empfohlen, den neuen Befehl zu geben, sobald das Busy-Signal anzeigt, daß der aktuelle Befehl eingeklinkt ist. Das ena-Signal kann einfach so lange anliegen, bis der letzte Befehl der Transaktion eingelastet ist.

Beispiel für Anwenderlogik zur Steuerung des I²C-Masters

Der folgende Ausschnitt aus der Anwenderlogik demonstriert eine einfache Technik zur Verwaltung von Transaktionen mit mehreren Lese- und Schreibvorgängen. Diese Beispielimplementierung zählt die Busy-Signalübergänge, um zum richtigen Zeitpunkt Befehle an den I²C-Master zu geben. Der Zustand “get_data” tut alles, was notwendig ist, um einen Schreib-, einen Lese-, einen zweiten Schreib- und einen zweiten Lesebefehl über den I²C-Bus in einer einzigen Transaktion zu senden, wie es z.B. beim Abrufen von Daten aus mehreren Registern in einem Slave-Gerät der Fall sein könnte.

WHEN get_data => --state for conducting this transaction

busy_prev <= i2c_busy; --capture the value of the previous i2c busy signal

IF(busy_prev = ‘0’ AND i2c_busy = ‘1’) THEN --i2c busy just went high

busy_cnt := busy_cnt + 1; --counts the times busy has gone from low to high during transaction

END IF;

CASE busy_cnt IS --busy_cnt keeps track of which command we are on

WHEN 0 => --no command latched in yet

i2c_ena <= ‘1’; --initiate the transaction

i2c_addr <= slave_addr; --set the address of the slave

i2c_rw <= ‘0’; --command 1 is a write

i2c_data_wr <= data_to_write; --data to be written

WHEN 1 => --1st busy high: command 1 latched, okay to issue command 2

i2c_rw <= ‘1’; --command 2 is a read (addr stays the same)

WHEN 2 => --2nd busy high: command 2 latched, okay to issue command 3

i2c_rw <= ‘0’; --command 3 is a write

i2c_data_wr <= new_data_to_write; --data to be written

IF(i2c_busy = ‘0’) THEN --indicates data read in command 2 is ready

data(15 DOWNTO 8) <= i2c_data_rd; --retrieve data from command 2

END IF;

WHEN 3 => --3rd busy high: command 3 latched, okay to issue command 4

i2c_rw <= ‘1’; --command 4 is read (addr stays the same)

WHEN 4 => --4th busy high: command 4 latched, ready to stop

i2c_ena <= ‘0’; --deassert enable to stop transaction after command 4

IF(i2c_busy = ‘0’) THEN --indicates data read in command 4 is ready

data(7 DOWNTO 0) <= i2c_data_rd; --retrieve data from command 4

busy_cnt := 0; --reset busy_cnt for next transaction

state <= home; --transaction complete, go to next state in design

END IF;

WHEN OTHERS => NULL;

END CASE;

Fehler quittieren

Nach jedem übertragenen Byte muß die Empfangsseite des I²C-Busses ein Acknowledge- oder No-Acknowledge-Signal ausgeben. Wenn der I²C-Master eine falsche Antwort vom Slave erhält, benachrichtigt er die Anwenderlogik, indem er den Fehler auf dem ack_error-Port kennzeichnet. Der I²C-Master versucht nicht automatisch, die Informationen erneut zu senden, sodaß die Anwenderlogik entscheiden muss, ob sie den Befehl erneut ausgibt und/oder zusätzliche Maßnahmen ergreift oder nicht. Der ack_error-Port wird gelöscht, sobald die nächste Transaktion beginnt.

Taktdehnung

Abschnitt 3.1.9 der I²C-Spezifikation definiert eine optionale Funktion, bei der ein Slave scl low halten kann, um die Transaktion im Wesentlichen zu pausieren. Einige Slaves sind dafür ausgelegt, dies zu tun, wenn sie z.B. mehr Zeit benötigen, um empfangene Daten zu speichern, bevor sie fortfahren. Dieser I²C-Master-Baustein ist mit Slaves kompatibel, die diese Funktion implementieren. Er erfordert keine Aktion durch die Benutzerlogik, die den I²C-Master steuert.

Reset

Der reset_n-Eingangsport muss einen logischen High-Pegel haben, damit die I²C-Master-Komponente funktioniert. Ein niedriger Logikpegel an diesem Port setzt die Komponente asynchron zurück. Während des Resets hält der Baustein den Busy-Port auf High, um anzuzeigen, daß der I²C-Master nicht verfügbar ist. Die Ports scl und sda nehmen einen hochohmigen Zustand an und die Ausgangsports data_rd und ack_error werden gelöscht. Nach dem Reset wird der Busy-Port deassert, wenn der I²C-Master wieder zur Kommunikation bereit ist.

Fazit

Dieser I²C-Master ist ein programmierbarer Logikbaustein, der die Kommunikation mit I²C-Slaves über ein einfaches paralleles Interface ermöglicht. Er hält sich an die Philips/NXP I²C-Spezifikation in Bezug auf Single-Master-Busse und verfügt zusätzlich über die optionale Funktion des Clock-Stretching.

Weitere Informationen

User Manual UM10204, I²C-Bus-Spezifikation und Benutzerhandbuch, NXP Semiconductors N.V.: UM10204, I2C-bus specification and user manual, NXP Semiconductors N.V.

Verwandte Themen

SPI to I²C Bridge (VHDL) – Dieses Design verwendet den oben beschriebenen I²C-Master zur Implementierung einer SPI-zu-I²C-Brücke.

Temperature Sensor TCN75A Pmod Controller (VHDL) – Dieses Design verwendet den oben beschriebenen I²C-Master, um einen Microchip TCN75AVUA713 Temperatursensor zu konfigurieren und Daten von diesem zu sammeln.

Color Sensor Pmod Controller (VHDL) – Dieses Design verwendet den oben beschriebenen I²C-Master, um einen AMS TCS34725FN Farblicht-Digital-Wandler zu konfigurieren und Daten von diesem zu sammeln.

Compass Pmod Controller (VHDL) – Dieser Entwurf verwendet den oben beschriebenen I²C-Master zur Konfiguration und Datenerfassung eines Memsic MMC34160PJ Magnetometers.

ADC AD7991 Pmod Controller (VHDL) – Dieses Design verwendet den oben beschriebenen I²C-Master, um Daten von einem Analog Devices AD7991YRJZ-0500RL7 4-Kanal, 12-Bit Analog-Digital-Wandler zu erfassen.

Humidity and Temperature Sensor Pmod Controller (VHDL) – Dieses Design verwendet den obigen I²C-Master, um Temperatur- und Feuchtigkeitsdaten von einem Texas Instruments HDC1080DMBR Hygrometer zu konfigurieren und abzurufen.

Real-Time Clock MCP79410 Pmod Controller (VHDL) – Dieses Design verwendet den obigen I²C-Master zur Steuerung einer Echtzeituhr/eines Echtzeitkalenders vom Typ Microchip MCP79410T-I/SN.

Kontakt

Kommentare, Feedback und Fragen können direkt an eewiki@digikey.com gesendet werden.