Hi, ich versuche mich gerade an der Implementierung einer TWI (I2C) Master Bibliothek, welche Interrupt gesteuert ablaufen soll. Konkret stehe ich jetzt vor folgendem Problem: Mittels einer Funktion "twi_master_start(uint8_t address)" habe ich eine START Condition erzeugt. Nun soll über "twi_master_transmit(unsigned char message, message_length)" eine bestimmte Menge an Bytes verschickt werden. Da es wie gesagt Interrupt basiert ablaufen soll, suche ich nun eine Möglichkeit wie ich in der Funktion "twi_master_transmit()" den Interrupt mit dem Vektor "TWI_vect" ausführen kann. Dort würde ich dann gerne über den entsprechenden Puffer iterieren und die Nachrichten auf den Bus legen. Leider scheint es nur die folgenden Möglichkeiten zu geben einen solchen Interrupt auszulösen: > The TWINT Flag is set in the following situations: > • After the TWI has transmitted a START/REPEATED START condition > • After the TWI has transmitted SLA+R/W > • After the TWI has transmitted an address byte > • After the TWI has lost arbitration > • After the TWI has been addressed by own slave address or general call > • After the TWI has received a data byte > • After a STOP or REPEATED START has been received while still addressed > as a Slave > • When a bus error has occurred due to an illegal START or STOP condition Die START Condition habe ich aber schon mittels "twi_master_start()" erzeugt und ggf. befindet sich die dafür notwendige Adresse nicht mehr im Puffer, sodass ich nicht nochmals eine erzeugen kann. Was gäbe es sonst noch für Möglichkeiten, bzw. wie würdet ihr das Ganze angehen? Ich habe schon einige Implementierungen einer "TWI Master" Bibliothek gesehen, allerdings sind die meisten nicht Interrupt basiert. Atmel's Application Note 315 enthält eine Interruptgesteuerte Beispielimplementierung, dort wird aber die start() und transmit() Funktion "zusammengewürfelt", sodass ich am Anfang des Puffers immer die entsprechende Adresse befinden muss. Das gefällt mir nicht sonderlich gut. Habt ihr brauchbare Vorschläge? Vielen Dank!
AVR schrieb im Beitrag #2885329: > start() und transmit() > Funktion "zusammengewürfelt", sodass ich am Anfang des Puffers immer die > entsprechende Adresse befinden muss. Für die I2C Hardware (im Master) ist das Adressbyte einfach nur das erste Byte, also Daten, die auf den Bus sollen. Sie kann dir nicht helfen, zwischen I2C Adresse, möglichem Adresspointer im Slave und Daten zu unterscheiden. Bau eine kleine State-Maschine in den Interrupt Handler, die immer das passende schickt. MfG Klaus
Hm, ich bin mir nicht sicher, ob wir hier nicht aneinander vorbeisprechen. Ich will im Wesentlichen folgenden Ablauf ermöglichen: twi_master_start(0xD8); twi_master_transmit(message, 2); twi_master_stop(); Dabei soll das Ganze Interrupt gesteuert ablaufen, d.h. insbesondere das Verschicken der Nachricht, welche hier 2 Byte lang ist. Andernfalls müsste ich ja das entsprechende Flag pollen und warten. Das ist bei 100 KHz SCL Takt und 16 MHz Mikrocontrollertakt doch eine ganze Menge an Zyklen, die hier verstreichen. Wie meinst du das jetzt mit der Statemachine? So ganz klar ist mir das noch nicht ...
Was mir unklar ist (bzw. was meine ursprüngliche Frage ist): Wie bewege ich den AVR dazu die TWI ISR auszuführen ohne eine START bzw. STOP Condition loszuschicken? Ich habe ja idealerweise zu diesem Zeitpunkt die Adresse bereits auf den Bus gelegt und kenne diese gar nicht mehr. In der ISR würde ich jetzt den entsprechenden Puffer auf den Bus legen. Wie aber führe ich die ISR aus ;)?
lass doch in der ISR ne statemachine mitlaufen..dann weisst du was gerade los ist..
AVR, Atmel liefert für die TWI-Schnittstelle alles was Du brauchst mundgerecht: Das Wichtigste ist das TWSR-Register. Bei jedem Interrupt findest Du dort ein Byte, das Dir anzeigt, was seit dem vorhergehenden Interrupt passiert ist (Du musst den Inhalt des TWSR nur durch 8 dividieren). Das Programm, das beim TWI-Interrupt aufgerufen wird, muss also als Erstes das TWSR lesen und dann anhand des Wertes entscheiden, welche Aktion als Nächstes ausgeführt werden soll. Sowas macht man am Besten mit einer Tabelle, bei der Wert des TWSR (dividiert durch 8) als Index dient. Das ist es, was die anderen Antworter meinen, wenn sie von "state machine" (Zustandsautomat) schreiben. Z.B.: Wenn in der Betriebsart Master-Transmitter die START-Bedingung erfolgreich abgesetzt wurde, enthält das TWSR beim Interrupt den Wert 8*0x01=0x08. Dann weisst Du, dass jetzt die TWI-Adresse des Sklaven gesendet werden muss. Beim nächsten Interrupt bekommst Du dann 8*0x03=0x18 zurück, wenn der Sklave mit ACK geantwortet hat. Wenn er NAK gesagt hat, bekommst Du 8*0x04=0x20 zurück. Je nachdem musst Du dann die nächste Aktion programmieren. Es geht wirklich wie am Schnürchen. Den ganzen Ablauf beim Master-Transmitter findest Du in der Doku, zum ATmega8 z.B., in Tabelle "Figure 87" auf Seite 187. Für die anderen Betriebsarten gibt es eigene Tabellen. Ciao, mare_crisium
AVR schrieb im Beitrag #2885329: > Die START Condition habe ich aber schon mittels "twi_master_start()" > erzeugt und ggf. befindet sich die dafür notwendige Adresse nicht mehr > im Puffer Dann hast Du es falsch programmiert. Im Main setzt Du nur das Startbit und der Interrupt macht den ganzen Rest inclusive Stop. Dazu muß er natürlich einen Puffer übergeben kriegen. Und dieser Puffer enthält die Slaveadresse und alle Datenbytes. Bzw. beim Lesen nur die Slaveadresse und der Interrupt füllt die Datenbytes. AVR schrieb im Beitrag #2885367: > Ich will im Wesentlichen folgenden Ablauf ermöglichen: > > twi_master_start(0xD8); > twi_master_transmit(message, 2); > twi_master_stop(); Das ist der Ablauf ohne Interrupt und somit Quatsch. Mit Interrupt: - erzeuge Puffer, schreibe Länge, Adresse und Daten hinein - übergib den Zeiger auf den Puffer dem Interrupt - setze Startbit Und der Interrupt setzt irgendwann ein Ready-Flag, daß alles erledigt ist. Peter
Hallo AVR, vielleicht hilft dir das hier weiter: Atmels AppNote für Nutzung des TWI-Moduls als Master Dokument: http://www.atmel.com/Images/doc2564.pdf Beispielcode: http://www.atmel.com/Images/AVR315.zip
AVR, nur eins nicht vergessen: Am Ende der Prozedur, die beim TWI-Interrupt aufgerufen wird, MUSS(!!!) das Bit TWINT im Register TWCR immer auf "Eins" gesetzt werden. Andernfalls bleibt der Interrupt inaktiv. Ciao, mare_crisium
Peter Dannegger schrieb: > Dazu muß er natürlich einen Puffer übergeben kriegen. Und dieser Puffer > enthält die Slaveadresse und alle Datenbytes. Und genau dieser Ansatz gefällt mir nicht sonderlich, weil ich hier Daten und Adresse im Puffer "vermische". Lässt sich das denn nicht eleganter lösen? Vorallem gebe ich bei diesem Ansatz ja immer den Bus komplett frei, weil ich im letzten Schritt ja immer eine STOP Condition verschicke. Vielen Dank!
AVR schrieb im Beitrag #2885707: > weil ich hier > Daten und Adresse im Puffer "vermische". Dann schreib halt deine ISR so, daß der zwei Puffer übergeben werden, einer mit Daten, einer mit Adresse. Alles kann, nichts muß, your choice... Oliver
Wenn Du den Code der ISR sowohl von den Bibliotheksfunktionen als auch von der ISR aus aufrufen willst, könntest Du ihn in eine eigene Funktion packen. Ist aber imo keine gute Idee, da es ineffizient ist und den Programmfluss undurchsichtig und fehleranfällig macht. Wenn Du Dein Start/Transmit/Stop-Interface unbedingt willst, könntest in der transmit()-Funktion prüfen, ob die Start-Condition schon fertig gesendet wurde. Wenn ja, sendest Du das erste Byte von der transmit()-Funktion aus und alle folgenden in der ISR. Wenn nein, wird das erste Byte von der ISR aus versendet (nachdem die Start-Condition raus ist). Die sinnvollste Variante ist aber imo, den kompletten Transmit mit nur einem Funktionsaufruf zu starten. Wenn Du darüber entscheiden können willst, ob Start/Stop-Conditions gesendet werden sollen, könntest Du ja noch ein entsprechendes Flag vorsehen. Also etwas so:
1 | #define TWI_NONE 0
|
2 | #define TWI_START 1
|
3 | #define TWI_STOP 2
|
4 | twi_transmit(uint8_t addr, const uint8_t* data, uint8_t len, uint8_t flags); |
Eine Aufruf wäre dann z.B.
1 | twi_transmit(0xD8, message, 2, TWI_START | TWI_STOP); |
Oder Du machst das gleiche mit Deinen drei Funktionen. Nur dass twi_start() und twi_stop() nichts tatsächlich senden, sondern nur die Flags bzw. die Adresse zwischenspeichern, die dann im twi_transmit()-Befehl in einem Rutsch zusammen mit den Daten gesendet werden.
AVR schrieb im Beitrag #2885707: > Und genau dieser Ansatz gefällt mir nicht sonderlich, weil ich hier > Daten und Adresse im Puffer "vermische". Niemand hindert dich daran, dir eine 'Message' Struktur zu bauen, in der du dir die Dinge so ablegst, wie dir das besser gefällt. Das Grundprinzip ist immer noch das gleiche: Eine Funktion stellt alle Informationen für die ISR zusammen und bereit, die die ISR zum Arbeiten braucht. Ob das jetzt nur ein einziger BUffer ist, oder ob das eine Struktur ist, in der die die Dinge schön in einzelne Member aufdröselst - deine Entscheidung und hängt nur von dem handwerklichen Programmiergeschick ab. > Vorallem gebe ich bei diesem Ansatz ja immer den Bus > komplett frei, weil ich im letzten Schritt ja immer eine STOP > Condition verschicke. Wenn du in deiner Message Struktur einen Member hast, der aussagt: für diese Übertragung keine Stop-Condition erzeugen, dann kann sich die ISR danach richten und eben keine Stop-Condition erzeugen. Das Prinzip ist: Das Arbeitspferd ist die ISR. Was immer die an Informationen braucht um so zu arbeiten wie du das haben willst, das braucht sie an Informationen. Also muss diese Info irgendwer bereitstellen. Zb. eine Funktion, die die Daten entgegen nimmt (ev. mit Zusatzinfo), und so aufbereitet, dass die ISR damit arbeiten kann. Du bist der Programmierer, der Künstler! Du entscheidest, wie das konkret und im Detail funktionieren soll.
AVR schrieb im Beitrag #2885707: > Und genau dieser Ansatz gefällt mir nicht sonderlich, weil ich hier > Daten und Adresse im Puffer "vermische". Dann schreibste eben den Puffer als Struct (Länge, Adresse, Daten) und diese als Union mit nem Byte-Array. Der CPU ist es völlig wurscht, welcher Variable Du welchen Namen gibst und an welcher RAM-Adresse sie landet. AVR schrieb im Beitrag #2885707: > Vorallem gebe ich bei diesem Ansatz ja immer den Bus > komplett frei, weil ich im letzten Schritt ja immer eine STOP Condition > verschicke. Vermutlich stört Dich das fürs EEPROM lesen. Dann einfach 2 Längenbyte übergeben, Schreiblänge und Leselänge und der Interrupthandler kann automatisch das Repeat-Start einfügen. Peter
Peter Dannegger schrieb: > Dann schreibste eben den Puffer als Struct (Länge, Adresse, Daten) und > diese als Union mit nem Byte-Array. Irgendwie komme ich immer noch nicht ganz mit. Wofür brauche ich denn die Union? Im Struct würde dann doch schon alles stehen? Bzw. wie "groß" wäre das Byte-Array dann überhaupt?
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.