Forum: Mikrocontroller und Digitale Elektronik I2C richtig implementieren


von Ralf (Gast)


Lesenswert?

Hi,

ich möchte mich mit I2C beschäftigen und frage mich, wie man's richtig 
macht?
MCU ist Cortex-MO/LPC11xx und Cortex-M3/LPC13xx bzw. LPC17xx von Nxp.

Die Frage kam beim Studium des UserManuals auf. Die I2C-Hardware basiert 
wohl darauf, dass der jeweilige Zustand (z.B. StartCondition gesendet, 
Arbitrierung verloren, etc.) über ein Statuswort rückgemeldet wird.
Somit kann (soll) die ganze Sache eigentlich als StateMachine betrachtet 
werden.

Meine Frage ist nun, wie man beispielsweise den I2C-Interrupt so 
schreibt, dass er universell verwendbar ist. Damit meine ich nicht, dass 
er gleich Multimasterbetrieb abdecken können muss, sondern dass man 
einen allgemeinen I2C-Treiber hat, über den alle angeschlossenen Devices 
(z.B. EEPROM, ADC, etc.) abgedeckt werden können.
Wie geht man da am geschicktesten vor? Mir kam eine Lösung in den Sinn, 
die einen Buffer (plus Hilfsvariablen) verwendet.
Hilfsvariablen wären ein Schreib- und ein Lesezähler.

Will man Daten zum Device senden, füllt man den Buffer, setzt den 
Schreibzähler auf die entsprechende Anzahl Daten und schickt das raus.
Dasselbe analog für's Lesen, wobei jeweils einer der beiden Zähler immer 
0 ist.
Will man Daten senden und direkt danach schreiben (Beispiel EEPROM: 
Adresspointer schreiben und dann Daten lesen) ohne den Bus aufzugeben 
(also mit RepeatedStart) setzt man den Lesezähler entsprechend. Die 
Interrupt-Routine erkennt dies und setzt anstatt einer StopCondition 
nach dem Schreiben eine (erneute) StartCondition ab.

Was meint ihr? Zu blauäugig/einfach oder brauchbar? Oder hat noch jemand 
ne Idee wie man's machen kann (sollte)?

Ralf

von Wilhelm F. (Gast)


Lesenswert?

Ja, ich baute auf einem LPC2000 auch mal eine Interruptmaschine zum I2C. 
Damit das Ding dann auch voll im Hintergrund abspielt.

Anfangs erschien mir das gar nicht so einfach, aber auf den 
Internetseiten von Philips, heute NXP, fand ich auch ausgezeichnete 
Papiere mit den States, und Flußdiagramme. Das half mir auf jeden Fall.

Wenn man in der Interruptmaschine alle States berücksichtigt, ist das 
Ding dann natürlich auch universell einsetzbar. Man muß es aber einmal 
machen.

Also: Such beim Hersteller Philips oder NXP mal nach den Papieren zu 
I2C. Philips hatte es wohl auch erfunden. Etwas nachdenken, mußt du dann 
schon noch. Das kann dir niemand abnehmen.

von Olaf (Gast)


Lesenswert?

> Was meint ihr? Zu blauäugig/einfach oder brauchbar?
> Oder hat noch jemand ne Idee wie man's machen kann (sollte)?

Das kann man machen und habe ich auch schonmal so gemacht.

Genauer gesagt habe ich eine Fifo mit structs implementiert wo 
ausserhalb des IRQs die Funktionen ihre Schreib/Lese-Daten, Adressen, 
usw. bereitstellen.
Im IRQ laeuft dann eine Statemachine welche die Fifo abarbeitet.

Grundsaetzlich funktioniert das gut. Vor allem ist es eine Freude sobald 
man auf Fehler stoesst weil man sich von der Statemachine genaue 
Fehlerflags zurueckgeben kann und man so weiss was wann wo und warum 
nicht funktioniert hat.
Allerdings ist es nicht ganz so effizient und cool wie man sich das 
urspruenglich vorstellt. Das liegt daran das man bei vielen Zugriffen 
auf I2C-Bausteinen dann im Hauptprogramm wartet bis der Zugriff gelaufen 
ist weil weil man erst dann etwas anderes machen kann. Mit anderen 
Worten man hat bei I2C-Haeufig den Bedarf blocking zu arbeiten und dann 
lohnt der Aufwand nicht mehr so.

Olaf

von Turbo J (Gast)


Lesenswert?

Man nehme einfach die fertigen Routinen aus der passenden NXP CMSIS:
http://ics.nxp.com/support/documents/?type=software

Zumistest in der Version für LPC17[56]x sind passende Interrupt Handler 
dabei.

von Ralf (Gast)


Lesenswert?

@Wilhelm:
Danke, dann werd ich die NxP-Seite mal genauer durchstöbern. Ich hatte 
dort nur nach der I2C-Spec gesucht. Mal sehen ob ich die von dir 
erwähnten Dokumente finde :)

@Olaf:
Für gewöhnlich schreibe ich meine Software so, dass niemals irgendwo auf 
etwas gewartet wird. Vielleicht bekomme ich das auch hier hin ^^
Das mit den Fehlerflags gefällt mir, ich will schauen ob ich das auch so 
hinbekomme.

@Turbo J:
CMSIS ist so ne Sache, weiss noch nicht was ich davon halten soll, weil 
ich mich etwas schwer tue, CMSIS richtig zu verwenden. Schätze das kommt 
mit der Erfahrung :)
Aber reingucken werd ich, danke.

Ralf

von Wilhelm F. (Gast)


Lesenswert?

Ralf schrieb:

> @Wilhelm:
> Danke, dann werd ich die NxP-Seite mal genauer durchstöbern. Ich hatte
> dort nur nach der I2C-Spec gesucht. Mal sehen ob ich die von dir
> erwähnten Dokumente finde :)

Die Dokumente waren damals auf der Homepage etwas versteckt, bekam sie 
teilweise von Kollegen. I2C-Specs. Wenn nicht NXP, vielleicht findet 
sich ja auf der Philips-HP noch was.

Ach ja, ich erinnere mich jetzt: Das Hinterhältige an der Sache war, daß 
I2C-Beispiele (z.B. vollständige Flußdiagramme) teilweise in 
Datenblättern von Bausteinen mit I2C-Schnittstelle versteckt waren. Wenn 
alle Stricke reißen, frag mich hier noch mal. Ich könnte auf alten 
Datenträgern mal suchen, ob ich noch Information finde.

Die Flußdiagramme mit allen States zum I2C-Bus fand ich für mich sehr 
wichtig, um einen klaren Überblick über die gesamte Funktionsweise zu 
bekommen.

Keil hatte zu den LPC2000 ein I2C-Beispiel, in dem die Arbeit mit der 
Interrupt-Statemachine anhand von 3 oder 4 States im Minimalausbau 
gezeigt wurde. Das half mir. Den Interrupt erweitert man dann einfach um 
die benötigten States, ich glaube es sind 27 insgesamt. Multimaster 
brauchte ich nicht, das blieb bei mir erst mal draußen.

Mit den LPC2000 habe ich lange nichts gemacht, auch nicht mehr auf die 
Keil-Homepage geschaut. Zu den LPC2000 hatte ich damals das Buch mit 
Beispielen von Trevor Martin, das war auch ganz nett.

von Ralf (Gast)


Lesenswert?

Sorry für die späte Antwort.

@Wilhelm:
Ich hab auch noch was interessantes gefunden, für die Leute, die sich 
auch damit beschäftigen möchten:
http://www.i2c-bus.org

Momentan bin ich dran, den Ablauf wie er im LPC11xx UserManual 
dargestellt ist in Software umzusetzen. Vielleicht kann ich heut abend 
schon einen ersten Probelauf machen.
Als Versuchskaninchen hab ich je ein 24LC02 und ein 24LC64 EEPROM an den 
Bus gepappt.
Erster Stolperstein dürfte die Frage sein, wie man bei der 
Implementierung gemäß UserManual das Polling durchführt um zu erkennen, 
ob das EEPROM bereit für neue Kommandos ist, aber ich schätze dafür muss 
man dann den Treiber nach Bedarf erweitern.

Ralf

von Wilhelm F. (Gast)


Lesenswert?

Ralf schrieb:

> Erster Stolperstein dürfte die Frage sein, wie man bei der
> Implementierung gemäß UserManual das Polling durchführt um zu erkennen,
> ob das EEPROM bereit für neue Kommandos ist, aber ich schätze dafür muss
> man dann den Treiber nach Bedarf erweitern.

So ein I2C-EEPROM eignet sich auch sehr gut für Tests. Verzeihung, es 
ist lange her, daß ich bei LPC2000 den I2C-Bus programmierte. Ich kann 
da nur aus lang zurück liegender Erinnerung sprechen. Aber das 
Acknowledge-Polling war wohl ein ganz normaler Lesezugriff vom Master 
auf den Slave, wie wenn man Daten liest.

Was ich auf meinem Rechner noch fand:

i2c_specs.pdf

I2C_BUS_SPECIFICATION_2.pdf
Hier sind Graphen der Abhängigkeiten der Buskapazität zu den 
Pullup-Widerständen drin.

8xC5x2_OV.pdf
Hier sind die I2C-States vollständig beschrieben, und ein paar Diagramme 
zu den Betriebsmodes (Master Transmitter, Master Receiver, Slave 
Transmitter, Slave Receiver).

Die sollten so auch bei Philips oder NXP auffindbar sein.

Die Datei mit dem vollständigen Flußdiagramm zu allen I2C-States fand 
ich noch nicht. Das ist aber die, die eigentlich sehr wichtig ist. Es 
ist etwas mühsam, 10 alte CDs mit 5GB Daten noch mal zu durchstöbern.

von Ralf (Gast)


Lesenswert?

Hi Wilhelm,

mach dir keine großen Umstände, ich werd dank deiner Beschreibung die 
Dateien schon finden.
Du hast recht, deswegen hab ich für den Anfang die EEPROMs gewählt. Der 
Stolperstein ist übrigens nicht wie erwartet das Polling, sondern meine 
Fehlinterpretation des LPC UserManuals. Ich hab brav das 
Softwarebeispiel umgesetzt ohne im ersten Moment zu kapieren, dass 
dieses nicht von einem Zustand zu allen möglichen anderen springt, 
sondern nur ein paar Fälle abdeckt. Das heisst für mich erstmal, der 
Stolperstein ist das Treiberdesign, damit ich soweit flexibel bin, dass 
diverse I2C-Peripherie angebunden werden, ohne nochmal im eigentlichen 
I2C-Treiber rumwerkeln zu müssen :)
Aber das guck ich mir morgen an,

gute Nacht.

Ralf

von Ralf (Gast)


Lesenswert?

So, hab mich nun noch etwas tiefer durchgewühlt durch die Materie, vor 
allem auch durch die Tabellen aus dem LPC11xx UserManual, in denen die 
einzelnen Zustände und die jeweils möglichen Folgezustände beschrieben 
sind.

Irgendwie will mir da was noch nicht ganz in den Kopf... Wenn ich wie 
oben eigentlich angedacht einen Buffer verwende, kann ich ja 
beispielsweise anhand eines Zählers bestimmen, wann der Master beim 
Empfangen ein NACK generieren soll (üblicherweise beim letzten 
Datenbyte).

Beim Senden generiert der Slave das Ack-Flag. So, nehmen wir jetzt an, 
ich habe am Bus ein Device, bei dem der Transfer abgebrochen und neu 
gestartet werden soll, wenn das Device unerwartet ein NACK generiert. 
Zusätzlich hab ich ein Device, bei dem ich den Transfer abbrechen, aber 
nicht neu starten will.
Soooo, wie macht denn das nun? Das einzige was mir so spontan 
eingefallen ist, wäre dass nicht die Interrupt-Routine (alleine) 
entscheidet was passiert, sondern auch der Buffer: Jedes Datenbyte (und 
das Adressbyte) bekommen zusätzlich noch zwei Statusbytes die 
entscheiden bei welchem I2C-Status es im ACK/NACK-Fall weitergeht.

Zu (kompliziert) um die Ecke gedacht oder sinnvoll?

Ralf

von holger (Gast)


Lesenswert?

>Beim Senden generiert der Slave das Ack-Flag. So, nehmen wir jetzt an,
>ich habe am Bus ein Device, bei dem der Transfer abgebrochen und neu
>gestartet werden soll, wenn das Device unerwartet ein NACK generiert.
>Zusätzlich hab ich ein Device, bei dem ich den Transfer abbrechen, aber
>nicht neu starten will.

Den Fall gibt es nicht bei I2C. Da geht alles der Reihe nach.

von Ralf (Gast)


Lesenswert?

@Holger:
> Den Fall gibt es nicht bei I2C. Da geht alles der Reihe nach.
Du meinst es gibt das unerwartete NACK nicht?

Ralf

von holger (Gast)


Lesenswert?

>> Den Fall gibt es nicht bei I2C. Da geht alles der Reihe nach.
>Du meinst es gibt das unerwartete NACK nicht?

Doch, aber es gibt kein zweites Device das auf einen
Transfer wartet. Das geht bei I2C nicht.

von Ralf (Gast)


Lesenswert?

> Doch, aber es gibt kein zweites Device das auf einen
> Transfer wartet. Das geht bei I2C nicht.
So meinte ich das auch nicht. Ich meinte, dass bei den beiden Devices 
unterschiedlich reagiert werden soll, wenn ein NACK auftritt.
Beim einen Device möchte man den Transfer wiederholen, beim anderen 
nicht.

Ralf

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.