Ich möchte den ATMEGA als I2C Slave einsetzen. Leider habe ich bei der Entwicklung meiner Platine den I2C Bus an PD6/PD7 angeschlossen. Gibt es eine Soft-I2C-Slave Library für einen ATMEGA (32/128/644/1284) zum herunterladen? Die oft genannte Software-Lib von P. Fleury enthält nur ein I2C-Master Implementation. Oder kann ich anstatt der Pins PC0 (SCL) und PC1 (SDA) andere Anschlüsse verwenden (softwaremässig umkonfigurieren) und trotzdem Hardware-I2C benutzen?
Hallo Alois, nimm z.B. die: http://www.jtronics.de/avr-projekte/library-i2c-twi-slave.html Die 2. Frage kann ich nicht beantworten, zumindest mit dieser Lib geht das nicht. Gruß Hermann-Josef
Application Note AVR154 mit Beschreibung und Code. Hättest Du auch mit minimalstem Einsatz selbst finden können.
Alois Neumann schrieb: > Ich möchte den ATMEGA als I2C Slave einsetzen. Leider habe ich bei der > Entwicklung meiner Platine den I2C Bus an PD6/PD7 angeschlossen. > > Gibt es eine Soft-I2C-Slave Library für einen ATMEGA (32/128/644/1284) > zum herunterladen? > > Die oft genannte Software-Lib von P. Fleury enthält nur ein I2C-Master > Implementation. > > Oder kann ich anstatt der Pins PC0 (SCL) und PC1 (SDA) andere Anschlüsse > verwenden (softwaremässig umkonfigurieren) und trotzdem Hardware-I2C > benutzen? Nein - leider nicht! Jedenfalls nicht mit der empfohlenen Software, die funktioniert nur mit entsprechenden Pins, die ein Interrupt / Event werfen. Gruß
Hendrik L. hat recht. Der SDA Pin muss mit einem externen Interrupt Eingang verbunden sein. Habe mein ATMEGA1284P SCL nun mit PD7 und SDA mit PD2 verbunden. Irgendwie scheint aber mein Code nicht zu funktionieren. twislave.h
1 | #ifndef TWISLAVE_H_
|
2 | #define TWISLAVE_H_
|
3 | |
4 | #include <avr/io.h> |
5 | #include <avr/interrupt.h> |
6 | |
7 | #define SDA PD2 // Definition of pin used as SDA
|
8 | #define SCL PD7 // Definition of pin used as SCL
|
9 | |
10 | #define SLAVE_ADDRESS 0x5D // Definition of 7 bit slave address
|
11 | |
12 | #define INITIALIZE_SDA() ( PORTD &= ~ (1 << SDA) ) // Clear port
|
13 | #define READ_SDA() ( PIND & (1 << SDA) ) // Read SDA pin value
|
14 | #define READ_SCL() ( PIND & (1 << SCL) ) // Read SCL pin value
|
15 | |
16 | #define SETSDA() ( PORTD &= ~(1 << SDA) ) // Set SDA pin Tristate
|
17 | #define CLRSDA() ( PORTD |= (1 << SDA) ) // Set SDA pin pull it low
|
18 | |
19 | #define INITIALIZE_TWI_INTERRUPT() (EICRA |= (1<<ISC01)) // Sets falling edge of SDA generates interrupt
|
20 | #define ENABLE_TWI_INTERRUPT() (EIMSK |= (1<<INT0)) // Enables SDA interrupt
|
21 | #define DISABLE_TWI_INTERRUPT() (EIMSK &= ~(1<<INT0)) // Disables SDA interrupt
|
22 | #define CLEAR_TWI_INTERRUPT_FLAG() (EIFR = (1<<INTF0)) // Clears interrupt flag
|
23 | |
24 | #define TWI_SLA_REQ_W_ACK_RTD 0x60
|
25 | #define TWI_SLA_DATA_RCV_ACK_RTD 0x80
|
26 | #define TWI_SLA_DATA_RCV_NACK_RTD 0x88
|
27 | |
28 | #define TWI_SLA_REQ_R_ACK_RTD 0xA8
|
29 | #define TWI_SLA_DATA_SND_ACK_RCV 0xB8
|
30 | #define TWI_SLA_DATA_SND_NACK_RCV 0xC0
|
31 | #define TWI_SLA_LAST_DATA_SND_ACK_RCV 0xC8
|
32 | |
33 | #define TWI_SLA_REPEAT_START 0xA0
|
34 | #define TWI_SLA_STOP 0x68
|
35 | #define I2C_IDLE 0x00
|
36 | |
37 | unsigned char readI2Cslavebyte(void); |
38 | void twi_slave_init(void); |
39 | void twi_slave_enable(void); |
40 | |
41 | void twi_slave_disable(void); |
42 | void receivedata(void); |
43 | void senddata(void); |
44 | void TWI_state_machine(void); |
45 | void GetStartCondition(void); |
twislave.c
1 | #include "twislave.h" |
2 | |
3 | unsigned char incomingBuffer[8]; // Incoming buffer |
4 | unsigned char outgoingBuffer[6] = {0xAA,0xAB,0xAC,0xAD,0xAE,0xAF}; // Preloaded outgoing buffer for testing |
5 | unsigned char walker = 0; // Walks through the buffer array. |
6 | |
7 | static char TWI_SLA_TWSR = 0; // Emulates TWI module TWSR |
8 | static char TWI_SLA_TWDR = 0; // Emulates TWI module TWDR |
9 | static char TWI_SLA_TWEA = 0; // If set, ACK is sent otherwise not. Similar to TWEA bit of TWI module. |
10 | |
11 | void twi_slave_init(void) |
12 | {
|
13 | INITIALIZE_SDA(); |
14 | INITIALIZE_TWI_INTERRUPT(); |
15 | TWI_SLA_TWEA = 1; |
16 | TWI_SLA_TWSR = I2C_IDLE; |
17 | }
|
18 | |
19 | void twi_slave_enable(void) |
20 | {
|
21 | CLEAR_TWI_INTERRUPT_FLAG(); |
22 | ENABLE_TWI_INTERRUPT(); |
23 | }
|
24 | |
25 | void twi_slave_disable(void) |
26 | {
|
27 | DISABLE_TWI_INTERRUPT(); |
28 | CLEAR_TWI_INTERRUPT_FLAG(); |
29 | }
|
30 | |
31 | ISR (INT0_vect) |
32 | {
|
33 | volatile char retval; |
34 | |
35 | if(TWI_SLA_TWSR == I2C_IDLE) |
36 | {
|
37 | GetStartCondition(); |
38 | }
|
39 | |
40 | //!Call the TWi state machine
|
41 | TWI_state_machine(); |
42 | CLEAR_TWI_INTERRUPT_FLAG(); |
43 | ENABLE_TWI_INTERRUPT(); |
44 | }
|
45 | |
46 | inline unsigned char readI2Cslavebyte(void) |
47 | {
|
48 | char index = 0; |
49 | unsigned char val = 0; |
50 | char cPin = 0; |
51 | |
52 | //Let SCL go low first. MCU comes here while SCL is still high
|
53 | while(READ_SCL()); |
54 | |
55 | //!read 8 bits from master, respond with ACK.
|
56 | //!SCL could be high or low depending on CPU speed
|
57 | for(index = 0;index < 8; index++) |
58 | {
|
59 | while(!READ_SCL()); |
60 | cPin = READ_SDA(); |
61 | val = (val<<1) | cPin; |
62 | while(READ_SCL()) |
63 | {
|
64 | //if SDA changes while SCL is high, it indicates STOP or START
|
65 | if((val & 1)!= cPin) |
66 | {
|
67 | if(READ_SDA()) |
68 | TWI_SLA_TWSR = TWI_SLA_STOP; |
69 | else
|
70 | TWI_SLA_TWSR = TWI_SLA_REPEAT_START; |
71 | return 0; |
72 | //!return READ_SDA()?I2C_STOP_DETECTED:I2C_START_DETECTED;
|
73 | }
|
74 | else
|
75 | cPin = READ_SDA(); |
76 | }
|
77 | }
|
78 | |
79 | //!Send ACK, SCL is low now
|
80 | if((val & 0xFE) == (SLAVE_ADDRESS << 1)) |
81 | {
|
82 | CLRSDA(); |
83 | while(!READ_SCL()); |
84 | while(READ_SCL()); |
85 | SETSDA(); |
86 | }
|
87 | else
|
88 | {
|
89 | TWI_SLA_TWSR = I2C_IDLE; |
90 | return 0; |
91 | }
|
92 | return val; |
93 | }
|
94 | |
95 | inline void senddata(void) |
96 | {
|
97 | char index; |
98 | for(index = 0;index < 8; index++) |
99 | {
|
100 | while(READ_SCL()); |
101 | if((TWI_SLA_TWDR >> (7 - index))&1) |
102 | SETSDA(); |
103 | else
|
104 | CLRSDA(); |
105 | while(!READ_SCL()); |
106 | }
|
107 | //!See if we get ACK or NACk
|
108 | while(READ_SCL()); |
109 | |
110 | //!tristate the pin to see if ack comes or not
|
111 | SETSDA(); |
112 | |
113 | while(!READ_SCL()); |
114 | if(!READ_SDA()) |
115 | TWI_SLA_TWSR = TWI_SLA_DATA_SND_ACK_RCV; |
116 | else
|
117 | TWI_SLA_TWSR = TWI_SLA_DATA_SND_NACK_RCV; |
118 | }
|
119 | |
120 | inline void receivedata(void) |
121 | {
|
122 | char index; |
123 | TWI_SLA_TWDR = 0; |
124 | |
125 | //!read 8 bits from master, respond with ACK.
|
126 | //!SCL could be high or low depending on CPU speed
|
127 | for(index = 0;index < 8; index++) |
128 | {
|
129 | while(!READ_SCL()); |
130 | TWI_SLA_TWDR = (TWI_SLA_TWDR<<1) | READ_SDA(); |
131 | while(READ_SCL()) |
132 | {
|
133 | //!if SDA changes while SCL is high, it indicates STOP or START
|
134 | if((TWI_SLA_TWDR & 1)!= READ_SDA()) |
135 | {
|
136 | if(READ_SDA()) |
137 | TWI_SLA_TWSR = TWI_SLA_STOP; |
138 | else
|
139 | TWI_SLA_TWSR = TWI_SLA_REPEAT_START; |
140 | return; |
141 | }
|
142 | }
|
143 | }
|
144 | |
145 | if(TWI_SLA_TWEA) |
146 | {
|
147 | //!Send ACK, SCL is low now
|
148 | CLRSDA(); |
149 | while(!READ_SCL()); |
150 | while(READ_SCL()); |
151 | SETSDA(); |
152 | TWI_SLA_TWSR = TWI_SLA_DATA_RCV_ACK_RTD; |
153 | TWI_SLA_TWEA = 0; |
154 | }
|
155 | else
|
156 | {
|
157 | TWI_SLA_TWSR = TWI_SLA_DATA_RCV_NACK_RTD; |
158 | }
|
159 | }
|
160 | |
161 | void TWI_state_machine(void) |
162 | {
|
163 | //! get current state
|
164 | START:
|
165 | //! \brief lock twi task
|
166 | switch (TWI_SLA_TWSR) |
167 | {
|
168 | /*! Own SLA_W has been received;
|
169 | *! ACK has been returned
|
170 | */
|
171 | case TWI_SLA_REQ_W_ACK_RTD: |
172 | walker = 0; |
173 | incomingBuffer[walker++] = TWI_SLA_TWDR; |
174 | TWI_SLA_TWEA = 1; |
175 | receivedata(); |
176 | goto START; |
177 | break; |
178 | |
179 | //! data recieved, NACK has been returned
|
180 | case TWI_SLA_DATA_RCV_NACK_RTD: |
181 | TWI_SLA_TWSR = I2C_IDLE; |
182 | TWI_SLA_TWEA = 1; |
183 | break; |
184 | |
185 | //! data recieved, ack has been returned
|
186 | case TWI_SLA_DATA_RCV_ACK_RTD: |
187 | incomingBuffer[walker++] = TWI_SLA_TWDR; |
188 | TWI_SLA_TWEA = 1; |
189 | receivedata(); |
190 | goto START; |
191 | break; |
192 | |
193 | /*! Own SLA_R has been received;
|
194 | *! ACK has been returned
|
195 | */
|
196 | case TWI_SLA_REQ_R_ACK_RTD: |
197 | walker = 0; |
198 | TWI_SLA_TWDR = outgoingBuffer[walker++]; |
199 | TWI_SLA_TWEA = 1; |
200 | senddata(); |
201 | goto START; |
202 | break; |
203 | |
204 | //! data has been transmitted, ACK has been received.
|
205 | case TWI_SLA_DATA_SND_ACK_RCV: |
206 | TWI_SLA_TWDR = outgoingBuffer[walker++]; |
207 | TWI_SLA_TWEA = 1; |
208 | senddata(); |
209 | goto START; |
210 | break; |
211 | |
212 | //! last data has been transmitted, ACK has been received.
|
213 | case TWI_SLA_LAST_DATA_SND_ACK_RCV: |
214 | |
215 | //! data has been transmitted, NACK has been received.
|
216 | case TWI_SLA_DATA_SND_NACK_RCV: |
217 | TWI_SLA_TWEA = 1; |
218 | TWI_SLA_TWSR = I2C_IDLE; |
219 | break; |
220 | |
221 | //! met stop or repeat start
|
222 | case TWI_SLA_STOP: |
223 | //! return to idle state
|
224 | TWI_SLA_TWEA = 1; |
225 | TWI_SLA_TWSR = I2C_IDLE; |
226 | break; |
227 | |
228 | case TWI_SLA_REPEAT_START: |
229 | GetStartCondition(); |
230 | goto START; |
231 | |
232 | //! Idle or bus error
|
233 | case I2C_IDLE: |
234 | default:
|
235 | TWI_SLA_TWEA = 1; |
236 | break; |
237 | }
|
238 | }
|
239 | |
240 | inline void GetStartCondition(void) |
241 | {
|
242 | char retval = 0; |
243 | // Make sure it is the start by checking SCL high when SDA goes low
|
244 | if(READ_SCL()) |
245 | {
|
246 | DISABLE_TWI_INTERRUPT(); |
247 | }
|
248 | else // false trigger; exit the ISR |
249 | {
|
250 | CLEAR_TWI_INTERRUPT_FLAG(); |
251 | ENABLE_TWI_INTERRUPT(); |
252 | return; |
253 | }
|
254 | // lop for one or several start conditions before a STOP
|
255 | if(TWI_SLA_TWSR == I2C_IDLE || TWI_SLA_TWSR == TWI_SLA_REPEAT_START) |
256 | {
|
257 | retval = readI2Cslavebyte(); // read address |
258 | if(retval == 0) // STOP or other address received |
259 | {
|
260 | TWI_SLA_TWSR = I2C_IDLE; |
261 | CLEAR_TWI_INTERRUPT_FLAG(); |
262 | ENABLE_TWI_INTERRUPT(); |
263 | return; |
264 | }
|
265 | else
|
266 | {
|
267 | if(retval & 1) |
268 | TWI_SLA_TWSR = TWI_SLA_REQ_R_ACK_RTD; |
269 | else
|
270 | TWI_SLA_TWSR = TWI_SLA_REQ_W_ACK_RTD; |
271 | }
|
272 | }
|
273 | TWI_SLA_TWDR = retval; |
274 | }
|
>Irgendwie scheint aber mein Code nicht zu funktionieren. Super Beschreibung. Da freut sich natürlich jeder dir zu helfen. Was funktioniert nicht? Wo hängt es? Exakte Angaben sind erwünscht. >Ich möchte den ATMEGA als I2C Slave einsetzen. Leider habe ich bei der >Entwicklung meiner Platine den I2C Bus an PD6/PD7 angeschlossen. Das lässt sich mit ein paar mal durchtrennen von Leitungen und einigen Fädeldrähten lösen. Das ist auf jeden Fall besser als eine Sofware I2C Krücke.
holger schrieb: >>Irgendwie scheint aber mein Code nicht zu funktionieren. > > Super Beschreibung. Da freut sich natürlich jeder dir zu helfen. > Was funktioniert nicht? Wo hängt es? Exakte Angaben sind erwünscht. Trotz der Verwendung eines externen Interrupts (INT0) scheint der oben verwendete Code nicht zu funktionieren. Die Kommunikation läuft nicht. Der Slave wird an der Adresse: 0x5D am I2C Master nicht angezeigt. >>Ich möchte den ATMEGA als I2C Slave einsetzen. Leider habe ich bei der >>Entwicklung meiner Platine den I2C Bus an PD6/PD7 angeschlossen. > > Das lässt sich mit ein paar mal durchtrennen von Leitungen und > einigen Fädeldrähten lösen. Das ist auf jeden Fall besser als > eine Sofware I2C Krücke. So habe ich es letztendlich gelöst. Habe SCL mit PC0 und SDA mit PC1 verbunden und den Code (Hardware TWI) dementsprechend angepasst. Jetzt läuft es. Es scheint wirklich keine funktionierende TWI-Slave Software-Emulation für den ATMega 32/644/1284 zu geben. Schade :(
Alois Neumann schrieb: > holger schrieb: >>>Irgendwie scheint aber mein Code nicht zu funktionieren. >> >> Super Beschreibung. Da freut sich natürlich jeder dir zu helfen. >> Was funktioniert nicht? Wo hängt es? Exakte Angaben sind erwünscht. > Trotz der Verwendung eines externen Interrupts (INT0) scheint der oben > verwendete Code nicht zu funktionieren. Die Kommunikation läuft nicht. > Der Slave wird an der Adresse: 0x5D am I2C Master nicht angezeigt. Na ja. Das ist ja immer noch keine vernünftige Beschreibung aus der man ablesen könnte, dass du weißt was du tust. Kommt der Interrupt? Wird der Startstate erkannt? Was macht die State Maschine? Du wirst doch wohl ein paar Dinge selbst überprüft haben. So funktioniert nun mal Debugging. Wenn nichts funktioniert, dann muss man rauskriegen, woran es hängt. Ist in allen Lebensbereichen in der Technik so. Du hast jetzt vom reinen Autofahrer zum Mechaniker gewechselt. Als Autofahrer ist es ok, wenn du sagst: mein Auto fährt nicht. Als Mechaniker muss es dir auffallen, dass der Motor deswegen nicht anspringt weil Wasser im Vergaser ist, weil das Auto im den Fluss gefallen ist.
Alois Neumann schrieb: ... > läuft es. > > Es scheint wirklich keine funktionierende TWI-Slave Software-Emulation > für den ATMega 32/644/1284 zu geben. Schade :( Sag mal, liest Du eigentlich die Antworten ??? http://www.jtronics.de/avr-projekte/library-i2c-twi-slave.html Diese Library funktioniert bei mir! Gruß
@Hendrik L Jo, wer des Lesens mächtig ist, ist echt im Vorteil! Sofware I2C war gesucht.
suzi schrieb: > @Hendrik L > > Jo, wer des Lesens mächtig ist, ist echt im Vorteil! > Sofware I2C war gesucht. Okay - sorry! Aber auch für die (HW-) I2C (USI) Schnittstelle brauchst Du Software. Was du suchst, ist so genannte http://de.wikipedia.org/wiki/Bitbang Software ...! Soft-I2C-Slave Lib ist ein wenig ambigiuous. Gruß
Nochmal für alle... einen TWI Slave mit Hardware TWI zu programmieren ist kein Problem. Ich suche eine Software Emulation. Einzigsten Code den ich gefunden habe der darauf abzielt sind die oben erwähnten Zeilen, die bei mir aber nicht laufen. Diese benötigen jedoch mindestens einen externen Hardware-Interrupt Eingang (INT0/INT1 oder INT2). Ansonsten habe ich in dieser Richtung nichts gefunden. Komischerweise gibt es mehrere funktionierende Quellcodes über eine TWI Master Software Emulation. Habe selbst die von Peter Fleury neben einem Hardware TWI am laufen. Aber eine TWI Slave Software Emulation... Fehlanzeige...
Alois Neumann schrieb: > Ich suche eine Software Emulation. Das geht beim AVR nunmal nicht. Bei den standard 100kHz hast Du max 4µs Zeit, um in den Interrupt zu springen und den SCL zu stretchen. Das sind nur 64 Zyklen bei 16MHz und damit hart auf knirsch. Sobald Du noch andere Interrupts benötigst, krachts (der AVR hat ja keine Interruptlevel). Wenn I2C in SW gehen soll, muß man daher mit dem I2C-Takt deutlich runter gehen, z.B. auf 10kHz oder der AVR darf nichts anderes machen, als nur I2C.
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.