Forum: Compiler & IDEs Suche Soft-I2C-Slave Lib


von Alois N. (alois)


Lesenswert?

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?

von Hermann-Josef M. (hermann-josef)


Lesenswert?

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

von amateur (Gast)


Lesenswert?

Application Note AVR154 mit Beschreibung und Code. Hättest Du auch mit 
minimalstem Einsatz selbst finden können.

von Hendrik L. (lbd)


Lesenswert?

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ß

von Alois N. (alois)


Angehängte Dateien:

Lesenswert?

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
}

von holger (Gast)


Lesenswert?

>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.

von Alois N. (alois)


Lesenswert?

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 :(

von Karl H. (kbuchegg)


Lesenswert?

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.

von Hendrik L. (lbd)


Lesenswert?

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ß

von suzi (Gast)


Lesenswert?

@Hendrik L

Jo, wer des Lesens mächtig ist, ist echt im Vorteil!
Sofware I2C war gesucht.

von Hendrik L. (lbd)


Lesenswert?

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ß

von Alois N. (alois)


Lesenswert?

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...

von Peter D. (peda)


Lesenswert?

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
Noch kein Account? Hier anmelden.