I2C auf Intel XScale PXA255
-
Hi
Ich arbeite gerade mit einem Gumstix Board mit einem Intel XScale PXA 255
Prozessor. Ich verwende einen selbt geschriebenen echtzeitfähigen Mikrokernel und muss deshalb alle Treiber selber schreiben. Ich habe einen Sensor, der den Stromverbrauch misst und dieser ist am I2C Bus angeschlossen.Ich hab leider keine Erfahrung mit dem I2C Bus und die dev-manual von Intel PXA ist war recht ausführlich, aber einige Fragen bleiben offen. Da ich (wie gesagt) keine Erfahrungen mit I2C hab, bin ich nicht sicher, wie ich an der Sache rangehen soll.
In der Doku steht, dass der I2C Controller in 4 Modi arbeiten kann (Master-Transmit, Master-Receive, usw). Was ich noch nicht verstehe: werden die Modi vom Treiber gewechselt oder macht der Controller das von alleine? Was wäre effizienter/besser, polling oder interrupts? ich will an sich nur Sensoren anschließen und diese auslesen/ansteuern, heißt es also, dass der Controler als Master arbeiten soll?
Ich hoffe, dass jemad, der mit I2C ein bisschen Erfahrung hat, mir vielleicht paar Tipps geben kann, worauf ich achten muss oder einen Link postet, wo ich Sachen nachlesen kann.
Danke.
-
Hallo supertux,
ich würde mir zuerst mal genau anschauen welche Bausteine am I2C Bus hängen. Jeder Baustein (z.B. Flash EEPROM, RTC oder IO-Explander) muss ganz speziell angesteuert werden.
Es gibt immer wieder kehrende Muster wie z.B. das "Start" Bit, gefolgt von der 7 bitigen Device Adresse, gefolgt vom "lesen/schreiben" Bit. Danach beginnen aber meist die Eigenheiten des jeweiligen Bausteins.
Jetzt kommts drauf wie das Innenleben des I2C Bus Master Bausteins ausschaut (also nicht der I2C Baustein den du eigentlich ansprechen willst sondern der auf deinem Gumstix Board den Bus Master darstellt). Welche Register sind vorhanden, gibt ein Empfangs- und Sende FIFO usw.
Normalerweise müsste in der Memory Map vom Gumstix Board was über den I2C Controller zu finden sein. Diesen musst du dann über seine internen Register entsprechend ansteuern.Falls das Datenblatt keine weiteren Infos über den darauf enthaltenen I2C Baustein bereithält, musst einfach mal den entsprechenden Application Ing. der Firma anrufen.
Vielleicht ist der darauf enthaltene Busmaster ja auch nur ganz was primitives wie z.B. ein 2bit Register (I2C_CLK, I2C_DATA), mit dem man dann jedes einzelne Bit rausnudeln muss.
-
Hi
danke für die Kommentare. Also, Infos über Registers usw. ist vorhanden (die dev-manual vom Intel PXA), sie ist aber schon recht umfangreich und geht wenig auf "wie man letztendlich den I2C Controller verwendet" sondern "was für Register gibt es, wo sie gemappt werden, usw" und wenn du noch nie mit dem I2C Bus bzw. Protokoll zu tun hattest, kannst du nicht viel damit anfangen, außer rumprobieren (wofür ich eigentlich keine Zeit hat). Denn ich möchte ein kleines Framework dafür schreiben, so dass man beliege I2C Sensoren anschließen kann ohne am Treiber Framework rumzubasteln (den gasamtem Funktionsumfang von I2C muss ich jedoch nicht unterstützen).
-
supertux schrieb:
In der Doku steht, dass der I2C Controller in 4 Modi arbeiten kann (Master-Transmit, Master-Receive, usw). Was ich noch nicht verstehe: werden die Modi vom Treiber gewechselt oder macht der Controller das von alleine?
das müsste in der doku stehen. normalerweise sorgt so'n iic-peripheral nur das grundlegende framing, also start/stop-conditions setzen, bits raus- und reinschieben usw, damit man sich mit möglichst vielen iic-slaves unterhalten kann. alles andere muss der treiber machen.
supertux schrieb:
Was wäre effizienter/besser, polling oder interrupts?
interrupts sind meistens besser, weil du ja 'nen taskswitching-kernel einsetzt und weil iic ein ziemlich langsamer bus ist.
supertux schrieb:
ich will an sich nur Sensoren anschließen und diese auslesen/ansteuern, heißt es also, dass der Controler als Master arbeiten soll?
ja, dein controller ist immer der, der die taktimpulse erzeugt.
ach ja, und vergiss nicht die pull-ups. iic ist 'open drain'. die teilnehmer ziehen den bus nur runter. pull-ups sorgen für den high-pegel.
supertux schrieb:
und wenn du noch nie mit dem I2C Bus bzw. Protokoll zu tun hattest, kannst du nicht viel damit anfangen, außer rumprobieren
http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf
-
erstaml danke für due Antwort
~fricky schrieb:
supertux schrieb:
Was wäre effizienter/besser, polling oder interrupts?
interrupts sind meistens besser, weil du ja 'nen taskswitching-kernel einsetzt und weil iic ein ziemlich langsamer bus ist.
so dachte ich es mir auch, aber in meinen Echtezeitmikrokernel möchte ich so wenig wie möglich im Kernel Modus wechseln (möchte Anzahl Context-Switches so gering wie möglich halten) und weiß nicht, wie viele Interrupts der I2C abgeben könnte. Ich werde das mal experimentell bestimmen.
-
supertux schrieb:
so dachte ich es mir auch, aber in meinen Echtezeitmikrokernel möchte ich so wenig wie möglich im Kernel Modus wechseln (möchte Anzahl Context-Switches so gering wie möglich halten)
naja, ein hardware-interrupt ist ja kein richtiger context-switch. darum muss sich dein scheduler nicht kümmern, das regelt die CPU alles selber. aber ich weiss ja nicht, wie dein system aufgebaut ist.
supertux schrieb:
und weiß nicht, wie viele Interrupts der I2C abgeben könnte. Ich werde das mal experimentell bestimmen.
oft passiert das bei jedem empfangenen byte und wenn das senderegister leer ist. wenn dein controller intern für iic FIFOs hat, dann natürlich seltener (z.b. wenn der empfangs-FIFO halb voll ist bzw. nach einem timeout und wenn der sende-FIFO leer ist). dazu kommen noch interrupts wie 'arbitration lost', für slave-modi noch ein paar mehr. aber die kannste sicherlich alle ausmaskieren. abhängig von FIFO-grösse, taktfrequenz, etc. kann man ausrechnen, wie viele interrupts pro sekunde bei vollast maximal auftreten können. du musst nun mal das user-manual des controllers durchackern. da kommst du nicht drum herum.
-
~fricky schrieb:
supertux schrieb:
so dachte ich es mir auch, aber in meinen Echtezeitmikrokernel möchte ich so wenig wie möglich im Kernel Modus wechseln (möchte Anzahl Context-Switches so gering wie möglich halten)
naja, ein hardware-interrupt ist ja kein richtiger context-switch. darum muss sich dein scheduler nicht kümmern, das regelt die CPU alles selber. aber ich weiss ja nicht, wie dein system aufgebaut ist.
Der Scheduler nicht, aber die Kontexte der ausführende Tasks muss dennoch gespeichert werden. Der Zeitgeber (für periodeische Releases von Tasks) wird mit dem OSTIMER + Match Register realisiert. Ein Match wird nur durch Interrupt bekannt gemacht, deswegen muss ich bei IRQs den gesamten Context der Tasks sepeichern (ein Context-Swicth im Prinzip, auch wenn die ausführende Tasks nicht preemted wird). 99% aller behandelten IRQs sind für Tasks Releases (was wegen EDF Scheduler zur Preemtion führen kann)
aus diesem Grund möchte ich keine weitere "unnötige" IRQ Behandlungen machen.
-
wieso stört eine zusätzliche interruptquelle? der scheduler hat doch seinen eigenen timer interrupt, oder nicht? auf einem ARM-system, das nur 'nen primitiven interrupt-controller hat (einer, der nicht vektorisiert), hast du zwar für alle IRQs eine globale ISR (ebenso für FIQs), aber dann kannstes doch so machen:
__interrupt void global_isr(void) { int src = ....; // Interruptquelle ausfindig machen switch (src) { case RTOS_TIMER: Schedule(); // <-- RTOS tick break; case IIC: HandleIIC(); // <--- das IIC peripheral will was von uns break; case ...; // noch mehr interruptquellen .... default: HandleSpuriousInterrupt(); // <--- sollte nicht auftreten break; } }
^^aber falls ich dich falsch verstanden habe, betrachte das posting bitte als hinfällig
-
~fricky schrieb:
der scheduler hat doch seinen eigenen timer interrupt,
ja, aber damit hat es nicht zu tun: mein IRQ handler sieht so aus:
__arm_core_irq_handler: sub lr, lr, #4 stmfd sp!, {r0-r12, lr} mov r0, sp bl cpu_save_context bl arm_core_irq_handler mov r0, sp bl cpu_restore_context ldmfd sp!, {r0-r12, pc}^
und
cpu_save_context
undcpu_restore_context
sind für Context Switches zuständig. Da 99% aller IRQ Scheduler Entscheidungen sind, war es deswgen am einfachsten dort den Kontext zu speichern.
-
^^na, das lässt sich ja leicht abändern, so dass andere interrupts, ausser scheduler-interrupts, keine save- und restore-contexts bewirken.
-
~fricky schrieb:
^^na, das lässt sich ja leicht abändern, so dass andere interrupts, ausser scheduler-interrupts, keine save- und restore-contexts bewirken.
theoretisch schon, aber das möchte ich jetzt nicht, wie sagt man so schön "never touch a running system"? Ist mir zu heiß und hab Angst mich zu verbrennen
-
supertux schrieb:
~fricky schrieb:
^^na, das lässt sich ja leicht abändern, so dass andere interrupts, ausser scheduler-interrupts, keine save- und restore-contexts bewirken.
theoretisch schon, aber das möchte ich jetzt nicht, wie sagt man so schön "never touch a running system"?
auch wenn's offensichtlich eine unschönheit enthält? mich würde es jedenfalls stören, wenn diese context-switch funktionen, bei jedem interrupt, egal wer ihn auslöste, aufgerufen werden. naja, aber du wirst schon wissen, was du tust.