Hallo zusammen, ich habe lange Zeit I2C im Polling Mode benutzt, also bei einem Read oder Write war nach Aufruf der Funktion der Datentransfer abgearbeitet. Nun möchte ich den I2C-Bus im Interrupt Mode verwenden. Das ganze klappt auch schon, ich habe aber noch eine grundsätzliche Frage. Ich Starte mit dem Aufruf einer Read oder Write Funktion nur die I2C Statemachine, der eigentliche Datentransfer wird im Hintergrund ausgeführt. Nach Beendung wird eine Callback Funktion aufgerufen. Im Moment starte ich zum Beispiel ein Read und warte danach 20ms, damit die Statemachine abgearbeit werden kann. Das ist aber nicht Sinn der Sache, wie bekomme ich meine I2C mit der Callback Routine verheiratet?
Uwe schrieb: > Im Moment starte ich zum Beispiel ein Read und warte danach 20ms, damit > die Statemachine abgearbeit werden kann. Das ist aber nicht Sinn der > Sache, wie bekomme ich meine I2C mit der Callback Routine verheiratet? Indem du deine Statmachine ganz zum Schluss zb die Callback Routine aufrufen lässt oder aber wenn du keinen Funktionaufruf aus der ISR heraus haben willst, dann setzt die Statemachine ganz zum Schluss eine globale Flag-Variable auf 'Übertragung FERTIG' und du pollst in der Hauptschleife auf diese Variable. Jetzt magst du dich fragen, wozu dann überhaupt Interrupt, wenn ich sowieso pollen muss? Du hast eine bessere Organisationsform, bei der du einfacher andere Dinge erledigen kannst, während die I2C Übertragung im Hintergrund abgewickelt wird. Der Trick bei der Programmierung, bei der ein µC scheinbar viele Dinge mehr oder weniger gleichzeitig erledigen kann, besteht darin, dass die einzelnen Tätigkeiten auf kleine Zeithappen heruntergebrochen werden und nirgends viel Zeit in einem Rutsch drauf geht. So auch hier.
:
Bearbeitet durch User
Zum Glück gibts ja auf diesem Planeten nur eine einzige Sorte Microcontroller und der Code den Du bereits geschrieben hast kann sich auch jeder schon denken. Ansonsten wüsste nämlich jetzt niemand womit Du Dich da überhaupt beschäftigst und keiner könnte irgendwelche Antworten geben.
@Karl-Heinz, gut hab ich soweit verstanden. Aber wenn ich z.b. mehrere Write Aufrufe habe z.B.: I2C_Write(Address_A, Value_1); I2C_Write(Address_B, Value_2); I2C_Write(Address_C, Value_3); da ist mir noch nicht klar wie ich die Wartezeiten hinbekomme. @Bernd Es geht mir doch nur um das Prinzip.
>Aber wenn ich z.b. mehrere Write Aufrufe >habe z.B.: > >I2C_Write(Address_A, Value_1); >I2C_Write(Address_B, Value_2); >I2C_Write(Address_C, Value_3); > >da ist mir noch nicht klar wie ich die Wartezeiten hinbekomme. Indem du jedem Write zwei States gibst. Einen wo der gestartet wird und einen wo das Ende festgestellt wird.
Uwe schrieb: > Es geht mir doch nur um das Prinzip. Wie auch immer die Hardware aussehen mag, ich bin sicher sie bietet die Möglichkeit einen Interrupt auszulösen wenn ein Byte samt ack/nack komplett geschrieben/gelesen worden ist. In dem Interrupt dann entscheidest Du welcher Zustand als nächstes folgen soll und was dafür nun zu tun ist.
Uwe schrieb: > Im Moment starte ich zum Beispiel ein Read und warte danach 20ms, damit > die Statemachine abgearbeit werden kann. Das ist aber nicht Sinn der > Sache, wie bekomme ich meine I2C mit der Callback Routine verheiratet? Du lernst einfach als erstes, ereignisorientiert zu denken und zu programmieren. Das ist eine grundsätzliche Abkehr von einfachen Kontrollstrukturen über das Gesamtproblem hin zu den Strukturen von state machines. Und damit ein durchaus großes Problem für Programmieranfänger, denen das auch heute erstaunlicherweise nicht wirklich vermittelt wird. Denn das ist eigentlich die unabdingbare Vorstufe zum Verständnis der Abläufe und damit zur Programmierung von Interrupts (oder auch multithreading). Also: Am Anfang machst du es wie bei den C-lern üblich: Du setzt in ISRs nur irgendein Flag und pollst in main() dieses Flag. Später dann die Flags von mehreren ISRs und du verarbeitest vielleicht sogar die Daten, die diese ISRs liefern (oder brauchen). Im letzten Schritt der Selbstaufschlauung erkennst du dann vielleicht irgendwann, daß dieses Konzept komplette Grütze ist, wenn's an's Eingemachte geht, also die ganze Sache an die Grenze des Machbaren gelangt. Genau nur deswegen gibt es Interrupts nämlich: Weil es komplett kontraproduktiver Schwachsinn ist, den Kram generell in main() zu serialisieren. Wäre das irgendwie sinnvoll oder nützlich, bräuchte es nämlich das ganze Konzept der Hardware-Interrupts einfach nicht zu geben. Es gibt sie aber und zwar aus sehr guten Gründen... Laß dir also von keinem dieser unwissenden C-ler Regeln einreden wie etwa "Interrupts müssen immer kurz sein" oder "alles Wesentliche sollte in main() passieren". Das ist nur das, was die umsetzen können, ohne in's Schwitzen zu geraten, weil ihnen die verwendete Sprache die volle Kontrolle über das Timing verwehrt und/oder sie Interrupts nur sehr ineffizient nutzen kann. Allerdings: Sobald du dich wirklich mit multithreading (oder der wesentlich härteren Form derselben Sache in Form von miteinander interagierenden nativen ISRs) beschaftigst, wirst du nicht darum herum kommen, dich mit den Problemen der Synchronisation nebenläufiger Software zu beschäftigen, sonst kommt ziemlich sicher nur Grütze raus. Natürlich vollkommen sprachunabhängig. Man kann das durchaus auch in C sauber lösen. Nur ist das Ergebnis halt in aller Regel wesentlich weniger effizient als in Assembler...
c-hater schrieb: > Nur ist das Ergebnis halt in aller Regel wesentlich > weniger effizient als in Assembler... Kein Mensch der noch ganz bei Sinnen ist schreibt eine komplette Anwendung in Assembler. Bestenfalls mal ne kurze Interrupt-Routine, aber auch dann nur wenns wirklich gar nicht anders geht, oder man macht vielleicht mal alle Jubeljahre mal eine winzige Änderungen am (bereits vorhandenen) Startup-code oder vielleicht mal nen kleinen 3-Zeiler um schmutzige Tricks mit dem Stackpointer zu veranstalten. Das wars dann aber schon.
Uwe schrieb: > @Karl-Heinz, > > gut hab ich soweit verstanden. Aber wenn ich z.b. mehrere Write Aufrufe > habe z.B.: > > I2C_Write(Address_A, Value_1); > I2C_Write(Address_B, Value_2); > I2C_Write(Address_C, Value_3); > > da ist mir noch nicht klar wie ich die Wartezeiten hinbekomme. Indem du die auszugebenden Werte in einer Warteschlange (Queue) parkst und die Statemaschine so umbaust, dass sie nicht nur einzelne Bytes abarbeiten kann sondern eine komplette Sequenz aus der Warteschlange. Ja, in einem gewissen Sinne hat c-hater schon recht. Jetzt gehts ans eingemachte. Aber die Sache mit 'nur Assembler ist das wahre' ist kompletter Blödsinn. Gerade wenn es um Datenstrukturen geht, als um höhere Konzepte als einfach nur Bytes in Register schieben, wirst du mit Assembler alt.
:
Bearbeitet durch User
Karl H. schrieb: > Ja, in einem gewissen Sinne hat c-hater schon recht. Jetzt gehts ans > eingemachte. Aber die Sache mit 'nur Assembler ist das wahre' ist > kompletter Blödsinn. Ähem, wo habe ich das denn geschrieben? Mich deucht, ich hätte vielmehr geschrieben: > Natürlich vollkommen sprachunabhängig. Man kann das durchaus auch in C > sauber lösen. Nur ist das Ergebnis halt in aller Regel wesentlich > weniger effizient als in Assembler... Und dazu stehe ich. Wir können das gerne bei jedem beliebigen realen Problem vergleichen. Im allerbesten Fall (sehr primitive Sachen) wirst du mit C gerade so Gleichstand erreichen können. Im Normalfall wirst du aber (je nach Compilerqualität) schlechter bis ziemlich lausig schlechter abschneiden. Besonders schlimm finde ich: Ich bin mir absolut sicher, daß du nur zu gut selber weißt, daß es so ist! Warum also immer wieder die ewig gleichen erbärmlichen Lügen? Was zum Teufel bringt dir das?
c-hater schrieb: > Wir können das gerne bei jedem beliebigen realen Problem vergleichen. Im > allerbesten Fall (sehr primitive Sachen) wirst du mit C gerade so > Gleichstand erreichen können. Im Normalfall wirst du aber (je nach > Compilerqualität) schlechter bis ziemlich lausig schlechter abschneiden. > Besonders schlimm finde ich: Ich bin mir absolut sicher, daß du nur zu > gut selber weißt, daß es so ist! Wir können das gerne an einem ansprechend komplexen Problem klären, wer schneller ein entsprechendes Programm lauffähig hat. Aber bitte kein Wischi-Waschi Blink LED Beispiel. Moby hat ja Zeitnot vorgeschoben, was ich (augenzwinker) überhaupt nicht verstehen kann. Wenn ich Sonntag nachmittags 3 Stunden investieren kann, dann wird er es ja wohl auch können. Wie, der kann in 3 Stunden in Assembler nichts vernünftiges auf die Beinde stellen? Komisch, ich kann das in C durchaus. > Warum also immer wieder die ewig gleichen erbärmlichen Lügen? Was zum > Teufel bringt dir das? Weil dieser Kram keinen mehr wirklich interessiert. Ein Programm muss nur schnell genug sein. Nicht mehr. In der realen Praxis ist es in 95% aller Fälle völlig unerheblich, ob der Compiler beim Aufruf einer ISR noch das Sichern und Wiederherstellen von 5 Registern einbaut oder nicht. Die paar Takte mehr stören, bis auf ein paar Ausnahmefälle, nicht weiter. Und das weisst du auch zur Genüge. Du versuchst hier ständig deine mangelnden C-Fähigkeiten als Ursache für Probleme vorzuschieben, die nichts mit der Sprachwahl zu tun haben. Ich stimme dir zu, dass das Problem des TO in der Strukturierung der Software zu lösen ist. Die Frage ob Assembler oder C ist dafür allerdings überhaupt nicht relevant. Du hast dieses Thema hier eingebracht, nicht ich. Ich will das nur nicht so im Raum stehen lassen. Auf der anderen Seite würde ich gerne mal ein hocheffizentes Programm für meine Problemstellungen laufen lassen, um zu sehen wie viel mir das tatsächlich bringt. Schliesslich will ich auch etwas dafür haben, wenn schon die Entwicklungszeit um einen Faktor von mindestens 10 ansteigt. Ich seh zwar noch nicht, was mir das bringen könnte, denn die C Versionen sind allesamt schnell genug und lassen sich zu einem Bruchteil der Kosten einer Assemblerversion produzieren, aber ich lass mich auch überzeugen.
:
Bearbeitet durch User
Hallo nochmal, bitte keinen C/Assembler Krieg anfangen. Ich will keine Rakete zum Mond schicken sondern nur einen Helligkeitssensor auslesen und werde in C programmieren. Also ich habe immer noch folgenden Aufruf z.B.
1 | void Sensor_Init(void) |
2 | {
|
3 | I2C_Write(Address_A, Value_1); |
4 | I2C_Write(Address_B, Value_2); |
5 | I2C_Write(Address_C, Value_3); |
6 | }
|
7 | |
8 | //wird immer nach einem fertigen Write automatisch aufgerufen
|
9 | void Write_Complete_Callback(void) |
10 | {
|
11 | //???
|
12 | }
|
13 | |
14 | void main(void) |
15 | {
|
16 | Sensor_Init(); |
17 | while(1) |
18 | {}
|
19 | }
|
Die Statemachine muss also in die Write_Complete_Callback funktion, oder?
Grob skizziert könnte das so ausehen, das ganze Drumherum fehlt natürlich.
1 | unsigned short statenum; |
2 | unsigned char goto_next_state=0; |
3 | |
4 | void SensorInit(void) |
5 | {
|
6 | statenum = 0; |
7 | goto_next_state = 1; |
8 | }
|
9 | |
10 | void SensorWrite(void) |
11 | {
|
12 | switch ( statenum ) { |
13 | case 0: |
14 | I2C_Write(Address_A, Value_1); |
15 | break; |
16 | case 1: |
17 | I2C_Write(Address_B, Value_2); |
18 | break; |
19 | case 2: |
20 | I2C_Write(Address_C, Value_3); |
21 | break; |
22 | case 3: |
23 | PUTS("Done\r\n"); |
24 | break; |
25 | default:
|
26 | // Illegal State
|
27 | }
|
28 | |
29 | //wird immer nach einem fertigen Write automatisch aufgerufen
|
30 | void Write_Complete_Callback(void) |
31 | {
|
32 | goto_next_state = 1; |
33 | statenum++; |
34 | }
|
35 | |
36 | void main(void) |
37 | {
|
38 | Sensor_Init(); |
39 | while(1) |
40 | {
|
41 | if ( goto_next_state ) { |
42 | goto_next_state = 0; |
43 | SensorWrite(); |
44 | }
|
45 | }
|
46 | }
|
Uwe schrieb: > Im Moment starte ich zum Beispiel ein Read und warte danach 20ms, damit > die Statemachine abgearbeit werden kann. Das ist aber nicht Sinn der > Sache, wie bekomme ich meine I2C mit der Callback Routine verheiratet? Du hast damit ein prinzipielles Problem, ich kenne das bis zum Abwinken: Bei eigentlich allen I2C-Anwendungen ist es so, daß man einen Transfer anstoßen und dann warten muß bis er fertig ist, weil man für den Fortgang des momentanen Prozesses die Ergebnisse einfach braucht. Man kommt da ganz einfach nicht darum herum, auf das I2C-Busgeschäft und dessen Ende zu warten. Die logische Antwort wäre, eben ein RTOS zu verwenden, damit andere Prozesse die Rechenzeit zwischendurch haben können. Aber in sehr vielen Fällen ist ein RTOS einfach daneben weil zu überdimensioniert, zu viel Overhead. Also bleibt dann doch nur übrig, irgendwie ereignisorientiert zu programmieren. Also Transfer interruptgesteuert, bei Ende einen "Erfolgs-Event" generieren, parallel einen Timeout-Counter (per Systemtick) laufen lassen, der bei Zeitüberschreitung dann einen "Mißerfolgs-Event" generiert. Das ist alles recht verwinkelt und ein bissel kompliziert, also frag dich lieber, ob das alles nötig ist oder ob dein bisheriges Polling für die Anwendung völlig ausreichend ist. W.S.
Karl H. schrieb: > Jetzt magst du dich fragen, wozu dann überhaupt Interrupt, wenn ich > sowieso pollen muss? > Du hast eine bessere Organisationsform, bei der du einfacher andere > Dinge erledigen kannst, während die I2C Übertragung im Hintergrund > abgewickelt wird. Der Trick bei der Programmierung, bei der ein µC > scheinbar viele Dinge mehr oder weniger gleichzeitig erledigen kann, > besteht darin, dass die einzelnen Tätigkeiten auf kleine Zeithappen > heruntergebrochen werden und nirgends viel Zeit in einem Rutsch drauf > geht. So auch hier. Die Frage ist warum? Jeder Thread/Task bekommt einen Zeitslot und in dieser Zeit muss du dafür sorgen, dass eine I2C-Kommunikation, ob Read oder Write abgeschlossen ist. I2C im Interrupt-Mode macht nur als I2C-Slave sinn, als Master aus meiner Sicht weniger. Alles in kleine Häppchen herunterzubrechen bringt nur Spaghetti-Code und Unvorhersagbarkeit.
:
Bearbeitet durch User
Uwe schrieb: > da ist mir noch nicht klar wie ich die Wartezeiten hinbekomme. Ali K. schrieb: > Die Frage ist warum? Nee, 8 Jahre später fragt sich das niemand mehr. Ich frage mich, warum du den knallroten Hinweis über dem Eingabefeld missachtet hast?: "Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen." Dazu kommt, dass ich deine Antwort für falsch halte. Es macht auch für den Master durchaus Sinn. I²C Transaktionen können komplett in ISR abgewickelt werden, wenn man dazu Puffer verwendet. Das Hauptprogramm müsste nur die "Message" in den Puffer (oder eine Warteschlange) legen und später die "Response" auswerten, nachdem sie komplett empfangen wurde.
Steve van de Grens schrieb: > Uwe schrieb: >> da ist mir noch nicht klar wie ich die Wartezeiten hinbekomme. > > Ali K. schrieb: >> Die Frage ist warum? > > Nee, 8 Jahre später fragt sich das niemand mehr. Ich frage mich, warum > du den knallroten Hinweis über dem Eingabefeld missachtet hast?: > > "Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. > Bitte hier nur auf die ursprüngliche Frage antworten, > für neue Fragen einen neuen Beitrag erstellen." > > Dazu kommt, dass ich deine Antwort für falsch halte. Es macht auch für > den Master durchaus Sinn. I²C Transaktionen können komplett in ISR > abgewickelt werden, wenn man dazu Puffer verwendet. Das Hauptprogramm > müsste nur die "Message" in den Puffer (oder eine Warteschlange) legen > und später die "Response" auswerten, nachdem sie komplett empfangen > wurde. Dh, der Master muss sowieso warten , bis irgendwann alles gesendet und empfangen wurde. Und dieses irgendwann ist nicht erlaubt.
Ali K. schrieb: > Dh, der Master muss sowieso warten , bis irgendwann alles gesendet und > empfangen wurde. Stimmt. Es macht aber schon für die Struktur des Programmes einen erheblichen Unterschied, ob er auf eine ganze Antwort wartet, oder ob er (z.B.) 4 mal auf das Senden einzelner Bytes und danach 12 mal den Empfang einzelner Bytes wartet. Die Abhandlung von Timeouts kann man unabhängig davon im Zustandsautomaten implementieren. Egal ob man nun auf ein einzelnes Byte oder eine komplette Response wartet. Das raus und rein schieben in Puffer macht man üblicherweise in einer ISR, wenn das nicht bereits die Hardware ganz alleine kann (STM32 können das).
Ali K. schrieb: > Dh, der Master muss sowieso warten , bis irgendwann alles gesendet und > empfangen wurde. Nein, er muss eben nicht warten. Er delegiert einfach die Arbeit an eine interruptgetriebene Statemachine, die die Arbeit nebenbei erledigt. Was er dann nur noch machen muss (zumindest bei Reads zwingend): immer mal wieder checken, ob und mit welchem Ergebnis das I2C-Subsystem die Aufgabe abgeschlossen hat. Und selbst diese Poll-Aufgabe kann man noch durch einen Callback ersetzen. Der dann aber natürlich im Kontext eines Interrupts ausgeführt wird und dementsprechend darin limitiert ist, was darin gefahrlos getan werden kann.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.