Hallo, ich habe ein "Netzwerk" aus mehreren atmega32. Diese Kommunizieren über i2c. Es wird nur gesendet im Master Transmitter Modus und Empfangen im Slave Receiver. Alle atmega sind sowohl Slave und Master. Das Funktioniert soweit prima, jedoch bekomme ich ein Problem wenn ich versuche zu senden (Master Transmitter) und etwas Empfange. Als Test lasse ich einfach beide atmegas Texte hin und her schicken. Was klappt bis ich senden will während ein Empfang läuft. Irgendwie habe ich noch keine Möglichkeit gefunden festzustellen, ob der i2c/TWI frei ist. Ich dachte ich kann "jederzeit" sagen sende ein Startcondition und sobald es geht wird dies ausgeführt und ein Interrupt ausgelöst. Das geht nicht. Bin das PDF des atmega nun mehrfach durch. Habe ich etwas übersehen ? Der i2c Code ist so alleine nicht Lauffähig, daher bringt das Posten hier vermutlich nichts. Ich hoffe es hat trotzdem noch jemand einen Tipp. Ich dachte ein Flag setzen sobald ich etwas Empfange sollte reichen, aber scheinbar deckt das nicht alle Sonderfälle ab. Gruss Juergen
>Als Test lasse ich einfach beide atmegas Texte hin und her schicken. Was >klappt bis ich senden will während ein Empfang läuft. Solange die TWI Hardware arbeitet darfst du nicht daran rumfummeln. Also laufende Übertragung abschließen und dann Start anfordern. >Irgendwie habe ich noch keine Möglichkeit gefunden festzustellen, ob der >i2c/TWI frei ist. Brauch dich ja auch nicht zu interessieren. >Der i2c Code ist so alleine nicht Lauffähig, daher bringt das Posten >hier vermutlich nichts. Ich hoffe es hat trotzdem noch jemand einen >Tipp. Poste deinen Code! Solange keiner sieht was du mit TWI anstellst kann dir keiner helfen. >Ich dachte ein Flag setzen sobald ich etwas Empfange sollte reichen, >aber scheinbar deckt das nicht alle Sonderfälle ab. Von wo bis wo setzt du das Flag? Code??
Alle Master müssen den I2C-Bus auf Startbedingung überwachen und eigene Starts unterdrücken, bis wieder ein Stop empfangen wird. Für die seltenen Fälle, bei denen mehr als ein Master gleichzeitig Start signalisiert, muss eine Bus-Arbitration durchgeführt werden. Dabei müssen alle Master sofort ihre Übertragung abbrechen, wenn sie selbst SDA=1 gesetzt haben, aber tatsächlich SDA=0 ist. Bei I2C-Teilnehmern, die gleichzeitig Master und Slave sein können, gibts dabei noch ein spezielles Problem. Wenn bei einer Bus-Arbitration ein Slave adressiert werden sollte, der aber selbst gerade als Master an der Bus-Arbitration beteiligt ist, kann auf die Adressierung dieses Teilnehmers kein ACK erfolgen. All diese Probleme, die in einem Single-Master Sytem gar nicht erst auftreten würden, müssen von der Software abgefangen werden.
Das Flag was ich weiter oben erwähnt habe, ist schon wieder drausen. Es hat ja nicht funktioniert :-( Ich kann den Code auf die schnelle nur hier "inline" posten. Ach ja, das Makro "_BVC()" ist "((0<<x))" und für mich einfacher mal ein Flag zu löschen und dazu zu nehmen. Wie gesagt der Code ist nur auf MT und SR ausgelegt.
1 | /*
|
2 | *
|
3 | *
|
4 | *
|
5 | */
|
6 | |
7 | #include "i2c.h" |
8 | #include <util/atomic.h> |
9 | #include <avr/interrupt.h> |
10 | #include <util/twi.h> |
11 | #include "avr_helper.h" |
12 | #include "uart.h" |
13 | |
14 | //#define IC2_DEBUG
|
15 | |
16 | enum
|
17 | {
|
18 | i2c_unknown = 0, |
19 | i2c_Idle, |
20 | i2c_send_start, |
21 | i2c_send_addr, |
22 | i2c_send_data, |
23 | i2c_send_stop
|
24 | };
|
25 | |
26 | struct _sI2cStatus |
27 | {
|
28 | uint8_t status; |
29 | uint8_t txBuffer[20]; |
30 | uint8_t txPos; |
31 | uint8_t toSendPos; |
32 | uint8_t isMaster:1; |
33 | };
|
34 | |
35 | volatile struct _sI2cStatus i2cStatus; |
36 | |
37 | #define getTWIstatus (TWSR & TW_STATUS_MASK)
|
38 | |
39 | void setI2CAddr(uint8_t addr) |
40 | {
|
41 | TWAR = (addr & 0x7F)<<1; |
42 | }
|
43 | |
44 | void initI2C(uint8_t isMaster, uint16_t baud, uint8_t myAddr) |
45 | {
|
46 | if (isMaster) |
47 | i2cStatus.isMaster = 1; |
48 | else
|
49 | i2cStatus.isMaster = 0; |
50 | |
51 | i2cStatus.status = i2c_Idle; |
52 | |
53 | uint8_t scl = (F_CPU/baud -16) / 2; |
54 | |
55 | TWBR = scl; |
56 | setI2CAddr(myAddr); |
57 | |
58 | PORTC = _BV(PC0)|_BV(PC1); // Pullup an |
59 | |
60 | |
61 | //last enable interrupt
|
62 | TWCR = _BVC(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
63 | //TWCR |= _BV(TWIE) | _BV(TWEN);
|
64 | |
65 | }
|
66 | |
67 | void sendI2CStart(void) |
68 | {
|
69 | #ifdef I2C_DEBUG
|
70 | char buffer[10]; |
71 | uint8_t twcr; |
72 | uartPut('t'); |
73 | twcr = TWCR; |
74 | uartPuts(uint8ToAh(buffer, twcr)); |
75 | uartPut(','); |
76 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
77 | uartPut(':'); |
78 | #endif
|
79 | ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) |
80 | {
|
81 | TWCR = _BV(TWINT)|_BV(TWEA)|_BV(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
82 | i2cStatus.status = i2c_send_start; |
83 | }
|
84 | #ifdef I2C_DEBUG
|
85 | //twcr = TWCR;
|
86 | uartPuts(uint8ToAh(buffer, twcr)); |
87 | uartPut(','); |
88 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
89 | uartPut('\r'); |
90 | #endif
|
91 | }
|
92 | |
93 | void sendI2CStop(void) |
94 | {
|
95 | ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) |
96 | {
|
97 | #ifdef I2C_DEBUG
|
98 | uartPut('e'); |
99 | #endif
|
100 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BV(TWSTO)|_BV(TWEN)|_BV(TWIE); |
101 | i2cStatus.status = i2c_send_stop; |
102 | }
|
103 | }
|
104 | |
105 | void sendI2C(uint8_t addr, uint8_t *data, uint8_t len) |
106 | {
|
107 | // wait to get last packet send
|
108 | // currently we do not support multiple commands in buffer
|
109 | while (i2cStatus.txPos > 0) |
110 | {
|
111 | //#ifdef I2C_DEBUG
|
112 | uartPut('-'); |
113 | //#endif
|
114 | nop(); |
115 | }
|
116 | /*
|
117 | while(!(getTWIstatus & _BV(TWINT)))
|
118 | {
|
119 | uartPut('.');
|
120 | }
|
121 | */
|
122 | ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) |
123 | {
|
124 | if (len < sizeof(i2cStatus.txBuffer)) |
125 | {
|
126 | #ifdef I2C_DEBUG
|
127 | uartPut('+'); |
128 | #endif
|
129 | i2cStatus.txBuffer[0] = (addr<<1)|TW_WRITE; // 0 = WRITE, 0x01 = READ |
130 | |
131 | uint8_t i; |
132 | for(i=0;i<len;i++) |
133 | {
|
134 | i2cStatus.txBuffer[i+1] = data[i]; |
135 | }
|
136 | |
137 | //memcpy(&(i2cStatus.txBuffer)+1, data, len);
|
138 | //uartPut('2');
|
139 | |
140 | i2cStatus.txPos = len+1; //add addressbyte |
141 | i2cStatus.toSendPos = 0; |
142 | sendI2CStart(); |
143 | }
|
144 | }
|
145 | }
|
146 | |
147 | ISR(TWI_vect) |
148 | {
|
149 | #ifdef I2C_DEBUG
|
150 | char buffer[10]; |
151 | uartPut('i'); |
152 | #endif
|
153 | switch(getTWIstatus) |
154 | {
|
155 | case TW_START: // START send |
156 | {
|
157 | #ifdef I2C_DEBUG
|
158 | uartPuts("TWI_TW_START,"); |
159 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
160 | uartPut('\r'); |
161 | #endif
|
162 | TWDR = i2cStatus.txBuffer[0]; |
163 | i2cStatus.toSendPos++; |
164 | i2cStatus.status = i2c_send_addr; |
165 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
166 | break; |
167 | }
|
168 | case TW_MT_SLA_ACK : |
169 | case TW_MT_DATA_ACK: |
170 | {
|
171 | #ifdef I2C_DEBUG
|
172 | uartPuts("TW_MT_SLA/DATA_ACK,"); |
173 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
174 | uartPut('\r'); |
175 | #endif
|
176 | if (i2cStatus.toSendPos < i2cStatus.txPos) |
177 | {
|
178 | TWDR = i2cStatus.txBuffer[i2cStatus.toSendPos]; |
179 | i2cStatus.toSendPos++; |
180 | i2cStatus.status = i2c_send_data; |
181 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
182 | }
|
183 | else
|
184 | {
|
185 | i2cStatus.txPos = 0; |
186 | sendI2CStop(); |
187 | }
|
188 | break; |
189 | }
|
190 | case TW_MT_DATA_NACK: |
191 | {
|
192 | #ifdef I2C_DEBUG
|
193 | uartPuts("TW_MT_DATA_NACK,"); |
194 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
195 | uartPut('\r'); |
196 | #endif
|
197 | i2cStatus.txPos = 0; |
198 | sendI2CStop(); |
199 | break; |
200 | }
|
201 | case TW_MT_ARB_LOST: |
202 | {
|
203 | #ifdef I2C_DEBUG
|
204 | uartPuts("TW_MT_ARB_LOST,"); |
205 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
206 | uartPut('\r'); |
207 | #endif
|
208 | //TODO we need to support this
|
209 | // ups we lost bus control
|
210 | break; |
211 | }
|
212 | case TW_MT_SLA_NACK: // addr not used, abbort |
213 | {
|
214 | #ifdef I2C_DEBUG
|
215 | uartPuts("TWI_MT_SLA_NACK,"); |
216 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
217 | uartPut('\r'); |
218 | #endif
|
219 | i2cStatus.txPos = 0; |
220 | //TWCR = _BV(TWINT)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);
|
221 | sendI2CStop(); |
222 | break; |
223 | }
|
224 | case TW_SR_SLA_ACK : |
225 | {
|
226 | #ifdef I2C_DEBUG
|
227 | uartPuts("TW_SR_SLA_ACK,"); |
228 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
229 | uartPut('\r'); |
230 | #endif
|
231 | char c = TWDR; |
232 | uartPut(c); |
233 | uartPut('\r'); |
234 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
235 | break; |
236 | }
|
237 | case TW_SR_DATA_ACK: |
238 | {
|
239 | #ifdef I2C_DEBUG
|
240 | uartPuts("TW_SR_DATA_ACK,"); |
241 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
242 | uartPut('\r'); |
243 | #endif
|
244 | char c = TWDR; |
245 | uartPut(c); |
246 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
247 | break; |
248 | }
|
249 | case TW_SR_STOP: |
250 | {
|
251 | #ifdef I2C_DEBUG
|
252 | uartPuts("TW_SR_STOP,"); |
253 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
254 | uartPut('\r'); |
255 | #endif
|
256 | char c = TWDR; |
257 | uartPut('\r'); |
258 | TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE); |
259 | i2cStatus.status = i2c_Idle; |
260 | break; |
261 | }
|
262 | default:
|
263 | {
|
264 | #ifdef I2C_DEBUG
|
265 | uartPuts("TWI int default,"); |
266 | uartPuts(uint8ToAh(buffer, getTWIstatus)); |
267 | uartPut('\r'); |
268 | #endif
|
269 | i2cStatus.txPos = 0; |
270 | //TWCR = _BV(TWINT)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);
|
271 | sendI2CStop(); |
272 | break; |
273 | }
|
274 | |
275 | }
|
276 | |
277 | |
278 | // last clear interrupt so we free TWI
|
279 | //TWCR |= _BV(TWINT);
|
280 | }
|
Hier ein parr Sachen die mir aufgefallen sind: Wofür i2cStatus.status? Wird nur geschrieben. sendI2C prüft anhand von i2cStatus.txPos ob TWI arbeitslos ist, kann so aber NIE erkennen ob das TWI gerade im SR Modus ist. Wenn du also gerade Daten empfängst und sendI2C aufrufst geht das garantiert schief. Vorschlag: Definiere für i2cStatus.status ein i2c_rvc_data und setzt das in TW_SR_SLA_ACK. sendI2C darf nur noch senden wenn i2cStatus.status == i2c_Idle. Beachte auch das es diverse Möglichkeiten gibt den Master Status zu verlieren: 0x38, 0x68, 0x78. Du fängst aber im Moment nur 0x38 ab.
Hallo, i2cStatus.txPos dient dazu wie voll der Sendespeicher ist. Im Moment Unterstütze ich nur 1 Packet in der Queue. Daher schicke ich den avr in eine while bis das letzte Packet verschickt wurde. Will ich mal ausbauen, aber Schritt für Schritt :-) Die noch nicht abgefangenen Sonderfälle sind in Arbeit. Ich will mich da erst mal rein arbeiten. Mit dem Vorschlag für den i2cStatus.status. So hat ich das auch umgesetzt, nur in einer extra Variablen. Nur kam es trotzdem noch vor. Das ich ein Start senden will und ein Empfang anfängt. Aber ich versuche das gleich nochmal, vielleicht habe ich mich gestern auch vertan. Ich werde berichten :-) Gruss Juergen
Ich habe gestern das och modifiziert und es scheint doch zu gehen. Ich lasse zwei master sich gegenseitig Meldungen schicken, die dann auf die Serielle ausgegeben werden. Und das ganze mir richtig speed :-) Nun habe ich noch folgendes Problem: Wird der "Bus Master" während der Übertragung resetet, bleibt der Bus natürlich belegt und ist bis zu einem Reset beider Master nicht nutzbar. Der "Salve Receiver" denkt der Bus ist noch belegt. Der MT der neu gestartet wurde fängt aber auch keine Übertragung an. Ich nehme an das passiert nur wärend der Übertragung eines bytes. Starte ich beide CPUs neu läuft alles wieder prima. Wie kann man so etwas sinnvoll handhaben ? Wenn für eine gewisse Zeit nichts passiert, einfach mal ein Stop senden ??? Für Tipps wäre ich dankbar. Gruss Juergen
Freut mich das es jetzt klappt. Ich habe hier einen timeout Zähler in jedem µC laufen. Der wird bei jedem TWI-IRQ zurückgesetzt. Wenn sich für eine gewisse Zeit nichts auf dem Bus tut gibt es einen Init des TWI und der Bus ist wieder frei. Und noch so als Tipp: Bau eine CRC ein. Wenn du dann nach der CRC noch ein Dummy Byte sendest kann der Slave durch ACK/NACK dem Master mitteilen ob die CRC gepasst hat.
Hallo, wenn ich einen Fehler feststelle. Sagen wir der Bus ist für eine gewisse Zeit still und der Verdacht da ist was faul. Nun möchte ich das TWI in einen definierten Zustand bringen. Was muss ich dann machen ? Ist folgendes ausreichend ?
1 | /*
|
2 | * get us in a known state
|
3 | */
|
4 | void reinitI2C(void) |
5 | {
|
6 | i2cStatus.status = i2c_Idle; |
7 | TWCR |= _BV(TWSTO); |
8 | TWCR &= ~_BV(TWEA); |
9 | TWCR |= _BV(TWEA); |
10 | }
|
Nachdem TWEA ja auch die Hardware freigibt, bin ich mir da unsicher. Ich möchte aus einem Fehler, egal ob Master oder Slave in einen definierten Zustand (Idle) wechseln. Natürlich nur bei Fehler die Fatal sind, also zu Busausfällen führen, weil ein Slave hängt oder nicht mehr reagiert oder der Master während dem Transfer resetet/abgeschaltet wurde. Gruss Juergen
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.