Forum: Mikrocontroller und Digitale Elektronik AVR als I2C-Slave macht Probleme


von Bert 4. (b42)


Angehängte Dateien:

Lesenswert?

Ich bin auf ein seltsames Problem gestoßen: Ich möchte, dass diese 
beiden Geräte per I2C kommunizieren:
- Raspberry Pi Zero als Master, 2 GPIOs bit-banged
- ATmega328P (3.3V) als Slave mit Hardware-I2C

Sonst hängt niemand am Bus (abgesehen vom LA). Ich habe externe 
2,4-kOhm-Widerstände nach +3.3V als Pull-ups und die internen Pull-ups 
sind auf beiden Seiten ausgeschaltet.

*Das Problem: Der AVR weigert sich sporadisch, an ihn adressierte SLA+W 
zu ACKen.* Der Fehler tritt häufig (normalerweise nach weniger als 5 
Kommunikationsversuchen), aber nicht deterministisch reproduzierbar auf. 
Mein erster Gedanke war natürlich, dass ich in der selbstgeschriebenen 
Master-Implementierung Fehler habe, aber das was mir der LA ausspuckt 
(siehe Bild) sieht m.E. ok aus.
Auf der Slave-Seite habe ich die Standard-Implementierung (AVR simuliert 
ein Register-File) genommen. Das Problem ist, dass die ganze 
AVR-Firmware mehrere Tausend Zeilen lang ist, weil der AVR noch eine 
Menge anderer Dinge zu tun hat (Timer-Interrupts, GPIO-Zeugs, PCINTs 
etc.). Bevor ich jetzt viele Stunden investiere um ein Minimalbeispiel 
zu finden, würde ich deshalb gerne ein paar allgemeinere Fragen stellen:

1.) Unter welchen Umständen kann die I2C-Hardware des AVR eine an sie 
gerichtete SLA+W-Adressierung NACKen? Ausgehend von Tabelle 22-4 und 
22-5 in der AVR-Spec habe ich zu Beginn des Hauptprogramms und in der 
ISR mit wenigen Ausnahmen* immer TWEA gesetzt. Habe ich einen 
Logikfehler in meinem Programm (s.u.)?
*) Die Ausnahmen betreffen lediglich die Statuscodes 0x80, 0xa8 und 
0xb8. Nichts davon sollte ein SLA+W beeinflussen, richtig?

2.) Angenommen der AVR ist gerade beschäftigt, z.B. mit anderen 
Interrupts oder im Idle-Sleep. Wird dann evtl. ein NACK erzeugt (bzw. 
ein ACK versäumt)? Nach meiner Lesart der Spec sollte in so einem Fall 
die I2C-Hardware des AVR den SCL-Pin low halten bis die Software Zeit 
hat sich zu kümmern, stimmt das?

3.) In der TWI-ISR (s.u.) habe ich verschiedenen Fälle in eigenen Worten 
so kommentiert wie ich die Spec interpretiere. Sind da irgendwelche 
Verständnisfehler drin?

4.) Ist das was der Master von sich gibt (siehe Bild) korrekt? Die 
bitgebangte Implementierung ist nicht besonders schnell, aber das sollte 
doch eigentlich kein Problem sein, oder? Das einzige was m.E. nicht 
100%ig der I2C-Spec entspricht, ist die etwas zu lange t_HD;DAT (data 
hold time, also die Zeit zwischen "Master zieht SCL nach einer 
Bitübertragung auf low" und "Master lässt SDA los"). Kann es daran 
liegen?

Hier ein kleiner Ausschnitt der AVR-Software. Das simulierte 
Register-File geht von 0 bis AVR_REG_MAX und i2cWriteRegister bzw. 
i2cReadRegister sind anderswo definierte Funktionen, die die Daten 
entgegennehmen bzw. liefern.
1
// currently selected register (0xff for none)
2
uint8_t    regSel = 0xff;
3
4
int main()
5
{
6
  ...
7
  PRR &= ~(1 << PRTWI);
8
  TWAR  = (AVR_I2C_ADDRESS << 1)  // set address
9
    | (0 << 0);      // don't respond to general call
10
  TWCR  = (1 << TWEA)  // acknowledge address
11
    | (1 << TWEN)  // enable TWI
12
    | (1 << TWIE);  // enable TWI interrupts
13
  ...
14
  sei();
15
  ...
16
  while(1) {...}
17
}
18
19
ISR(TWI_vect)
20
{
21
  uint8_t twcrBase = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
22
23
  if((TWSR & 0xf8) == 0x60)
24
  {
25
    // SLA+W has been received, expecting register address
26
    // which we will ACK. 
27
    regSel = 0xff;
28
    TWCR = twcrBase | (1 << TWEA);
29
  }
30
  else if(regSel == 0xff && (TWSR & 0xf8) == 0x80)
31
  {
32
    // The register address byte has been received (and ACKed)
33
    regSel = TWDR;
34
    // Note that we've already acked the transmission of this
35
    // register address byte. If the register address is invalid,
36
    // we will NACK the next received byte which contains the
37
    // actual data that the master wants to write there. 
38
    if(regSel <= AVR_REG_MAX) // check if register is valid
39
      TWCR = twcrBase | (1 << TWEA); // next written byte will be ACKed
40
    else
41
      TWCR = twcrBase; // next written byte will be NACKed
42
  }
43
  else if(regSel != 0xff && (TWSR & 0xf8) == 0x80)
44
  {
45
    // write received data to register (and perform operations)
46
    i2cWriteRegister(regSel, TWDR);
47
    regSel++;
48
    // Check if another byte would fit into the register file and
49
    // have the next received byte be ACKed (or NACKed if not). 
50
    if(regSel <= AVR_REG_MAX)
51
      TWCR = twcrBase | (1 << TWEA);
52
    else
53
      TWCR = twcrBase;
54
  }
55
  else if((TWSR & 0xf8) == 0x88)
56
  {
57
    // data has been received and NACKed
58
    // switch to non-addressed slave mode, wait for new addressing
59
    TWCR = twcrBase | (1 << TWEA);
60
  }
61
  else if((TWSR & 0xf8) == 0xa0)
62
  {
63
    // stop or restart condition occurred while still being addressed
64
    // switch to non-addressed slave mode, wait for new addressing
65
    TWCR = twcrBase | (1 << TWEA);
66
  }
67
  else if((TWSR & 0xf8) == 0xa8 || (TWSR & 0xf8) == 0xb8)
68
  {
69
    // SLA+R has been received and ACKed or
70
    // data has been sent and the master has requested more by ACKing
71
72
    // if no register has been addressed, start reading at 0x00
73
    if(regSel == 0xff)
74
      regSel = 0x00;
75
    TWDR = i2cReadRegister(regSel);
76
    regSel++;
77
    if(regSel <= AVR_REG_MAX) // are there more bytes we could send after this?
78
      TWCR = twcrBase | (1 << TWEA); // yes: if the master continues reading, we'll get another 0xb8
79
    else
80
      TWCR = twcrBase; // no: if the master continues reading, we'll get a 0xc8 and the TWI hardware will only return '1' bits
81
  }
82
  else if((TWSR & 0xf8) == 0xc0)
83
  {
84
    // data has been transmitted to the master who has
85
    // NACKed it, indicating that he wants no more data
86
87
    // switch to non-addressed slave mode, wait for new addressing
88
    regSel++;
89
    TWCR = twcrBase | (1 << TWEA);
90
  }
91
  else if((TWSR & 0xf8) == 0xc8)
92
  {
93
    // master has tried another read even though we're past the end of the register file
94
    // switch to non-addressed slave mode, wait for new addressing
95
    TWCR = twcrBase | (1 << TWEA);
96
  }
97
  else
98
    TWCR = twcrBase | (1 << TWEA) | (1 << TWSTO);
99
}

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Bitübertragung auf low" und "Master lässt SDA los"). Kann es daran
> liegen?

 Hmmm.
 Das, was ich lese und das, was dein LA als ACK dekodiert, reimt sich
 irgendwie nicht, oder ?

 Womit wir wieder beim Problem mit I2C_Adresen gelandet sind - obwohl
 das wahrscheinlich nicht die Ursache ist - Adresse ist 0x94 schreibend,
 da nur die oberen 7 bits dekodiert werden, kann es auch 0x4A sein
 - aber wie gesagt, wahrscheinlich nicht die Ursache.

 Und wer hat die Werte dann überhaupt eingetragen ( Adress, ACK) ?

: Bearbeitet durch User
von Bert 4. (b42)


Lesenswert?

Sorry, ich vergaß: AVR_I2C_ADDRESS ist per #define auf 0x4a gesetzt. 
(Steht in einem gesonderten Header-File, das sowohl vom AVR- als auch 
vom RPi-Programm eingebunden wird, daher hab ich vergessen, das in den 
Sourcecode mit reinzukopieren.)

Was meinst du mit dem ACK, das mein LA dekodiert? Vielleicht hab ich 
Tomaten auf den Augen, aber ich sehe da nichts Verkehrtes. Während des 
neunten Pulses auf SCK hält der AVR SDA low (beim ersten, erfolgreichen 
SLA+W) bzw. tut es nicht (beim zweiten, fehlgeschlagenen SLA+W). Was 
übersehe ich da?

Edit: Um meine Frage vielleicht etwas klarer auszudrücken - bei den drei 
korrekten ACKs zieht der AVR die die SDA-Leitung low noch bevor der RPi 
sie losgelassen hat (er hält sie noch low vom zuletzt gesendeten 
Null-Bit, gibt sie aber rechtzeitig frei bevor er den neunten SCK-Puls 
schickt). Ich wüsste aber nicht wie ich das zuverlässig verhindern 
sollte, denn Echtzeitkontrolle habe ich auf dem RPi nicht (bin auf 
Raspbian im Userspace). Andererseits wüsste ich aber auch nicht wieso es 
ein Problem darstellen sollte wenn sich da was überlappt. Der AVR wird 
doch wohl kaum prüfen ob SDA frei ist, bevor er ihn low zieht, oder?

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Was meinst du mit dem ACK, das mein LA dekodiert? Vielleicht hab ich
> Tomaten auf den Augen, aber ich sehe da nichts Verkehrtes. Während des

 Sorry, wahrscheinlich habe ich Tomaten auf den Augen.

> ein ACK versäumt)? Nach meiner Lesart der Spec sollte in so einem Fall
> die I2C-Hardware des AVR den SCL-Pin low halten bis die Software Zeit
> hat sich zu kümmern, stimmt das?

 Wenn erstmal (beim Slaven) TWINT gestezt wird, wird SCL solange auf
 LOW gehalten bis TWINT zurückgesetzt wird. Das ist bei dir nicht
 der Fall, also wird TWINT (wahrscheinlich) gar nicht gesetzt und die
 ISR wird nicht angesprungen.

 Was du versuchen kannst:
 Jedes empfangene Byte ausgeben.
 Jeder Eintritt in die ISR wird ausgegeben (aufsteigend, mit TWSR).
 Bei jedem Eintritt in die ISR wird (als TEST, natürlich) SCL für
 eine bestimmte Zeit auf LOW gehalten, mit LA kontrollieren.
 Vorausgesetzt, die andere Seite unterstützt clockstretching.

 Und vor allem, SCL beim Testen so weit wie nur  möglich runtersetzen.

 EDIT:
 Da anscheinend die Adresse gar nicht erst erkannt wird, vielleicht
 könntest du es mal mit einer anderen Adresse versuchen oder General
 Call erlauben und dann ständig damit versuchen ?

: Bearbeitet durch User
von Bert 4. (b42)


Angehängte Dateien:

Lesenswert?

Gute Idee, danke! Ich habe jetzt zu Beginn des ISR einen Befehl 
eingefügt, der (TWSR & 0xf8) auf den UART gibt. Ergebnis (siehe Bild): 
Ich bekomme ein 0xa0 (Stop Condition der vorherigen Transaktion), aber 
kein 0x60 (SLA+W). Du hast also recht, ich bekomme keinen Interrupt für 
das SLA+W. Bloß warum?

Mein SCL-Takt liegt dank der ineffizienten Bit-Banging-Implementierung 
des Masters bei ca. 3 kHz; ich denke das ist nicht zu schnell.

Es ist natürlich naheliegend, dass das Problem irgendwie von der 
vorangegangenen Transaktion herrührt, denn es tritt nur auf, wenn direkt 
vorher eine Schreiboperation* durchgeführt wurde. Wenn ich Master-seitig 
eine sehr lange Pause zwischen den einzelnen Registerzugriffen einfüge, 
verschwindet der Fehler. Aber das ist nicht praktikabel und sollte doch 
auch nicht nötig sein, schließlich halte ich die spezifizierte t_BUF 
(bus free time) ein.

*) Ob es auch nach Leseoperationen auftritt, kann ich ich gerade nicht 
sagen. Bisher waren es nur Schreiboperationen, aber der Fehler ist wie 
gesagt sporadisch.

Deinen Vorschlag mit dem Clock Stretching habe ich noch nicht getestet 
(kommt noch). Was genau würde mir das sagen?

Edit: Bei allen anderen Transaktionen (die, bei denen das ACK kommt) 
kommen die Daten an. Das sehe ich an den Aktionen, die der AVR auf 
seinen übrigen GPIOs tätigt.

Dein Vorschlag, mit General Call anstatt mit der normalen Adresse zu 
arbeiten, ist ein bisschen aufwändig umzusetzen. Dazu müsste ich beim 
Master den ganzen Code aufdröseln und bei der Adresse zwischen Read und 
Write unterscheiden, denn der AVR macht (sinnvollerweise) kein Slave 
Transmitter bei General Calls. Ich weiß auch gar nicht, ob die Abfolge 
"SLA+W mit GC -> Repeated Start -> SLA+R mit normaler Adresse" überhaupt 
legal ist.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Mein SCL-Takt liegt dank der ineffizienten Bit-Banging-Implementierung
> des Masters bei ca. 3 kHz; ich denke das ist nicht zu schnell.

 Nöö, wenn es noch bisschen langsamer wäre, könntest du mitschreiben...

> Deinen Vorschlag mit dem Clock Stretching habe ich noch nicht getestet
> (kommt noch). Was genau würde mir das sagen?

 Nicht viel, aber wenn der Master das auch unterstützt, kannst du mit
 Vollgas am Bus fahren und der Slave bremst in Kurven, wenn nötig ;)

> Transmitter bei General Calls. Ich weiß auch gar nicht, ob die Abfolge
> "SLA+W mit GC -> Repeated Start -> SLA+R mit normaler Adresse" überhaupt
> legal ist.

 Da bin ich mir auch nicht sicher, aber es geht nur um darum, ob die
 Adresse als solche immer erkannt wird.
 Also mit "SLA+W(GC) -> Pause -> STOP" auf ACK testen.
 Unabhängig davon würde ich auch nur "SLA+W(Adr) -> Pause -> STOP" im
 loop laufen lassen, um zu sehen, ob es so ohne Probleme funktioniert
 und die Adresse immer mit ACK bestätigt wird.

: Bearbeitet durch User
von Bert 4. (b42)


Lesenswert?

Der Master unterstützt Clock Stretching, das war einer der Gründe warum 
ich den bit-bange. Aber es hilft halt nichts, den Takt noch weiter zu 
senken, denn das Problem wird dadurch nur schwerer reproduzierbar. Wenn 
ich eine Sekunde Pause zwischen zwei Registerzugriffe setze, bekomme ich 
innerhalb vertretbarer Testzeiträume gar keine Fehler mehr.

Das mit dem General Call habe ich getestet, es ändert leider überhaupt 
nichts: Nach dem 0x80 (das jetzt natürlich ein 0x90 ist) kommt ein 0xa0 
für die Stop Condition und dann nichts mehr.

Irgendwelche Ideen? Ich bin leider zusehends am Ende mit meinem Latein.

von Bert 4. (b42)


Lesenswert?

Was mir noch auffällt (siehe das Bild in meinem Post von 15:54): Der ISR 
für die Stop Condition (0xa0) kommt relativ spät, jedenfalls im 
Vergleich zu den anderen ISR-Aufrufen (0x60, 0x80 etc.), die praktisch 
sofort erscheinen. Kann natürlich am UART liegen, aber angenommen, das 
0xa0 kommt tatsächlich mit Verspätung, würde das erklären, warum das 
folgende SLA+W übersehen wird? In dem Fall kann der AVR schließlich 
nicht den Takt verlangsamen, indem er SCL runter zieht.

von (prx) A. K. (prx)


Lesenswert?

Mich stört in den Diagrammen ein wenig, dass sich darin mitunter 
scheinbar gleichzeitig SCL und SDA ändern. Nun sind die allerdings nicht 
so weit aufgelöst, dass man daraus wirklich was entnehmen könnte. Das 
wär aber mal ein Punkt, wo du genauer reinsehen könntest.

von Carl D. (jcw2)


Lesenswert?

Bert 4. schrieb:
> Was mir noch auffällt (siehe das Bild in meinem Post von 15:54): Der ISR
> für die Stop Condition (0xa0) kommt relativ spät, jedenfalls im
> Vergleich zu den anderen ISR-Aufrufen (0x60, 0x80 etc.), die praktisch
> sofort erscheinen.

Man kann auch einfach in der ISR am Anfang/Ende mit einem PortBit 
wackeln.
Quasi ein "in ISR"-Signal. Es sind ja noch ein paar Kanäle an 
10€-LA-Pulseview frei.
Damit mache ich normalerweise "Timing-Debugging". Allerdings sollte man 
dann mit annähernd F_CPU abtasten, um das ISR-Timing gut zu sehen.

von (prx) A. K. (prx)


Lesenswert?

Ich stand auch mal vor der Frage, wie ich einen AVR an einen RPi 
anflanschen soll. Und entschied mich für die serielle Schnittstelle 
statt I2C, da der RPi als I2C-Master in Hardware ausscheidet.

Das war dann freilich ein wenig ums Eck, weil ich der Seriellen ein 
Packet Framing verpassen musste.

: Bearbeitet durch User
von Bert 4. (b42)


Lesenswert?

Da hast du recht. Leider ist mein LA ein Billigst-Teil, das den Namen 
kaum verdient und mit Ach und Krach 8 MHz schafft. Was besseres habe ich 
leider nicht.

Allerdings bin ich vorsichtig optimistisch, dass diese 
Gleichzeitigkeiten ok sind. Sie treten nämlich immer nur dann auf, wenn 
der Slave etwas mit SDA macht in Reaktion auf eine SCL-Änderung des 
Masters.

Wenn ich mir mein letztes Bild im Detail anschaue, gibt es drei Stellen, 
an denen SCL und SDA "gleichzeitig" Flanken haben:
1. Stelle: Der Master zieht nach dem Übertragen des 8. Bits (eine 1) SCL 
auf low. Der Slave (AVR) zieht dann sofort SDA auf low, in Vorbereitung 
auf den kommenden ACK-Takt.
2. und 3. Stelle: Der Master zieht SCL nach dem ACK-Takt wieder auf low. 
Daraufhin lässt der Slave SDA sofort los.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Irgendwelche Ideen? Ich bin leider zusehends am Ende mit meinem Latein.

 Als eine Möglichkeit:
 Master sendet "SLA+W(Adr)" als Ping.
 Sollte ACK empfangen werden, geht es normal weiter.
 Falls nicht, wird solange "STOP -> START -> SLA+W(Adr)" gesendet,
 bis ein ACK erfolgt.

 EDIT:
 Das passierte mir einmal auch, nur kann ich mich nicht mehr erinnern,
 was das für ein IC war - scheint also nicht unüblich zu sein.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Wenn ich mir mein letztes Bild im Detail anschaue, gibt es drei Stellen,
> an denen SCL und SDA "gleichzeitig" Flanken haben:
> 1. Stelle: Der Master zieht nach dem Übertragen des 8. Bits (eine 1) SCL
> auf low. Der Slave (AVR) zieht dann sofort SDA auf low, in Vorbereitung
> auf den kommenden ACK-Takt.
> 2. und 3. Stelle: Der Master zieht SCL nach dem ACK-Takt wieder auf low.
> Daraufhin lässt der Slave SDA sofort los.

 Ich sehe da absolut kein Problem. Zeigt nur, dass der Slave voll da
 ist und entsprechend reagiert. Vielleicht wäre es interessant wenn
 der Master damit ein Problem hätte, was aber nicht der Fall ist.

von Bert 4. (b42)


Lesenswert?

Sorry, ich hänge gerade zwei, drei Posts hinterher, weil ich nebenher 
noch teste.

Die Idee am Pin zu wackeln hatte ich auch nachdem ich das mit dem UART 
geschrieben hatte. Das habe ich jetzt gemacht und es scheint dass der 
Stop-Condition-Interrupt doch nicht verspätet kommt. Tatsächlich wird 
mir das Pin-Wackeln zu Beginn der ISR sogar schon 1,8us vor der Stop 
Condition angezeigt. Entweder habe ich Zeitreisen erfunden oder der 
Schrott-LA baut in dem Bereich ein bisschen Mist.

Den UART würde ich gerne für andere Sachen offen lassen. Im Moment dient 
er zum Debugging und später vielleicht für was anderes. Außerdem bin ich 
mir nicht sicher, ob man dem RPi beibringen kann, dass er den UART beim 
Start nicht fürs Terminal benutzt. SPI wäre noch eine Möglichkeit, aber 
ich würde ungern die Pins dafür verschwenden. Der letzte Ausweg wäre 
dann, I2C auch auf der Slave-Seite zu bit-bangen, aber das würde ich mir 
wirklich gerne ersparen. Und zu guter letzt geht's ja auch ums Lernen. 
Ich würde schon gerne wissen, warum sowas passiert. Entweder der 
Hardware-Slave-I2C des AVR hat einen Fehler (sehr unwahrscheinlich) oder 
ich mache einen fundamentalen Denkfehler und den hätte ich dann doch 
gerne behoben.

von (prx) A. K. (prx)


Lesenswert?

Bert 4. schrieb:
> Außerdem bin ich
> mir nicht sicher, ob man dem RPi beibringen kann, dass er den UART beim
> Start nicht fürs Terminal benutzt.

/boot/cmdline.txt:
  console=ttyAMA0,115200 rauswerfen
/etc/inittab:
  Zeile mit ttyAMA0 mit # auskommentieren

> SPI wäre noch eine Möglichkeit,

AVR als SPI-Slave macht keinen Spass.

: Bearbeitet durch User
von Jobst M. (jobstens-de)


Lesenswert?

Beispielschnipsel in ASM, der im Polling funktioniert.
1
; TWI Init
2
      LDI  tmp, 14      ; Slave address + no respond to general call
3
      STS  TWAR, tmp
4
      LDI  tmp, 0
5
      STS  TWAMR, tmp
6
7
      LDI  tmp, 0x44    ; TWI an und ACK senden
8
      STS  TWCR, tmp
9
10
      LDI  tmp2, 0xFF
11
12
13
; ########################
14
; Main
15
; ########################
16
17
18
19
loop:      ; some other stuff
20
21
22
warte_auf_TWINT:  LDS  tmp, TWCR
23
      SBRS  tmp, 7
24
      JMP  warte_auf_TWINT
25
26
      LDS  tmp, TWSR
27
      ANDI  tmp, 0xF8
28
      CPI  tmp, 0x60
29
      BREQ  adressed  ;  Own SLA+W has been received ACK has been returned
30
31
      LDS  tmp, TWSR
32
      ANDI  tmp, 0xF8
33
      CPI  tmp, 0x80
34
      BREQ  received  ;  Previously addressed with own SLA+W; data has been received; ACK has been returned.
35
36
      LDS  tmp, TWSR
37
      ANDI  tmp, 0xF8
38
      CPI  tmp, 0x88
39
      BREQ  received  ;  Previously addressed with own SLA+W; data has been received; NACK has been returned.
40
41
      LDS  tmp, TWSR
42
      ANDI  tmp, 0xF8
43
      CPI  tmp, 0xA0
44
      BREQ  stopped    ;  A STOP condition or repeated START condition has been received while still addressed as Slave
45
46
47
; Unknown states
48
      LDI  tmp, 128  ; reset
49
      STS  TWCR, tmp
50
      CALL  WAIT_100us
51
      LDI  tmp, 0    ; reset
52
      STS  TWCR, tmp
53
      CALL  WAIT_100us
54
      LDI  tmp, 0x44  ; TWI an und ACK senden
55
      STS  TWCR, tmp
56
      JMP  loop
57
58
      ; Wenn Startbit, Adresse empfangen und Ack gesendet (Status 0x60)
59
      ; dann mit 0x84 (Data byte will be received and NOT ACK will be returned) antworten
60
      ; dann mit 0xC4 (Data byte will be received and ACK will be returned) antworten
61
      ;  0xC4 -> TWCR - ret
62
adressed:    LDI  tmp, 0x84
63
      STS  TWCR, tmp
64
      JMP  loop
65
66
      ; Wenn Daten empfangen (Status 0x88) ohne ACK zurück
67
      ; Wenn Daten empfangen (Status 0x80) mit ACK zurück
68
      ; dann mit 0xC4  antworten (Switched to the not addressed Slave mode; own SLA will be recognized)
69
received:    LDS  tmp2, TWDR  ; Datenregister lesen
70
      LDI  tmp, 0xC4
71
      STS  TWCR, tmp
72
      ; Ab hier liegen nun die empfangenen Daten in tmp2 und können verarbeitet werden. I²C Kommunikation aber nicht behindern!
73
      JMP  loop
74
75
      ; Wenn Stop empfangen (Status 0xA0) dann mit 0xC4 antworten
76
stopped:    LDI  tmp, 0xC4
77
      STS  TWCR, tmp
78
      JMP  loop


Gruß

Jobst

von c-hater (Gast)


Lesenswert?

Bert 4. schrieb:

> Entweder der
> Hardware-Slave-I2C des AVR hat einen Fehler (sehr unwahrscheinlich)

Genau, das wäre längst schon jemandem aufgefallen.

> oder
> ich mache einen fundamentalen Denkfehler

Ich vermute: nein. Ich würde die Ursache eher beim Master vermuten, 
genauer: dessen angeblichen Support für Clock-Stretching mal 
hinterfragen. Allein davon, dass man bit-banged, ist das ja noch lange 
nicht gewährleistet. Entsprechender Code ist schwieriger korrekt zu 
implementieren, als man im ersten Moment vermuten würde, jedenfalls im 
Userspace eines Nicht-Echtzeit-Systems...

von Bert 4. (b42)


Lesenswert?

A. K. schrieb:
> /boot/cmdline.txt:
>   console=ttyAMA0,115200 rauswerfen
> /etc/inittab:
>   Zeile mit ttyAMA0 mit # auskommentieren
Danke! Behalte ich mal im Hinterkopf, könnte eines Tages noch nützlich 
sein.

> AVR als SPI-Slave macht keinen Spass.
Mist. AVR als Irgendwas-Slave scheint keinen Spaß zu machen.

Jobst M. schrieb:
> Beispielschnipsel in ASM, der im Polling funktioniert.
Danke. Das Problem ist, dass der AVR noch viele andere Dinge zu tun hat 
(auf pin changes und timer reagieren, mit anderen Geräten kommunizieren 
etc.). Ohne Interrupts fürchte ich, dass ich noch viel mehr Ärger 
bekomme. Und nach der Diskussion mit Marc glaube ich, wenn ich den 
Interrupt nicht bekomme und SCL nicht low gehalten wird, dass dann auch 
TWI nicht gesetzt sein wird.

c-hater schrieb:
> Bert 4. schrieb:
>> oder
>> ich mache einen fundamentalen Denkfehler
>
> Ich vermute: nein. Ich würde die Ursache eher beim Master vermuten,
> genauer: dessen angeblichen Support für Clock-Stretching mal
> hinterfragen. Allein davon, dass man bit-banged, ist das ja noch lange
> nicht gewährleistet. Entsprechender Code ist schwieriger korrekt zu
> implementieren, als man im ersten Moment vermuten würde, jedenfalls im
> Userspace eines Nicht-Echtzeit-Systems...

Ich kann meinen Master-Code gerne posten (müsste ihn aber vorher noch 
etwas aufräumen). Mein Eindruck war, dass ein (Single-)Master recht 
einfach zu implementieren ist, weil der Master den Takt bestimmen kann. 
(Wie ich einen Slave ohne Echtzeitkontrolle schreiben sollte, wüsste ich 
dagegen nicht.) Wenn ich die Spec richtig lese (korrigiert mich hier 
bitte ggfs.!), ist das einzige Timing, das der Master einhalten muss, 
t_VD;DAT (data valid time) und t_VD;ACK (data valid acknowledge time). 
Das hatte ich ja bereits oben angesprochen und bisher hat darin niemand 
ein großes Problem gesehen.

von Jobst M. (jobstens-de)


Lesenswert?

Bert 4. schrieb:
> Ohne Interrupts fürchte ich, dass ich noch viel mehr Ärger
> bekomme. Und nach der Diskussion mit Marc glaube ich, wenn ich den
> Interrupt nicht bekomme und SCL nicht low gehalten wird, dass dann auch
> TWI nicht gesetzt sein wird.

Die Funktion ist mit IRQ die selbe. Alle zeitkritischen Dinge werden von 
der Hardware erledigt.
Wenn also andere IRQs nicht länger als ein Byte dauern, sollte es keine 
Probleme geben.


Gruß

Jobst

von Bert 4. (b42)


Lesenswert?

Jetzt muss ich doch nochmal auf meine zweite Frage im Eingangspost 
zurückkommen. Inwieweit können andere Interrupts das TWI beeinflussen?

Ich habe probeweise alle anderen Interrupts abgedreht und konnte den 
Fehler nicht mehr reproduzieren. Ob er ganz verschwunden oder nur so 
selten geworden ist, dass ich ihn beim Testen nicht mehr treffe, ist 
schwer zu sagen.
Dann habe ich schrittweise die Interrupts wieder angestellt und versucht 
das Problem einzugrenzen. Der Übergang zwischen Fehler und kein Fehler 
scheint hier zu sein: Wenn ich in ISR(TIMER0_COMPA_vect) zu viele 
Befehle ausführe, kriege ich das Problem mit den verpassten SLA+Ws. 
Genauer gesagt bedeutet "zu viele Befehle"  Folgendes:
ISR(TIMER0_COMPA_vect) {millis++;} funktioniert.
ISR(TIMER0_COMPA_vect) {millis++; millis %= 86400000UL;} führt zum 
SLA+W-Problem.
Dabei ist millis ein (volatile) uint32_t und TIMER0_COMPA_vect tickt mit 
1kHz. Hier die Initialisierung (F_CPU=8000000):
1
PRR &= ~(1 << PRTIM0);
2
OCR0A = 125;
3
TCCR0A  = (0b00 << COM0A0)      // OC0A disconnected
4
        | (0b00 << COM0B0)      // OC0B disconnected
5
        | (0b10 << WGM00);      // CTC mode, reset on OCR0A
6
TCCR0B  = (0b0 << WGM02)
7
        | (0b011 << CS00);      // 8 MHz / 64 / 125 = 1 kHz
8
TIMSK0  |= (1 << OCIE0A);  // interrupt when reaching OCR0A

Mir ist klar, dass man in ISRs nicht zu viel machen sollte und ich weiß 
nicht genau wie viele Takte diese beiden 32-Bit-Operationen haben, aber 
so viel können es nicht sein, oder?
Abgesehen davon, warum kann ein anderer Interrupt das TWI durcheinander 
bringen? Meinem Verständnis nach (und wie Jobst oben geschrieben hat), 
sollten zeitkritische Dinge auf dem I2C-Bus - und dazu gehört doch wohl 
das ACKen eines SLA+W - von der Hardware abgehandelt werden, egal was 
das Programm gerade macht, oder nicht?

Auch spannend: Timer 1 läuft nebenher auch noch und macht PWM auf einem 
Pin, allerdings ohne ISR. Davon lässt sich das TWI (wie erwartet) 
überhaupt nicht stören.


Jobst M. schrieb:
> Wenn also andere IRQs nicht länger als ein Byte dauern, sollte es keine
> Probleme geben.
Selbst wenn, so lange der Master das Clock Stretching des AVRs 
akzeptiert, dürfen andere IRQs doch beliebig lang dauern, weil der AVR 
SCL low hält bis die TWI-ISR durchgelaufen ist. Oder nicht? Ein Problem 
würde ich erst dann sehen, wenn andere Interrupts so häufig feuern, dass 
der TWI-Interrupt mit seiner niedrigen Prio gar nicht mehr zum Zug 
kommt.

: Bearbeitet durch User
von Jobst M. (jobstens-de)


Lesenswert?

Bert 4. schrieb:
> so lange der Master das Clock Stretching des AVRs
> akzeptiert

Bert 4. schrieb:
> weil der AVR SCL low hält

Wo steht, dass der das macht?


Bert 4. schrieb:
> der TWI-Interrupt mit seiner niedrigen Prio

IRQ-Prioritäten gibt es beim AVR nicht.

Wenn ich IRQs unterbrechbar machen möchte, steht am Anfang der 
Serviceroutine SEI


Gruß

Jobst

von Draco (Gast)


Lesenswert?

Jobst M. schrieb:
> Wenn ich IRQs unterbrechbar machen möchte, steht am Anfang der
> Serviceroutine SEI

Hä?

Meinst du so?
1
ISR(Some_ISR)
2
{
3
   sei();
4
5
6
   //do something
7
}

Das ist doch völliger quark. sei und cli setzen doch bloß das I Bit im 
SREG Register, das schaltet globale Interrupts doch frei oder sperrt. 
Das I Bit wird doch nicht gelöscht sobald ne ISR aufgerufen wird?! Oder 
täusche ich mich?

von Draco (Gast)


Lesenswert?

Draco schrieb:
> Das ist doch völliger quark. sei und cli setzen doch bloß das I Bit im
> SREG Register, das schaltet globale Interrupts doch frei oder sperrt.
> Das I Bit wird doch nicht gelöscht sobald ne ISR aufgerufen wird?! Oder
> täusche ich mich?

Oh... ich korrigiere mich!
1
The I-bit is cleared by
2
hardware after an interrupt has occurred, and is set by the RETI instruction to enable subsequent interrupts.

Wusste ich nicht. Dachte das I Bit bleibt immer stehen, auch wenn ein 
Int ausgelößt wurde.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Jobst M. schrieb:
> Wo steht, dass der das macht?

 Im DaBla.

Jobst M. schrieb:
> IRQ-Prioritäten gibt es beim AVR nicht.

 Gibt es sehr wohl.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Marc V. schrieb:
> Jobst M. schrieb:
>> IRQ-Prioritäten gibt es beim AVR nicht.
>
>  Gibt es sehr wohl.

Allerdings. Grob gesagt, je niedriger die Adresse des Vektors in der ISR 
Sprungtabelle ist, desto höher die Priorität. RESET geht also vor alles 
andere.

Man kann eine ISR in avrgcc als 'ISR_NOBLOCK' deklarieren, wenn man 
weiss, was man da tut. Im Allgemeinen ist I²C aber ein synchroner Bus, 
der recht unempfindlich gegenüber anderen ISR ist.
Man darf aber z.B. nicht zulassen, das SDA den Pegel wechselt, wenn SCL 
high ist, das interpretiert jeder Slave sofort als Start oder Stop. I²C 
ist eben kein fehlerkorrierter oder störunempfindlicher Bus.

: Bearbeitet durch User
von Jobst M. (jobstens-de)


Lesenswert?

Marc V. schrieb:
> Jobst M. schrieb:
>> Wo steht, dass der das macht?
>
>  Im DaBla.

Bevor ich das geschrieben habe, habe ich dort hinein geschaut, aber 
nichts darüber gefunden.
Zeig mir bitte mal die richtige Stelle.


Matthias S. schrieb:
> Marc V. schrieb:
>> Jobst M. schrieb:
>>> IRQ-Prioritäten gibt es beim AVR nicht.
>>
>>  Gibt es sehr wohl.
>
> Allerdings. Grob gesagt, je niedriger die Adresse des Vektors in der ISR
> Sprungtabelle ist, desto höher die Priorität.

Naja. Wenn zwei IRQs gleichzeitig eintreffen, ist das richtig. Aber 
Prioritäten im Sinne A darf B unterbrechen, B aber A nicht, so wie beim 
8051, hat er nicht.


Gruß

Jobst

von Bert 4. (b42)


Lesenswert?

>> weil der AVR SCL low hält
> Wo steht, dass der das macht?
22.5.5, erster Abschnitt: "As long as the TWINT Flag is set, the SCL 
line is held low." Deswegen ist es so wichtig, bei jedem TWI-ISR das 
TWINT-Bit zu löschen.

Wenn ich das richtig verstanden habe, funktionieren 
Interrupt-Prioritäten so: Interrupts unterbrechen sich nicht, es sei 
denn man murkst explizit mit SEI oder ISR_NOBLOCK rum. Treten während 
eines ISR weitere Interrupts auf, werden diese gesammelt (wobei 
mehrfaches Auftreten desselben Interrupts nur einmal gesammelt wird). 
Nach dem reti entscheidet die Prio darüber, welcher der gesammelten 
Interrupts zuerst seinen ISR-Aufruf bekommt. Wenn Interrupts hoher 
Priorität zu schnell hintereinander auftreten, kann der ISR-Aufruf eines 
niedriger priorisierten Interrupts beliebig lange verzögert werden. 
Korrekt?

Das erklärt aber m.E. alles nicht, warum mein SLA+W nicht geACKt wird, 
schließlich sollte das in Hardware passieren.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Mir ist klar, dass man in ISRs nicht zu viel machen sollte und ich weiß
> nicht genau wie viele Takte diese beiden 32-Bit-Operationen haben, aber
> so viel können es nicht sein, oder?

 Im Prinzip uninteressant, du sollst nicht deine Interrupt Routinen
 oder Funktionen der I2C anpassen - also, dies dauert 100us, das geht
 noch, aber dies muss raus weil es langer dauert und TWI_ISR wird
 möglicherweise übersprungen...
 Eine einfache Möglichkeit dies zu umgehen, wäre:
1
 Master sendet "SLA+W(Adr)" als Ping.
2
 Sollte ACK empfangen werden, geht es normal weiter.
3
 Falls nicht, wird solange "STOP -> START -> SLA+W(Adr)" gesendet,
4
 bis ein ACK erfolgt oder eine bestimmte Anzahl von Anfragen
5
 überschritten ist.
 Dies sollte beim Master sowieso implementiert werden und wird
 dein Problem wahrscheinlich lösen.

 Wie gesagt, es kann sein, dass eine Device auf dem I2C Bus nicht
 antworten kann und NACK, bzw, gar keine Bestätigung sendet, was
 ja auch ein NACK ist.
 Und nicht alle I2C-Devices müssen Clock-Stretching unterstützen.

 I2C wird sowieso nicht für zeitkritische Aufgaben genommen, im
 Prinzip hast du da auch kein grosses Problem. So, wie von dir
 geschildert und von LA angezeigt wird die Adresse nicht als
 eigene erkannt, gilt auch für GC.
 Timing beim Master wird wahrscheinlich eingehalten, deswegen war
 auch der Vorschlag mit GC - da kann man keine bits falsch senden.

 Das einzige, was ich mir noch vorstellen kann ist, dass SDA und
 SCL unzureichende Widerstände nach VCC haben.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Jobst M. schrieb:
> Zeig mir bitte mal die richtige Stelle.
1
 Bit 7  TWINT: TWI Interrupt Flag
2
This bit is set by hardware when the TWI has finished its current job and expects application software
3
response. If the I-bit in SREG and TWIE in TWCR are set, the MCU will jump to the TWI Interrupt Vector.
4
While the TWINT Flag is set, the SCL low period is stretched. The TWINT Flag must be cleared by software
5
by writing a logic one to it.

von Bert 4. (b42)


Lesenswert?

Marc V. schrieb:
>  Im Prinzip uninteressant, du sollst nicht deine Interrupt Routinen
>  oder Funktionen der I2C anpassen - also, dies dauert 100us, das geht
>  noch, aber dies muss raus weil es langer dauert und TWI_ISR wird
>  möglicherweise übersprungen...
Ja, definitiv auch meine Meinung.

>  Eine einfache Möglichkeit dies zu umgehen, wäre:
> (...)
>  Dies sollte beim Master sowieso implementiert werden
Ist es auch. Aber ich würde gerne verstehen was da passiert, aus zwei 
Gründen: erstens einfach um etwas zu lernen und zweitens weil es sich um 
einen sporadischen Fehler handelt. Wenn ich mit irgendwelchen 
Workarounds komme, die auf der Testbank zunächst funktionieren, in 
Wirklichkeit aber die Fehlerrate nur senken ohne den Fehler ganz zu 
beheben, dann bekomme ich im Betrieb, womöglich nach Monaten, plötzlich 
unerklärliche Fehler. Ich wüsste schon gern was da kaputt ist und würde 
es idealerweise reparieren. Und wenn das Problem nur workaround-bar aber 
nicht lösbar ist, würde ich zumindest in die Doku schreiben wollen, was 
denn nun Sache ist.

>  Das einzige, was ich mir noch vorstellen kann ist, dass SDA und
>  SCL unzureichende Widerstände nach VCC haben.
2,4 kOhm nach +3.3V. Ich habe diverse andere Werte probiert, hat keinen 
Unterschied gemacht.

von Jobst M. (jobstens-de)


Lesenswert?

Marc V. schrieb:
> • Bit 7 – TWINT: TWI Interrupt Flag

??

Die richtige Stelle hat aber Bert schon gezeigt.

Bert 4. schrieb:
> 22.5.5, erster Abschnitt:


Gruß

Jobst

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Ist es auch. Aber ich würde gerne verstehen was da passiert, aus zwei
> Gründen: erstens einfach um etwas zu lernen und zweitens weil es sich um

 Mal was anderes:
 Ich sehe nirgendwo 0x60 in TWSR als Antwort, diese sollte aber als
 erste kommen ?

 Mit 0x80 bzw. 0x90 bist du schon adressiert worden, kann aber nach
 STOP nicht mehr sein, oder ?

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Jobst M. schrieb:
> ??
>
> Die richtige Stelle hat aber Bert schon gezeigt.

 Hör auf mit zumüllen.

von Bert 4. (b42)


Lesenswert?

Marc V. schrieb:
> Bert 4. schrieb:
>  Mal was anderes:
>  Ich sehe nirgendwo 0x60 in TWSR als Antwort, diese sollte aber als
>  erste kommen ?
Ja, aber irgendwo musste ich schneiden, das Bild ist ja selbst jetzt 
noch breiter als die Polizei erlaubt.
0x60 (bzw. 0x70 bei GC) kam, dann ein oder mehrere 0x80 (bzw. 0x90), 
dann das 0xa0, dann der Fehler. Auch bei Lesezugriffen kamen die 
korrekten Statuscodes.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Ja, aber irgendwo musste ich schneiden, das Bild ist ja selbst jetzt
> noch breiter als die Polizei erlaubt.

 Probiere mal TWSTO nach 0xA0 zu setzen, könnte helfen.
1
  else if((TWSR & 0xf8) == 0xa0)
2
  {
3
    TWCR = twcrBase | (1 << TWEA) | (1 << TWSTO);
4
  }

 EDIT:
 Und vielleicht in der ISR TWSR ganz am Anfang in eine locale
 Variable einlesen ?
 Reg zugriff ist bestimmt schneller, da TWSR auf Adresse 0xBC ist.

: Bearbeitet durch User
von Bert 4. (b42)


Lesenswert?

Marc V. schrieb:
>  Probiere mal TWSTO nach 0xA0 zu setzen, könnte helfen.
>  Und vielleicht in der ISR TWSR ganz am Anfang in eine locale
>  Variable einlesen ?
Ändert beides nichts. Außer natürlich, dass Leseoperationen (SLA+W -> 
Registeradresse schreiben -> Repeated Start -> SLA+R -> Registerinhalt 
lesen) nicht mehr funktionieren.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Ändert beides nichts. Außer natürlich, dass Leseoperationen (SLA+W ->

 Das mit local war ja auch nur, um schneller zu sein. Am Anfang
1
locVar = TWSR & 0xf8;
 sollte nur die if Abfrage schneller machen.

 Und jetzt bin ich mit meinem Latein auch am Ende, so etwas darf
 ganz einfach nicht passieren, wenn doch, ist die AVR Hardware nicht
 in Ordnung...

: Bearbeitet durch User
von Bert 4. (b42)


Lesenswert?

Ja, der zweite Satz bezog sich natürlich auf das TWSTO.

Danke einstweilen für den Input! Vielleicht hat sonst noch jemand eine 
Idee?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Danke einstweilen für den Input! Vielleicht hat sonst noch jemand eine
> Idee?

 a) Mit einer anderen MEGA probieren.
 b) Mit Oszi I2C-Bus beobachten.

von c-hater (Gast)


Lesenswert?

Bert 4. schrieb:

> Ich kann meinen Master-Code gerne posten (müsste ihn aber vorher noch
> etwas aufräumen).

Das wäre sicher sinnvoll.

> Mein Eindruck war, dass ein (Single-)Master recht
> einfach zu implementieren ist, weil der Master den Takt bestimmen kann.

Nein, das kann er eben nicht, jedenfalls nicht in vollem Umfang.

Genau darin besteht doch Clock-Stretching im Kern: dass der Slave in den 
Takt des Masters eingreifen, diesen also sozusagen anhalten kann. Der 
Master muss sich diesbezüglich kooperativ verhalten und in dieser 
Situation auch wirklich anhalten, sonst jibbet effektiv kein 
Clock-Stretching uuuund bumm...

von Bert 4. (b42)


Lesenswert?

Marc V. schrieb:
>  a) Mit einer anderen MEGA probieren.
Kann ich später mal machen. Aber ehrlich gesagt habe ich da nicht viel 
Hoffnung. Es kann sogar sein, dass ich da zwischenzeitlich schon mal 
einen anderen AVR drin hatte, ich hab die nicht nummeriert oder so.

>  b) Mit Oszi I2C-Bus beobachten.
Hab ich leider nicht.

c-hater schrieb:
> Bert 4. schrieb:
>
>> Ich kann meinen Master-Code gerne posten (müsste ihn aber vorher noch
>> etwas aufräumen).
>
> Das wäre sicher sinnvoll.
Wie gesagt, ich muss erst den Code ein bisschen aufräumen und die Teile 
rauswerfen, die nicht für den RPi sind.

>> Mein Eindruck war, dass ein (Single-)Master recht
>> einfach zu implementieren ist, weil der Master den Takt bestimmen kann.
>
> Nein, das kann er eben nicht, jedenfalls nicht in vollem Umfang.
>
> Genau darin besteht doch Clock-Stretching im Kern (...)
Ja, sicher, aber das macht die Sache ja nur langsamer. Wenn ich 
innerhalb von gewissen Zeiten auf etwas reagieren müsste, hätte ich ein 
Problem ohne Echtzeitkontrolle. Clock Streching heißt ja nur, dass der 
Master etwas länger nichts tun braucht. Dann geht er halt wieder 
schlafen, wendet sich anderen Tasks zu oder eiert schlimmstenfalls in 
einer Warteschleife herum. No biggie.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Bert 4. schrieb:

>> Genau darin besteht doch Clock-Stretching im Kern (...)
> Ja, sicher, aber das macht die Sache ja nur langsamer.

Natürlich, genau das ist ja der Sinn der Sache.

> Wenn ich
> innerhalb von gewissen Zeiten auf etwas reagieren müsste, hätte ich ein
> Problem ohne Echtzeitkontrolle. Clock Streching heißt ja nur, dass der
> Master etwas länger nichts tun braucht.

Genau so ist das auch.

Der Master muss halt nur im richtigen Moment fragen, ob er fortsetzen 
darf und nicht z.B. zu früh, zu einem Zeitpunkt, zu dem er das noch 
garnicht erfahren kann.

Ich bin nahezu sicher, dass du da einen fetten Bug eingebaut hast. 
Vielleicht gibt er sich ja bereits beim "Aufräumen des Codes" zu 
erkennen...

von Bert 4. (b42)


Angehängte Dateien:

Lesenswert?

c-hater schrieb:
> Der Master muss halt nur im richtigen Moment fragen, ob er fortsetzen
> darf und nicht z.B. zu früh, zu einem Zeitpunkt, zu dem er das noch
> garnicht erfahren kann.
Ich verstehe nicht ganz was du damit meinst. Was genau kann der Master 
(nicht) erfahren?

> Ich bin nahezu sicher, dass du da einen fetten Bug eingebaut hast.
> Vielleicht gibt er sich ja bereits beim "Aufräumen des Codes" zu
> erkennen...
Würde mich freuen, den zu finden. Ich bin gespannt. Siehe Anhang.

von c-hater (Gast)


Lesenswert?

Bert 4. schrieb:

> Ich verstehe nicht ganz was du damit meinst. Was genau kann der Master
> (nicht) erfahren?

Dass der Slave gesagt hat: Stop this shit for now. Was denn sonst?

Das kann man z.B. nicht erfahren, bevor es der Slave überhaupt gesagt 
hat. Das ist aber vermutlich eher nicht das Problem, eher das:

Das kann man aber auch nicht erfahren, bevor der eigene Eingang auf den 
eigenen Ausgang reagiert haben kann. Ja, das kann auch schonmal länger 
dauern, als der Code braucht, um den Ausgang zu setzen und danach den 
Eingang abzufragen. Sowas hat man selbst beim vergleichsweise 
schweinelangsamen AVR8, selbst da muss man immer mindestens einen Takt 
warten...

> Würde mich freuen, den zu finden. Ich bin gespannt. Siehe Anhang.

Heute nicht mehr. Morgen schau' ich mir das mal an.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bert 4. schrieb:
> Danke einstweilen für den Input! Vielleicht hat sonst noch jemand eine
> Idee?

 0xA0 wird noch erkannt, probiere nach Empfang von 0xA0 die anderen
 Interrupts eine Zeit lang zu sperren (ausser TWI, natürlich).
 Sollte das helfen, kannst du weitergehen und andere Interrupts einen
 nach dem anderen freigeben, um zu sehen wo es genau happert.

Bert 4. schrieb:
> Würde mich freuen, den zu finden. Ich bin gespannt. Siehe Anhang.

 Viel einfacher wäre es, eine andere MEGA anstatt RPi zu nehmen und
 es damit probieren. Sollte es klappen, beim RPi weitersuchen, sollte
 es nicht klappen, liegt der Fehler beim Slave.

von Bert 4. (b42)


Lesenswert?

c-hater schrieb:
> Bert 4. schrieb:
>
>> Ich verstehe nicht ganz was du damit meinst. Was genau kann der Master
>> (nicht) erfahren?
>
> Dass der Slave gesagt hat: Stop this shit for now. Was denn sonst?
Ok, aber wenn ich meinen LA-Bildern trauen darf, ist das nicht das 
Problem, oder?

> Das kann man aber auch nicht erfahren, bevor der eigene Eingang auf den
> eigenen Ausgang reagiert haben kann. Ja, das kann auch schonmal länger
> dauern, als der Code braucht, um den Ausgang zu setzen und danach den
> Eingang abzufragen. Sowas hat man selbst beim vergleichsweise
> schweinelangsamen AVR8, selbst da muss man immer mindestens einen Takt
> warten...
Dazu weißt ich zu wenig über den RPi (und über wiringPi). Um welche 
Zeitintervalle geht's da? Meine Schalterei auf den GPIOs bewegt sich ja 
im Bereich von dutzenden bis hunderten us.

Marc V. schrieb:
>  Viel einfacher wäre es, eine andere MEGA anstatt RPi zu nehmen und
>  es damit probieren.
Kann sein, dass ich sowas letztlich machen muss, aber bevor ich Hardware 
umstöpsle, würde ich gerne erst die anderen Fehlerursachen ausschließen.

von (prx) A. K. (prx)


Lesenswert?

c-hater schrieb:
> Das kann man aber auch nicht erfahren, bevor der eigene Eingang auf den
> eigenen Ausgang reagiert haben kann. Ja, das kann auch schonmal länger
> dauern, als der Code braucht, um den Ausgang zu setzen und danach den
> Eingang abzufragen.

Der Ablauf von clock stretching ist:
   release SCL
   wait until SCL=1
denn der Slave zeigt das an, indem er SCL auf 0 hält. Genau das findet 
sich im gezeigten Code in Form von I2CMSW_WAIT_FOR_SCL wieder.

> Sowas hat man selbst beim vergleichsweise
> schweinelangsamen AVR8, selbst da muss man immer mindestens einen Takt
> warten...

Wäre bei diesem Ablauf kein Problem.

Ist es aber schon deshalb nicht, weil der RPi die GPIO m.W. über 
Systemaufrufe steuert, d.h. da geht für jede Aktion einige Zeit ins 
Land.

von c-hater (Gast)


Lesenswert?

A. K. schrieb:

> Der Ablauf von clock stretching ist:
>    release SCL
>    wait until SCL=1
> denn der Slave zeigt das an, indem er SCL auf 0 hält. Genau das findet
> sich im gezeigten Code in Form von I2CMSW_WAIT_FOR_SCL wieder.

Nein, das stimmt schlicht nicht. In I2CMSW_WAIT_FOR_SCL macht der 
gezeigte Teil der Implementierung ausschließlich das, was in deinem 
Pseudocode der Zeile

>    wait until SCL=1

entspricht (zusätzlich bloss noch das Timeout-Gedöns für die 
stall-Erkennung). Der zeitliche Bezug zur gewollten, aber möglicherweise 
durch den Client zu verzögernden LH-Flanke geht daraus absolut nicht 
hervor. Und schon garnicht der Bezug zu der davor liegenden HL-Flanke.

Da der eigentliche Code überhaupt nicht gepostet wurde, kann ich nur 
raten, was da passiert. Aber ein educated guess dürfte sein, dass zur 
Steuerung der Abstände dieser Flanken letztlich das Macro

I2CMSW_WAIT_NS

zum Einsatz kommt. Und dessen Implementierung durch den TO scheint mir 
doch einigermassen schräg zu sein. Mal abgesehen davon, dass es sowieso 
maximal nur mit µs-Genauigkeit funktionieren kann (es wird aber 
wenigstens aufgerundet), ist die Funktionsfähigkeit der Sache daran 
gekoppelt, das das letztlich benutzte delayMicroseconds() immer wie 
erwartet funktioniert. Und darauf würde ich nicht wetten wollen. Schon 
ganz allgemein nicht und noch viel weniger unter den Bedingungen eines 
User-Tasks in einem Nicht-Echtzeitsystem.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

c-hater schrieb:
> raten, was da passiert. Aber ein educated guess dürfte sein, dass zur
> Steuerung der Abstände dieser Flanken letztlich das Macro

 Und mein educated guess ist, dass ihr am falschen Ende sucht.
 Nicht beim MASTER liegt das Problem, sondern beim Slaven.
 GCA hat auch nicht funktionert obwohl das lauter Nullen sind.

 Es wird eher sein, dass da nach dem (richtig erkanntem) STOP irgendwo
 beim Slave in TWCR die Adresserkennung ausgeschaltet wird und die
 ISR deswegen gar nicht angesprungen wird.
 Wie und wo so etwas passieren kann ist die eigentliche Frage.

von Bert 4. (b42)


Lesenswert?

c-hater schrieb:
> Da der eigentliche Code überhaupt nicht gepostet wurde
Was fehlt denn?

> (...) das Macro
> I2CMSW_WAIT_NS
> zum Einsatz kommt. Und dessen Implementierung durch den TO scheint mir
> doch einigermassen schräg zu sein.
Die "Schrägheit" kommt daher, dass diese Library generisch gehalten ist. 
Ich habe sie auf den unterschiedlichsten Plattformen verwendet und muss 
jeweils nur die I2CMSW_*-Makros anpassen. Nanosekunden scheinen mir da 
die einzig richtige Wahl, denn das ist schließlich was in der I2C-Spec 
steht.

> (...) ist die Funktionsfähigkeit der Sache daran
> gekoppelt, das das letztlich benutzte delayMicroseconds() immer wie
> erwartet funktioniert.
Nun, mit der og. Ausnahme ist die einzige Erwartung an die Funktion, 
dass sie mindestens das übergebene Zeitintervall wartet. 
delayMicroseconds ist eine WiringPi-Funktion 
(http://git.drogon.net/?p=wiringPi;a=blob;f=wiringPi/wiringPi.c;h=dfd5de484297b293f6c95978c7eba95b4d53628e;hb=HEAD#l1951) 
und wenn ich mir deren Source anschaue, sehe ich nichts, was da 
schiefgehen könnte. Ich lasse mich aber gerne korrigieren. Unter welchen 
Umständen erwartest du Probleme?

Aber selbst wenn wir einfach mal annehmen, dass die Funktion ihre 
Aufgabe nicht immer erfüllt - in meinen konkreten Beispielen tut sie das 
doch, oder nicht? Jedenfalls sind alle Timings, die ich mit dem LA 
nachgemessen habe absolut im Rahmen der Spec.

von dunno.. (Gast)


Lesenswert?

Habt ihr diesen Fehler eigentlich jemals finden können?


Ich hab hier grade 2 mega168, einer sendet als master, wird dann slave, 
bekommt eine antwort vom anderen master -

-> und schickt sporadisch kein ACK auf SLA+W, obwohl TWEA gesetzt, 
TWINT in ISR zurückgesetzt wird etc..


Fun fact: gleiche software auf anderem controller (328P) in anderer 
hardware funktioniert..

von c-hater (Gast)


Lesenswert?

dunno.. schrieb:

> Habt ihr diesen Fehler eigentlich jemals finden können?

Vermutlich hat ihn der TO damals(tm) finden können, sonst hätte er wohl 
weitere Hilferufe gesendet...

> Ich hab hier grade 2 mega168, einer sendet als master, wird dann slave,
> bekommt eine antwort vom anderen master -
>
> -> und schickt sporadisch kein ACK auf SLA+W, obwohl TWEA gesetzt,
> TWINT in ISR zurückgesetzt wird etc..

Finde den Fehler in deiner Software. Das System (also die 
TWI-Funktionalität in allen derzeit käuflichen Mega) funktioniert "as 
expected". Da kannst du einen drauf lassen. Sonst hätten wir (und viel 
bedeutendere Abnehmer) die Hardwarebasis längst zum Teufel geschickt, 
was unweigerlich dazu geführt hätte, dass MC die entweder erst garnicht 
mitgekauft oder unverzüglich eingestellt hätte. Haben sie aber nicht...

> Fun fact: gleiche software auf anderem controller (328P) in anderer
> hardware funktioniert..

Schöner Ansatz, um nach dem Fehler in der (offensichtlich ziemlich 
beschissenen) Software zu suchen, also in deinem Werk. Aber klar: mit an 
Sicherheit grenzender Wahrscheinlichkeit ist es eben nicht tatsächlich 
dein Werk, sondern zusammengeklauter, unverstandener Code...

Tja, wohl ein typischer Fall ein C&P-Droiden. Billich einzukaufender 
Menschenmüll, aber schon mittelfristig teuer, weil weitgehend nutzlos...

von dunno.. (Gast)


Lesenswert?

Um den thread nicht dumm sterben zu lassen:

Scheinbar gibt es ein problem, wenn zum zeitpunkt des sla+w das twie im 
twcr abgeschaltet wird..

Das passiert bei mir, um aus einem anderen thread her den status des i2c 
zu holen und ggfs daten zu kopieren.

Der avr verschluckt scheinbar dabei das adressbyte, sendet kein nack, 
hält aber dann scl dauerlow, ohne das per interrupt mitzuteilen.

Das ganze funktioniert problemlos, wenn ich nicht den twi interrupt, 
sondern alle interrupts per sreg sperre.

Und ja, beim zugriff auf twcr maskiere ich twint, und twen ist gesetzt.

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.