Forum: Mikrocontroller und Digitale Elektronik ATtiny841 I2C Slave Beispiel


von Zepp (Gast)


Lesenswert?

Hallo,

ich möchte mit dem ATtiny841 als I2C-Slave benutzen.
Dazu habe ich schon intensiv bei Google und Co. gesucht, bin aber nicht 
fündig geworde. Einige I2C-Slave Beispiele konnte finden, die sind aber 
für den ATtiny841 nicht geeignet.

Deshalb meine Frage:
Hat jemand ein simples Beispiel, Aplication Note oder Lib für den 
ATtiny841/441 in C/GCC, um diesen als I2C-Slave zu betreiben?

von hans (Gast)


Lesenswert?

steht alles im Datenblatt ab Seite 197

von Zepp (Gast)


Lesenswert?

Das Datenblatt hatte ich bereits gelesen, hilft mir aber nicht weiter, 
da dort kein Beispiel enthalten ist. Die trockene Beschreibung der 
Register und dessen Flags hat mir leider kein Licht aufgehen lassen, wie 
ich das in C realisieren kann.
Ich habe konkret nach einen Beispiel in C gefragt, weil genau das mir 
weiter helfen würde.

Ich Beschreibe mal das was ich mit den Tiny machen möchten etwas 
detaillierter.

Der Tiny soll als Slave an einen I2C-Bus hängen.
Wenn er vom Master mit seiner 7-Bit Adresse ein Befehls Byte empfängt,
antwortet er mit 4 Bytes.

Ist eigendlich nichts kompliziertes, aber wenn man es zum ersten mal 
macht, ist es erstmal doch ein wenig schwierig.

von Philipp K. (philipp_k59)


Lesenswert?

Da gibts irgendwo ein i2c via USI Master/Slave Document für die Tinys ;)

Gibts hier im forum auch hunderte Beispiele.

von F. F. (foldi)


Lesenswert?

http://www.atmel.com/Images/doc2565.pdf

Man sollte immer erstmal auf der Seite zum Controller unter "Documents"
schauen.
Da stehen viele allgemein gültige, aber auch spezielle auf den 
Controller zugeschnittene Dokumente.
http://www.atmel.com/devices/ATTINY841.aspx?tab=documents

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Ja, dem ATtiny841 hat Atmel wieder einen völlig anderen I2C-Controller 
verpaßt. Wär ja sonst langweilig.

Wenns keine App-Note für den gibt, siehts schlecht aus.

von Zepp (Gast)


Lesenswert?

Die ATtiny841/441 unterscheiden sich beim I2C/TWI-Bus von den anderen 
Tinys, die Register unterscheiden sich aber erheblich und nicht nur von 
der Bezeichnung her. Die 841/441 haben wohl ausschließlich einen 
Hardware I2C-Slave Bus.

Ich habe das besagte Beispiel (AVR311 
http://www.atmel.com/images/AVR311.zip) natürlich auch schon angesehen, 
es läßt sich aber nicht so auf die ATtiny841/441 übertragen. Ich habe 
dieses Beispiel sogar schon versucht mit Hilfe des Datenblatts zu 
migrieren. Das scheitert aber daran das die 841/441 von der Steuerung 
her anders sind.

von F. F. (foldi)


Lesenswert?

Peter Dannegger schrieb:
> Ja, dem ATtiny841 hat Atmel wieder einen völlig anderen I2C-Controller
> verpaßt. Wär ja sonst langweilig.
>
> Wenns keine App-Note für den gibt, siehts schlecht aus.

Auf der Seite gibt es einiges mit I2C. Hatte nur das mit dem Slave 
gepostet, weil die Frage danach kam.

von Bernd K. (prof7bit)


Lesenswert?

Die hier schon probiert?
http://www.jtronics.de/avr-projekte/library-i2c-twi-slave-usi.html

Die ging bei mir auf Anhieb, allerdings mit nem anderen Tiny als Deinem, 
aber vielleicht kannst Du sie ja passend machen, denn wenn der ATtiny841 
die selbe Art von USI-Schnittstelle hat wie die ganzen anderen 
unterstützten Controller (was ich vermute) wäre das der optimale 
Ausgangspunkt.

von Zepp (Gast)


Lesenswert?

Ja, funktoniert leider nicht steht aber auch

Zitat:
Unterstützt werden bisher folgende Controller:
ATtiny2313, ATtiny25, ATtiny45, ATtiny85, ATtiny26, ATtiny261, 
ATtiny461, ATtiny861, ATmega165, ATmega325, ATmega3250, ATmega645, 
ATmega6450, ATmega329, ATmega3290, ATmega169,ATtiny24, ATtiny44, 
ATtiny88



Bei einen ATMega hab ich diese Lib auch schon benutzt, hat auch ohne 
probleme geklappt.
Die tiny 441/881 sind wohl nicht kompatibel und werden auch nicht 
explizit unterstützt.

von Zepp (Gast)


Lesenswert?

Kleiner Fehler nicht 441/881 sondern 441/841

von Bernd K. (prof7bit)


Lesenswert?

Sehe gerade daß der 841 eine "richtige" I²C Hardware eingebaut hat, 
genauso wie die Megas. Dann müsste es doch relativ einfach sein das 
notfalls zu Fuß hinzubekommen, evtl auch mit Inspiration der 
entsprechenden libs für irgendeinen Mega?

von Zepp (Gast)


Lesenswert?

Ich habe jetzt mal mit Hilfe einiger Libs und Datenblatt diese 
Funktionsweise hergeleitet. Leider ist irgendwo noch der Wurm trin.
Sieht vieleicht jemand, wo der Fehler sein könnte?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define I2C_SLAVE_ADDRESS       0x20
5
#define I2C_DATA_INTERRUPT      0x80
6
#define I2C_BUS_COLLISION       0x08  
7
#define I2C_ADDRESS_STOP_MATCH  0x40 
8
9
volatile uint8_t command;
10
volatile uint8_t tx_buf[4];
11
volatile uint8_t tx_buf_index = 0;
12
13
14
int main(void)
15
{
16
  TWSA = I2C_SLAVE_ADDRESS;
17
  TWSD = 0xFF;
18
    
19
  TWSCRA = (1 << TWEN)   // Two-Wire Interface Enable
20
         | (1 << TWSHE)  // TWI SDA Hold Time Enable
21
         | (1 << TWASIE) // TWI Address/Stop Interrupt Enable  
22
         | (1 << TWSIE)  // TWI Stop Interrupt Enable
23
         | (1 << TWDIE); // TWI Data Interrupt Enable  
24
  
25
  sei();
26
  while(1)
27
  {
28
    if(command != 0x00)
29
    {
30
        switch(command)    
31
        {
32
    case 0x01:
33
            // Test Daten 
34
            tx_buf[0] = 0x01;
35
            tx_buf[1] = 0x02;
36
            tx_buf[2] = 0x03;
37
            tx_buf[3] = 0x04;  
38
            tx_buf_index = 0;
39
            break;        
40
        }
41
      
42
        command = 0x00;
43
    }
44
  }
45
}
46
47
ISR( TWI_SLAVE_vect )
48
{  
49
  uint8_t status = TWSSRA & 0xC0;
50
  
51
  if (status & I2C_DATA_INTERRUPT) // Daten wurden vom Master empfangen oder werden angefordert
52
  {      
53
    if (TWSSRA & (1 << TWDIR)) // Master fordert Daten vom Slave an
54
    {
55
      if(tx_buf_index >= sizeof(tx_buf))
56
      {
57
        tx_buf_index=0;
58
      }
59
    
60
      TWSD = tx_buf[tx_buf_index];
61
      tx_buf_index++;
62
    
63
      TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
64
    }
65
    else // Master sendet Daten zum Slave
66
    {
67
       TWSCRB |= (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
68
       command = TWSD;
69
    }
70
  }
71
  else if (status & TWI_ADDRESS_STOP_MATCH) 
72
  {    
73
    if (TWSSRA & TWI_BUS_COLLISION) 
74
    {
75
      TWSCRB = (uint8_t) (1<<TWCMD1);
76
    } 
77
    else 
78
    {    
79
      if (TWSSRA & (1<<TWAS)) 
80
      {
81
        // ACK 
82
        TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
83
      }  
84
      else 
85
      {
86
        // Stop Condition      
87
        TWSCRB = (uint8_t) (1<<TWCMD1);
88
      }
89
    }
90
  }    
91
}

von Harald (Gast)


Lesenswert?

Kannst Du beschreiben, was nicht geht? Bis wo funktioniert es noch?

von Zepp (Gast)


Lesenswert?

Ich habe einen Master Mikcrocontroller der das Byte 0x01 senden und vier 
Bytes läd. Ich erhalte nicht 0x01 0x02 0x03 0x04 sondern 0xFF  0xFF 0xFF 
0xFF.
Da an diesen Master noch ein weiterer I2C-Sensor Angeschlossen ist, der 
auch funktioniert. Denke ich das der Master korrekt arbeitet.
Ich habe leider kein Oszi, sonst hätte ich mir die Pegel angesehen.

von Harald (Gast)


Lesenswert?

Oha, du brauchst wenigstens (oder besser noch) ein Logic Analyzer, sonst 
wird das schwierig. Wie wäre es damit:

Beitrag "10 Euro Logikanalyzer"

Hilfreich wäre es noch wenn Du die genaue Abfolge, die Du per Master 
sendest noch einmal aufschreiben könntest. Mit "Senden" und "Laden" kann 
man als Außenstehender nicht viel anfangen. Also z.B. so

I2C-Start, Adr. 01, I2C-Restart, usw...

von Karl H. (kbuchegg)


Lesenswert?

Zepp schrieb:
> Ich habe einen Master Mikcrocontroller der das Byte 0x01 senden und vier
> Bytes läd. Ich erhalte nicht 0x01 0x02 0x03 0x04 sondern 0xFF  0xFF 0xFF
> 0xFF.
> Da an diesen Master noch ein weiterer I2C-Sensor Angeschlossen ist, der
> auch funktioniert. Denke ich das der Master korrekt arbeitet.
> Ich habe leider kein Oszi, sonst hätte ich mir die Pegel angesehen.

Ich würde mir an deiner Stelle mal an den Tiny das Oszi des kleinen 
Mannes ankabeln: ein paar LED.

Denn im Tiny stocherst du im Nebel und das muss sich ändern.
Du willst zb wissen, ob der Interrupt überhaupt ausgelöst wird. Eine LED 
kann dir das verraten. Du willst wissen, ob du einen Dateninterrupt 
kriegst. Wieder: Eine LED kann dir das verraten.

Ja, ich weiß. Es ist mühsam. Aber wenn man nichts anderes zum Debuggen 
hat, dann muss das eben reichen.

von Zepp (Gast)


Lesenswert?

Der Master hat folgende Funktion die ich so aufrufe:
1
uint32_t result = Tiny841_Read(0x01);

Das Ergebnis zeige ich mir dann noch auf den LCD an.
1
uint32_t Tiny841_Read(uint8_t command)
2
{
3
  int32_t returnValue;
4
  
5
  i2c_Start();
6
  i2c_put(I2C_SLAVE_ADDRESS);        // I2C-Adresse und 0 für Write
7
  i2c_put(command);                    // Befehlscode
8
  i2c_Stop();
9
  
10
  i2c_Start();
11
  i2c_put(I2C_SLAVE_ADDRESS | 1);   // I2C-Adresse und 1 für Read
12
  returnValue =  i2c_get_ack()<<24;
13
  returnValue |= i2c_get_ack()<<16;
14
  returnValue |= i2c_get_ack()<<8;
15
  returnValue |= i2c_get();
16
  i2c_Stop();
17
  
18
  return returnValue;
19
}

von Harald (Gast)


Lesenswert?

Da hat Karl Heinz absolut recht. Hast Du vielleicht sonstige 
Debug-Möglichkeiten, z.B. AVR Dragon mit Single-Wire Debug? Oder ist das 
zum 841 noch nicht kompatibel?

von Zepp (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ich würde mir an deiner Stelle mal an den Tiny das Oszi des kleinen
> Mannes ankabeln: ein paar LED.
>
> Denn im Tiny stocherst du im Nebel und das muss sich ändern.
> Du willst zb wissen, ob der Interrupt überhaupt ausgelöst wird. Eine LED
> kann dir das verraten. Du willst wissen, ob du einen Dateninterrupt
> kriegst. Wieder: Eine LED kann dir das verraten.
>
> Ja, ich weiß. Es ist mühsam. Aber wenn man nichts anderes zum Debuggen
> hat, dann muss das eben reichen.

Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe 
Sensoren belegt. Der Tiny hat ja nur wenige Pins.

von Zepp (Gast)


Lesenswert?

Harald schrieb:
> Da hat Karl Heinz absolut recht. Hast Du vielleicht sonstige
> Debug-Möglichkeiten, z.B. AVR Dragon mit Single-Wire Debug? Oder ist das
> zum 841 noch nicht kompatibel?

Zur Zeit habe ich nur den AVR ISP MKII, ich lege mir aber bald einen 
Dragon zu.
Ich werde mir mal von einen Bekannten ein Oszi leihen gehen.

von Zepp (Gast)


Lesenswert?

Harald schrieb:
> Oha, du brauchst wenigstens (oder besser noch) ein Logic Analyzer, sonst
> wird das schwierig. Wie wäre es damit:
>
> Beitrag "10 Euro Logikanalyzer"
>
> Hilfreich wäre es noch wenn Du die genaue Abfolge, die Du per Master
> sendest noch einmal aufschreiben könntest. Mit "Senden" und "Laden" kann
> man als Außenstehender nicht viel anfangen. Also z.B. so
>
> I2C-Start, Adr. 01, I2C-Restart, usw...

Der Logikanalyzer sieht für den Preis wirklich sehr gut aus.

von Harald (Gast)


Lesenswert?

Mach das mal mit der LED berichte, ob Du überhaupt in den Interrupt 
kommst. Und hast Du mal mit einem anderen Teilnehmer getestet, ob Dein 
Master überhaupt korrekt arbeitet?

von Zepp (Gast)


Lesenswert?

Harald schrieb:
> Mach das mal mit der LED berichte, ob Du überhaupt in den Interrupt
> kommst. Und hast Du mal mit einem anderen Teilnehmer getestet, ob Dein
> Master überhaupt korrekt arbeitet?

Der Master arbeitet, ich habe da noch einen Temperatur Sensor und der 
liefert korrekte Werte. Ich werde mir ein Ozi organisieren und irgenwie 
eine LED tran basteln.

von Karl H. (kbuchegg)


Lesenswert?

Zepp schrieb:

> Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe
> Sensoren belegt. Der Tiny hat ja nur wenige Pins.

Tja.
Du hast 2 Möglichkeiten.

Entweder du verschaffst dir eine Möglichkeit, wie du dem Tiny bei der 
Arbeit zusehen kannst um rauszufinden was da abgeht.
Oder du stellst dich auf den Standpunkt "dich trifft keine Schuld, du 
tust nichts und es geht eben nicht".

Nichts gegen ein Oszi. Aber was wirst du am Bus sehen? das der Tiny 
keinen Acknowledge bringt. Das kann ich dir auch so sagen. Dazu brauchst 
du kein Oszi.

: Bearbeitet durch User
von Zepp (Gast)


Lesenswert?

Karl Heinz schrieb:
> Zepp schrieb:
>
>> Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe
>> Sensoren belegt. Der Tiny hat ja nur wenige Pins.
>
> Tja.
> Du hast 2 Möglichkeiten.
>
> Entweder du verschaffst dir eine Möglichkeit, wie du dem Tiny bei der
> Arbeit zusehen kannst um rauszufinden was da abgeht.
> Oder du stellst dich auf den Standpunkt "dich trifft keine Schuld, du
> tust nichts und es geht eben nicht".
>
> Nichts gegen ein Oszi. Aber was wirst du am Bus sehen? das der Tiny
> keinen Acknowledge bringt. Das kann ich dir auch so sagen. Dazu brauchst
> du kein Oszi.

Mein Problem dabei war halt, das ist schon alles fertig auf einer 
Platine aufgelötet. Ich werde jetzt einen der Sensoren wieder heruter 
löten, um an dessen Pads ein Paar LEDs tran machen können.
Ich war natürlich erstmal ein wenig gehemmt, da wieder Bauteile heruter 
zu löten. Aber da hab ich wohl keine andere Wahl.

von F. F. (foldi)


Lesenswert?

Löw Current einfach mit anlöten.

von Zepp (Gast)


Lesenswert?

Ich habe es jetzt geschafft zwei LEDs anzubringen.
In der Interrupt Funktion sollte jetzt LED1 beim empfangen des Befehls 
Byte eingeschaltet werden und LED2 beim senden der 4 Antwort Bytes.
Mein Master hat ein LCD-Bildschirm und Tatsatur, deshalb kann ich per 
Tastendruck steuern wann der Master eine Anfrage zum Slave sendet.

Das Ergebnis ist LED2 leuchtet und LED1 nicht.
d.h das Befehls Byte (im Code ##1##) wurde nicht erkannt, aber warum?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define I2C_SLAVE_ADDRESS       0x20
5
#define I2C_DATA_INTERRUPT      0x80
6
#define I2C_BUS_COLLISION       0x08  
7
#define I2C_ADDRESS_STOP_MATCH  0x40 
8
9
volatile uint8_t command;
10
volatile uint8_t tx_buf[4];
11
volatile uint8_t tx_buf_index = 0;
12
13
volatile uint32_t debug_counter = 0;
14
#define DEBUG0_ON() (PORTA &= ~(1 << 0))
15
#define DEBUG0_OFF() (PORTA |= (1 << 0))
16
#define DEBUG1_ON() (PORTA &= ~(1 << 1))
17
#define DEBUG1_OFF() (PORTA |= (1 << 1))
18
19
20
int main(void)
21
{
22
  TWSA = I2C_SLAVE_ADDRESS;
23
  TWSD = 0xFF;
24
    
25
  TWSCRA = (1 << TWEN)   // Two-Wire Interface Enable
26
         | (1 << TWSHE)  // TWI SDA Hold Time Enable
27
         | (1 << TWASIE) // TWI Address/Stop Interrupt Enable  
28
         | (1 << TWSIE)  // TWI Stop Interrupt Enable
29
         | (1 << TWDIE); // TWI Data Interrupt Enable  
30
  
31
  // DEBUG - Definiert die Pins als Ausgänge
32
  DDRA |= (1 << 0) | (1 << 1);
33
  DEBUG0_OFF();
34
  DEBUG1_OFF();
35
36
  sei();
37
  while(1)
38
  {
39
    // Schaltet die LED nach kurzer Zeit wieder ab
40
    if(debug_counter++ >= 200000)
41
    {
42
      DEBUG0_OFF();
43
      DEBUG1_OFF();
44
      debug_counter = 0;
45
    }    
46
47
    if(command != 0x00)
48
    {
49
        switch(command)    
50
        {
51
    case 0x01:
52
            // Test Daten 
53
            tx_buf[0] = 0x01;
54
            tx_buf[1] = 0x02;
55
            tx_buf[2] = 0x03;
56
            tx_buf[3] = 0x04;  
57
            tx_buf_index = 0;
58
            break;        
59
        }
60
      
61
        command = 0x00;
62
    }
63
  }
64
}
65
66
ISR( TWI_SLAVE_vect )
67
{  
68
  uint8_t status = TWSSRA & 0xC0;
69
  
70
  if (status & I2C_DATA_INTERRUPT) // Daten wurden vom Master empfangen oder werden angefordert
71
  {      
72
    if (TWSSRA & (1 << TWDIR)) // Master fordert Daten vom Slave an
73
    {
74
      if(tx_buf_index >= sizeof(tx_buf))
75
      {
76
        tx_buf_index=0;
77
      }
78
    
79
      TWSD = tx_buf[tx_buf_index];
80
      tx_buf_index++;
81
    
82
      TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
83
84
      //*************************
85
      DEBUG1_ON();
86
      debug_counter = 0;
87
      //*************************
88
    }
89
    else // Master sendet Daten zum Slave
90
    {
91
       // ##1##
92
93
       TWSCRB |= (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
94
       command = TWSD;
95
96
      //*************************
97
      DEBUG0_ON();
98
      debug_counter = 0;
99
      //*************************
100
    }
101
  }
102
  else if (status & I2C_ADDRESS_STOP_MATCH) 
103
  {    
104
    if (TWSSRA & I2C_BUS_COLLISION) 
105
    {
106
      TWSCRB = (uint8_t) (1<<TWCMD1);
107
    } 
108
    else 
109
    {    
110
      if (TWSSRA & (1<<TWAS)) 
111
      {
112
        // ACK 
113
        TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));
114
      }  
115
      else 
116
      {
117
        // Stop Condition      
118
        TWSCRB = (uint8_t) (1<<TWCMD1);
119
      }
120
    }
121
  }    
122
}

von Zepp (Gast)


Lesenswert?

Das Problem habe ich jetzt gelöst, der Master war nur ein bischen zu 
schnell.
Ich habe beim Master, die Übertragunggeschwindigkeit ein bischen 
reduziert und daraufhin klappt jetzt die Kommunikation zwischen Master 
und Slave.

Ich danke allen, für die Hilfe und Tipps.

Das könnte sogar als Muster Beispiel für den ATtiny441/841 taugen,
ist aufs nötigste beschrängt und deshalb auch gut überschaubar.

von Zepp (Gast)


Lesenswert?

Hatte noch was vergessen,
allen ein frohen neues Jahr :)

von Harald (Gast)


Lesenswert?

Hallo Zepp,
Auch Dir ein frohes neues Jahr!

Danke für den Code, für dieses neue I2C Modul ist die Anzahl an 
verfügbarem Code ja noch recht dünn.

Hast Du die Frequenz gesenkt oder die Zeit zwischen den Bytes erhöht? 
Was hattest Du vorher und was nachher? Falls die Frequenz viel zu hoch 
war könnten ja ein rein elektrisches Problem die Ursache gewesen sein.

von Zepp (Gast)


Lesenswert?

Hallo Harald,
ich habe die Zeit zwischen den Bytes von 2µs auf 20µs erhöht.

von Bernd K. (prof7bit)


Lesenswert?

Sollte der Slave den Master nicht eigentlich selbst auf das gerade eben 
Nötige herunterbremsen können? Bei der lib für die anderen ATTiny 
funktioniert das bei mir, da kann ich 400kHz takten und dann schön das 
Wirken des Clock-Stretching auf dem Oszilloskop beobachten.

: Bearbeitet durch User
von Zepp (Gast)


Lesenswert?

Bernd K. schrieb:
> Sollte der Slave den Master nicht eigentlich selbst auf das gerade
> eben
> Nötige herunterbremsen können? Bei der lib für die anderen ATTiny
> funktioniert das bei mir, da kann ich 400kHz takten und dann schön das
> Wirken des Clock-Stretching auf dem Oszilloskop beobachten.

Mein Master hat eine Software I2C Schnittstelle, der Slave hingegen eine 
Hardware I2C-Schnittstelle.
Ich gehe davon aus das dieses Beispiel noch Verbesserungspotenzial hat, 
es ist aber auf jeden Fall schon mal eine funktionierende Grundlage.

Im Datenblatt ist z.B. beschieben, dass man via Flag zwischen Ack/Nack 
umschalten kann. Register TWSCRB (TWI Slave Control Register B) Flag 
TWAA (TWI Acknowledge Action). Ich habe dieses Flag nicht benutzt, da 
bei mir keine Probleme daraus entstanden sind. Wenn man aber zwei 
Hardware I2C-Schnittstellen hat, könnte dieses möglicherweise 
erforderlich sein.

Wobei die Stop-Condition das problem mit dem fehlenden Nack eigendlich 
beheben sollte.

von Harald A. (embedded)


Lesenswert?

Was mich noch etwas stutzig macht ist die Lösung mit der reinen 
Zeitverzögerung zwischen den Bytes. Ob nun 2µs oder 20µs sollte 
normalerweise keine Rolle spielen, so schnell ist der Controller ja 
allemal. Genauere Analyse geht wahrscheinlich nur per Scope bzw. LA.
In den nächsten Wochen brauche ich ebenfalls eine I2C Implementation auf 
einem ATTiny 841. Ich werde berichten.

von Zepp (Gast)


Lesenswert?

Harald A. schrieb:
> Was mich noch etwas stutzig macht ist die Lösung mit der reinen
> Zeitverzögerung zwischen den Bytes. Ob nun 2µs oder 20µs sollte
> normalerweise keine Rolle spielen, so schnell ist der Controller ja
> allemal. Genauere Analyse geht wahrscheinlich nur per Scope bzw. LA.
> In den nächsten Wochen brauche ich ebenfalls eine I2C Implementation auf
> einem ATTiny 841. Ich werde berichten.

Das könnte auch irgendwo am Software I2C des Masters liegen. Mit einen 
Hardware I2C-Master hätte man da sicher keine probleme. Das Bauteil ist 
bei mir in SMD und es wurde leider nicht daran gedacht ein Paar Pads 
einzufügen, damit man mal am I2C Bus mit nen Oszi schauen kann. Ich 
hatte ein Oszi da, aber habe es dann leider doch nicht benutz, weil es 
zu knifflich war mit den SMD Bauteilen. Jetzt habe ich das Oszi schon 
wieder abgegeben.  Wegen der Geschwindigkeit, beide Micocontroller 
(Msster/Slave) haben einen 14 Mhz Quwarz.
Hiermal mal ein Auszug vom Master, und was ich da geändert habe, ist nur 
der Wert in der Funktion I2CM_Delay().
Vorher habe ich 0xFFFFFFFF empfangen und nach der Änderung den 
erwarteten Testwert.
1
void i2c_delay(void)
2
{
3
  // Vorher 2
4
  _delay_us(20);
5
}
6
7
void i2c_Start(void)
8
{
9
  i2c_delay();
10
  SDA_HI(); // SDA High
11
  i2c_delay();
12
  i2c_delay();
13
  SCL_HI(); // SCL High
14
  i2c_delay();
15
  i2c_delay();
16
  SDA_LO(); // SDA Low
17
  i2c_delay();
18
  i2c_delay();
19
  SCL_LO(); // SLC Low
20
}
21
22
uint8_t i2c_get_ack(void)
23
{
24
  uint8_t i; 
25
  uint8_t returnValue = 0;
26
    
27
  i2c_delay();
28
  SDA_HI();
29
    
30
  for(i=0; i<8; i++)
31
  {
32
    i2c_delay();
33
    i2c_delay();
34
    returnValue <<= 1;
35
    SCL_HI();        
36
    i2c_delay();
37
    i2c_delay();
38
    if(SDAPIN & (1<<SDA)) 
39
      returnValue |= 1;
40
    SCL_LO();
41
  }
42
  
43
  i2c_delay();
44
45
  // Ack
46
  SDA_LO();
47
    
48
  i2c_delay();
49
  SCL_HI();
50
  i2c_delay();
51
  i2c_delay();
52
  SCL_LO();
53
54
  return returnValue;
55
}
56
57
uint8_t i2c_put(uint8_t b)
58
{
59
  uint8_t i;
60
  uint8_t ack;
61
  
62
  for (i=0;i<8;i++)
63
  {
64
    i2c_delay();
65
    if ( b & 0x80 )
66
    {
67
      SDA_HI();
68
    }
69
    else
70
    {
71
      SDA_LO();
72
    }
73
  
74
    i2c_delay();
75
    SCL_HI();
76
    i2c_delay();
77
    i2c_delay();
78
    SCL_LO();
79
    b <<= 1;
80
  }
81
82
  i2c_delay();
83
  SDA_HI();    
84
  i2c_delay();
85
  SCL_HI();    
86
    
87
  i2c_delay();
88
  ack = SDAPIN & (1<<SDA);  
89
  i2c_delay();
90
91
  SCL_LO();
92
  i2c_delay();
93
  SDA_LO();  
94
  
95
  return (ack == 0);      
96
}

von Peter D. (peda)


Lesenswert?

Zepp schrieb:
> Mein Master hat eine Software I2C Schnittstelle

Wenn Du den Quellcode des Masters hast, würde ich doch besser das 
Rücklesen der SCL-Leitung einbauen.
Die darf dann natürlich nicht mehr als push/pull konfiguriert sein, 
sondern als open-drain (wie ja SDA auch).

von Bernd K. (prof7bit)


Lesenswert?

Peter Dannegger schrieb:
> Zepp schrieb:
>> Mein Master hat eine Software I2C Schnittstelle
>
> Wenn Du den Quellcode des Masters hast, würde ich doch besser das
> Rücklesen der SCL-Leitung einbauen.
> Die darf dann natürlich nicht mehr als push/pull konfiguriert sein,
> sondern als open-drain (wie ja SDA auch).

Wie sieht denn Dein SCL_HI() Makro aus?


Hoffentlich nicht
1
DDRx &= ~SCL_BIT;

sondern stattdessen so:
1
DDRx &= ~SCL_BIT;
2
while (!(PINx & SCL_BIT));    // wait for slave to release SCL (clock stretching!)

[Edit] Sorry, ging an falschen Adressaten, Frage bleibt aber bestehen.

: Bearbeitet durch User
von Zepp (Gast)


Lesenswert?

Bernd K. schrieb:
> Wie sieht denn Dein SCL_HI() Makro aus?
>
> Hoffentlich nichtDDRx &= ~SCL_BIT;
>
> sondern stattdessen so:DDRx &= ~SCL_BIT;
> while (!(PINx & SCL_BIT));    // wait for slave to release SCL (clock
> stretching!)
>
> [Edit] Sorry, ging an falschen Adressaten, Frage bleibt aber bestehen.

Hallo Bernd K.,

der Master ist nicht von mir, ich habe den Quellcode dazu so bekommen.
Den werde ich noch überarbeiten müssen. Der aktuelle Master Code scheint 
auf die bestehnden Slave und deren Geschwindigkeit angepasst zu sein, 
das ist natürlich kein richtiges I2C.
Die Kommunikationsprobleme, sind wohl genau darauf zurück zu führen.
1
#define SDA_LO()      SDADDR |=  (1<<SDA)
2
#define SDA_HI()      SDADDR &= ~(1<<SDA)
3
4
#define SCL_LO()      SCLDDR |=  (1<<SCL) 
5
#define SCL_HI()      SCLDDR &= ~(1<<SCL)

von Bernd K. (prof7bit)


Lesenswert?

Zepp schrieb:

>
1
> #define SCL_HI()      SCLDDR &= ~(1<<SCL)
2
>

Das ist falsch! Wer immer Dir diesen Code gegeben hat hat selbst nicht 
begriffen wie I2C funktioniert.

Änder das mal zu:
1
#define SCL_HI()      SCLDDR &= ~(1<<SCL); while(!(SCLPIN & (1<<SCL)))

Obiges basiert auf der Annahme dass konsequenterweise SCLPIN das 
input-Register für diesen Pin ist wenn der Autor dieses Codes halbwegs 
konsequent war in seiner Namensgebung, notfalls entsprechend anpassen.

Wenn der Master die SCL-Leitung loslässt dann MUSS er warten bis sie 
auch tatsächlich auf high geht, denn ein Slave kann jederzeit selbst 
diese Leitung auf low ziehen um den Master zu bremsen, jederzeit und so 
lange er will. Der Master muss an diesem Punkt innehalten und darf auf 
gar keinen Fall weitermachen bevor die Leitung auf high geht.

: Bearbeitet durch User
von Zepp (Gast)


Lesenswert?

@ Bernd K.
Danke für den Hinweis, jetzt funktioniert der Master auch mit 2µs.

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.