Ich habe mich jetzt mal in I2C eingelesen und frage mich ob ich so richtig denke. Also, ich erteile dem Slave eine Adresse, nun habe ich das wie folgt verstanden: Master: I2C Start I2C Schreibe Adresse + Write I2C Schreibe byte I2C Stop Das ist ja die Übertragung. Nun habe ich wie folgt verstanden das, sobald der Slave seine Adresse "verstanden" hat in einen ISR springt und dort dann die register, eins nach dem anderen einliest. Ist das so richtig? Demach könnte ich als "erstes" Byte eine länge der Daten senden und auf dem Slave dann eine schleife mit der Anzahl der länge laufen lassen, richtig? Also auf dem Master: Sende start Sende adresse+write Sende länge Sende bytes in schleife Sende checksumme Sende stop Und auf dem slave: Empfange adresse Springe ISR Lese erstes byte (länge) Führe schleife in anzahl der länge aus Lese checksumme Führe programm aus Habe ich das so richtig verstanden? Dann muss ich nur noch gucken wie es vom slave zum master geht. Danke euch schonmal.
Hallo Tobias, auf Anhieb würde ich sagen, Du hast hier einen speziellen Fall beschrieben. Das Protokoll für einen PCF8574 ist etwas einfacher. Wenn Du Dich da praktisch einarbeiten möchtest, dann fang mit dem PCF8574 an. Der ist ziemlich simpel und unkritisch. mfg klaus
Ich möchte halt atmega <-> atmega Ich prügel mich da schon durch g Mir ging es nur darum ob der "ablauf" so richtig ist!?
Schau mal bei TI nach, MSP430. Da gibt es auch Beispiele für I2C Master-Client Komunikation. Aber im Prinzip müsste Dein Ansatz so OK sein. Du erstellst das Protokoll ja selber. Ansonsten ähneln sich die Protokolle der I2C-Bausteine. Aber in jedem Datenblatt findest Du trotzdem dazu eine spezielle Protokollbeschreibung. mfg Klaus
Tobias N. schrieb: > Ich möchte halt atmega <-> atmega > > Ich prügel mich da schon durch *g* > > Mir ging es nur darum ob der "ablauf" so richtig ist!? Richtig ist das was du als richtig definierst. Einem Telefon ist es auch egal, ob du reinpfeifst oder deine Nachricht in Form von Rülpsern übermittelst. Das Telefon stellt den Übertragunskanal bereit, wie deine Botschaften dann aussehen, ist dein Bier (so etwas nennt man ein Protokoll) Also: wenn du das so machen willst, dann mach das so. Nur das man beim Client keine Schleife machen wird. Wenn per I2C was reinkommt, dann 'benachrichtigt' die Hardware ds Programm mit einem Interrupt. In diesem Interrupt entscheidest du dann, was das gerade eingetroffene Byte für eine Bedeutung hat und was du damit machst. Ist es das erste Byte in der Übertragung, dann speicherst du es dir als Länge der noch zu empfangenden Bytes ab, ist es nicht das erste Byte, dann speicherst du das Byte als Datenbyte ab und erniedrigst diesen Längenzähler um 1. Wird so der Längenzähler zu 0, dann weisst du, dass damit die Nachricht komplett übertragen wurde. In den vielen Fällen muss man in der µC Programmierung weg von dieser 'Ich mach was in einer Schleife' Denkweise. Man muss hin zu einer 'Ich wache gerade auf, finde diese Inforamtion vor, was mache ich damit ehe ich wieder schlafen gehe' Denkweise.
Ich dachte dann so:
1 | unsigned char *buf; |
2 | |
3 | ISR (TWI_vect) |
4 | {
|
5 | len = read_Ack(); // Erstes Byte ist die Länge. |
6 | |
7 | for(i=0;i <len; i++) |
8 | {
|
9 | buf[i] = read_Ack(); // Lese Byte für Byte ein und schreibe in Array |
10 | }
|
11 | len = read_Nack(); // weiss nicht ob das wichtig ist |
12 | return buf; |
13 | }
|
:
Bearbeitet durch User
Habe da noch eine Frage. Wird die ISR mit jedem Byte was empfangen wird aufgerufen oder wird diese aufgerufen sobald die Slave Adresse erkannt wurde und arbeitet dann bis zum Stop?
------ Arduino: i2c avr <--> avr "mal eben machen" ist schon einfacher mit Arduino: http://arduino.cc/en/Tutorial/MasterReader ------ AVR: AVR311 PDF: http://www.atmel.com/images/doc2565.pdf AVR311 Software: http://www.atmel.com/Images/AVR311.zip (geht wohl mit avr-gcc nicht?!) ---> dann halt diese modifizierte hier: https://github.com/kelvinlawson/avr311-twi-slave-gcc AVR TWI Master und Slave Funtionen in C Beitrag "AVR TWI Master und Slave Funtionen in C"
Karl Heinz schrieb: > Nur das man beim > Client keine Schleife machen wird. Wenn per I2C was reinkommt, dann > 'benachrichtigt' die Hardware ds Programm mit einem Interrupt. In diesem > Interrupt entscheidest du dann, was das gerade eingetroffene Byte für > eine Bedeutung hat und was du damit machst. Ist es das erste Byte in der > Übertragung, dann speicherst du es dir als Länge der noch zu > empfangenden Bytes ab, ist es nicht das erste Byte, dann speicherst du > das Byte als Datenbyte ab und erniedrigst diesen Längenzähler um 1. Wird > so der Längenzähler zu 0, dann weisst du, dass damit die Nachricht > komplett übertragen wurde. Jein. ich mach das immer etwas anders: Ich definiere mir einen Empfangspuffer mit einer definierten Größe, sodass die maximal erwartete Nachricht noch Platz hat. Danach empfange ich Byte für Byte, kümmere mich an der Stelle aber überhaupt nicht darum welche Bedeutung die Bytes haben (ob das erste Byte eine Länge oder eine Registernummer oder die relative Mondfeuchtigkeit ist, ist an der Stelle irrelevant). Solange ich noch Platz im Buffer für das gerade empfangene Byte habe, speichere ich das Byte und antworte ich mit ACK, hab ichs nicht mehr Platz, mit NACK. Danach stelle ich den Buffer und die Länge des empfangenen Pakets der darüberliegenden Schicht zur Verfügung. Was die damit macht, ist mir auch wieder egal. Tobias N. schrieb: > ISR (TWI_vect) > { > len = read_Ack(); // Erstes Byte ist die Länge. > > for(i=0;i <len; i++) > { > buf[i] = read_Ack(); // Lese Byte für Byte ein und schreibe in > Array > } > len = read_Nack(); // weiss nicht ob das wichtig ist > return buf; > } Da denkst du grad ziemlich verkehrt: die TWI-ISR wird für jeden Vorgang am TWI aufgerufen (adressierung, byte empfangen usw) da drinnen kannst du kein read_Ack() machen.
Dann muss ich über das isr alles auswerten? Also auch die adresse, lesen oder schreiben, und die einzelnen bytes. Also: start signal -> isr Adresse -> isr Jedes byte -> isr Stop signal -> isr So? Also springt er jedesmal in die isr!? Danke euch schonmal.
Tobias N. schrieb: > So? Also springt er jedesmal in die isr!? Ja, korrekt. Wobai man am Master noch die Wahl hat, ob man I2C mit oder ohne Interrupt realisiert (ohne ist gebräuchlicher), den Slave kann man sinnvollerweise nur mit ISR implementieren.
Also in etwa so:
1 | if (todo > 0) |
2 | {
|
3 | |
4 | if (todo == 1) |
5 | {
|
6 | addr = TWDR; |
7 | |
8 | if (addr == lese_addr) |
9 | {
|
10 | todo = 2; |
11 | sende ack; |
12 | }
|
13 | |
14 | if (addr == schreibe_addr) |
15 | {
|
16 | todo = 3; |
17 | sende ack; |
18 | }
|
19 | }
|
20 | |
21 | if (todo == 3) |
22 | {
|
23 | len = TWDR; |
24 | array_len = 0; |
25 | sende ack; |
26 | todo = 4; |
27 | }
|
28 | |
29 | if (todo == 4) |
30 | {
|
31 | data[array_len] = TWDR; |
32 | array_len++; |
33 | if(array_len == len) |
34 | {
|
35 | sende nack; |
36 | }
|
37 | else
|
38 | {
|
39 | sende ack; |
40 | }
|
41 | }
|
42 | |
43 | if stopsignal => todo = 0; |
44 | |
45 | }
|
46 | else
|
47 | {
|
48 | if startsignal -> setze todo = 1; |
49 | }
|
Nur mal so grob aber so in etwa sollte das ja dann sein, oder?
:
Bearbeitet durch User
Tobias N. schrieb: > Nur mal so grob aber so in etwa sollte das ja dann sein, oder? nein, eigentlich überhaupt nicht :-( Am AVR (zumindest auf den "großen" wie ATmega) gibt dir die TWI-Hardware schon die Rahmenbedingungen für die State Machine vor, gesteuert über TWSR (TWI Status Register). Dazu gibts eine wunderbare Beschreibung mit wunderbaren Tabellen im Datenblatt. Arbeite das mal durch. Wenn schon dann sähe es etwa so aus:
1 | ISR(TWI_vect) |
2 | {
|
3 | // code based on tables 21.4..6 from ATmega328 data sheet
|
4 | |
5 | // cache some volatile variables
|
6 | uint8_t index, length; |
7 | |
8 | switch (TW_STATUS) { |
9 | |
10 | case 0x00: // Bus error due to an illegal START or STOP condition |
11 | TWCR = _BV(TWSTO) | _BV(TWINT); // return to a well-defined unaddressed slave mode and release SCL and SDA |
12 | I2C_Busy = 0; // transmission aborted |
13 | I2C_Error = 1; // set error flag |
14 | break; |
15 | |
16 | case 0x60: // own SLA+W has been received; ACK has been returned |
17 | case 0x68: // own SLA+W has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master |
18 | case 0x70: // general call address has been received; ACK has been returned |
19 | case 0x78: // general call address has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master |
20 | I2C_Busy = 1; // transmission started |
21 | I2C_RxIndex = 0; // reset packet index |
22 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // send ACK on next byte |
23 | break; |
24 | |
25 | case 0x80: // previously addressed with own SLA+W; data has been received; ACK has been returned |
26 | case 0x88: // previously addressed with own SLA+W; data has been received; NAK has been returned |
27 | case 0x90: // previously addressed with general call; data has been received; ACK has been returned |
28 | case 0x98: // previously addressed with general call; data has been received; NAK has been returned |
29 | index = I2C_RxIndex; // local cache |
30 | if (index < I2C_PACKETSIZE) { |
31 | I2C_RxBuffer[index++] = TWDR; // store received data |
32 | I2C_RxIndex = index; // write back cache |
33 | }
|
34 | if (index < I2C_PACKETSIZE) { |
35 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // send ACK on next byte |
36 | } else { |
37 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); // no space left, send NAK on next byte |
38 | }
|
39 | break; |
40 | |
41 | case 0xA8: // own SLA+R has been received; ACK has been returned |
42 | case 0xB0: // own SLA+R has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master |
43 | I2C_Busy = 1; // transmission started |
44 | I2C_TxIndex = 0; // reset packet index |
45 | // fall through
|
46 | |
47 | case 0xB8: // data byte in TWDR has been transmitted; ACK has been received |
48 | index = I2C_TxIndex; // local cache |
49 | length = I2C_TxLength; // local cache |
50 | if (index < length) { |
51 | TWDR = I2C_TxBuffer[index++]; // transmit next byte |
52 | I2C_TxIndex = index; // write back cache |
53 | } else { |
54 | TWDR = 0; // we have nothing more... |
55 | }
|
56 | if (index < length) { |
57 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // expect ACK |
58 | } else { |
59 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); // last byte: expect NAK |
60 | }
|
61 | break; |
62 | |
63 | case 0xA0: // a STOP condition or repeated START condition has been received while still addressed as Slave |
64 | I2C_Busy = 0; // transmission ended |
65 | if (I2C_RxIndex > 0) { |
66 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); // disable TWI until data has been processed |
67 | } else { |
68 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // re-enable TWI |
69 | }
|
70 | break; |
71 | |
72 | case 0xC0: // data byte in TWDR has been transmitted; NAK has been received |
73 | case 0xC8: // last data byte in TWDR has been transmitted (TWEA = 0); ACK has been received |
74 | I2C_Busy = 0; // transmission ended |
75 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // re-enable TWI |
76 | break; |
77 | |
78 | case 0xF8: // no relevant state information available; TWINT = 0 |
79 | break; |
80 | |
81 | default: // all other states |
82 | I2C_Busy = 0; // transmission aborted |
83 | I2C_Error = 1; // something strange happened |
84 | TWCR = _BV(TWSTO) | _BV(TWINT); // return to a well-defined unaddressed slave mode and release SCL and SDA |
85 | break; |
86 | }
|
87 | }
|
Wie du vielleicht erkennst: I2C ist keine Anfängerkost...
Aahh. In TWCR steht drinne ob Start oder adresse+R oder adresse+W also muss ich jedesmal nur das Register auslesen und auswerten :) Wird ja immer einfacher die ganze Sache g
Tobias N. schrieb: > In TWCR steht drinne ob Start oder adresse+R oder adresse+W also muss > ich jedesmal nur das Register auslesen und auswerten :) nein. Tobias N. schrieb: > Wird ja immer einfacher die ganze Sache nein. Bitte - lies dir im Datenblatt das Kapitel zu TWI, und danach die AVR311 "Using the TWI module as I2C slave". Sonst wird das nix. nochmal: LIES DAS! Vorher kriegst du von mir keine Antwort mehr.
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.