Forum: Mikrocontroller und Digitale Elektronik I2C "passthrough" device mit Atmega328


von Olli Z. (z80freak)


Lesenswert?

Für mein Retro-TV Projekt, aber sicher auch mal für andere 
I2C-Basteleien geeignet, möchte ich mir ein "Zwischenschaltgerät" für 
ein am I2C Bus bauen. Es soll vor einen zu untersuchenden Baustein in 
die SDA/SCL Leitung eingefügt werden, die Kommunikation durchleiten aber 
zusätzlich auf eine serielle Konsole ausgeben. Das Device muss also 
gleichzeitig als Slave und einmal als Master arbeiten. Fast-Mode ist 
erstmal nicht notwendig.

Also im Grunde klingt die Aufgabe trivial, aber bei genauerer 
Betrachtung ist sie das nicht.

Zum üben habe ich mir einen meiner Arduino Nanos mit dem Atmega328 
geschnappt und losgelegt, bin aber schon bald auf Probleme gestoßen für 
die ich so keine Lösung hatte.

Problem 1: "Nur ein TWI Port im Atmega328"
Der Atmega328p hat nur einen I2C (TWI) Controller, den anderen I2C Port 
muss ich per Software (Bigbang) emulieren. Da schien es mir ratsam die 
Slave-Emulation auf den HW-Port zu nehmen, da ich so per Interrupt auf 
die Sendung des Master reagieren kann und nicht immer einen Pin pollen 
muss.

Problem 2: "Timing"
Ich glaube ein Grund warum das bei mir nicht funktionierte war das 
Timing. Das ist selbst bei nur 50kHz Takt schon kritisch per Software. 
Wenn ich ein Byte vom Master empfange, dann muss ich dieses ja zum Slave 
senden und seine Antwort abwarten, welche ich dann zum Master schicke. 
Das dauert sehr viel länger als wenn Master/Slave direkt verbunden sind. 
Ich müsste also sowas wie ein Clock-Streching durchführen können um den 
Master hinzuhalten. Dazu habe ich keine Funktion in den Libs gefunden 
die ich mir so angesehen habe.

Problem 3: "NACK senden"
Wenn mein Device vom Master ein Byte empfängt auf das der Slave beim 
Empfang aber mit NACK antworten würde, dann habe ich dem Master aber 
bereits ein ACK gesendet. Ich müsste also, bevor ich dem Master ein 
ACK/NACK sende, das Byte zum Slave schicken und abwarten was er 
antwortet. Das führte mich wieder zu Problem 2

Gibt es Libs die diese Low_level Funktionalitäten unterstützen oder muss 
ich mir da was selbst machen?
Mit diesen Libs habe ich bereits rumexperimentiert:
- Standard <Wire.h>
- 
https://travis-ci.com/Seeed-Studio/Arduino_Software_I2C.svg?branch=maste
- https://github.com/bitbank2/BitBang_I2C

von Kevin M. (arduinolover)


Lesenswert?

Warum willst du die Nachricht durchschleifen? Monitor doch einfach die 
Pegel der Leitungen und gib das aus.

von Wolfgang (Gast)


Lesenswert?

Olli Z. schrieb:
> Problem 2: "Timing"
> Ich glaube ein Grund warum das bei mir nicht funktionierte war das
> Timing. Das ist selbst bei nur 50kHz Takt schon kritisch per Software.

Beim I2C-Timing geht es nicht um Glauben, sondern um das Einhalten der 
Datenblätter.
I2C ist durch den Clock gesteuert, d.h. die Frequenz ist ziemlich 
unkritisch, solange sie nicht zu hoch ist.
Wenn allerdings die Daten von deinem Master schneller rein getaktet 
werden, als du sie weiterreichen kannst, brauchst du ausreichend Puffer.
Häng einen LA dran und gucke, was schief läuft.

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


Lesenswert?

Eine Hälfte würde ich mit der I2C Library von Peter Fleury lösen - und 
zwar den Soft-I2C Modus.
Fürs Hardware I2C dann eines der vielen Beispiele, z.B. von 
Atmel/Microchip.

von Frank K. (fchk)


Lesenswert?

Olli Z. schrieb:
> Für mein Retro-TV Projekt, aber sicher auch mal für andere
> I2C-Basteleien geeignet, möchte ich mir ein "Zwischenschaltgerät" für
> ein am I2C Bus bauen. Es soll vor einen zu untersuchenden Baustein in
> die SDA/SCL Leitung eingefügt werden, die Kommunikation durchleiten aber
> zusätzlich auf eine serielle Konsole ausgeben.

Ein Logic Analyzer würde das auch machen, aber das Originalsignal nicht 
beeinflussen. Das wäre ein deutlich sinnvollerer Ansatz.

fchk

von Olli Z. (z80freak)


Lesenswert?

Kevin M. schrieb:
> Warum willst du die Nachricht durchschleifen? Monitor doch einfach die
> Pegel der Leitungen und gib das aus.

Sorry, das hätte ich gleich erwähnen sollen: Weil ich in einem zweiten 
Schritt dann nicht nur mitlauschen, sondern auch gezielt eingreifen 
möchte. Z.B. einige Bytes abändern.

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

Kevin M. schrieb:
> Warum willst du die Nachricht durchschleifen? Monitor doch einfach
> die Pegel der Leitungen und gib das aus.

Da hat Kevin absolut Recht.
Das Durchschleifen verändert das I2C Timing, Bus Hookups, etc
Du wirst viel mehr Probleme mit dem Debuggen deiner Lösung haben, als du 
dann was gescheites auf dem Bus sehen wirst.
Miss die Pegel besser mit einem Oszi oder LA. Unter Umständen hast du 
dort euch schon einen Protokollanalyzer mit drin

von Dieter W. (dds5)


Lesenswert?

Bei so einem "man in the middle" braucht es schon einige Überlegungen 
zur Realisierung.
Man muss z.B. sowohl sda wie auch scl zusätzlich zur 
Hardwareschnittstelle noch auf Portpins legen. Wie sollen sonst beim 
Lesevorgang des Masters die Daten bitweise weitergegeben werden? Der 
Lauscher sieht die Daten ja sonst erst, wenn ein komplettes Byte im 
Datenregister eingetrudelt ist.

Zudem muss man am Beginn der Sitzung die Adresse des Slaves kopieren, 
sonst werden die Daten des Masters einfach ignoriert.

Mitlesen ohne die Verbindung zu unterbrechen ist wesentlich einfacher.

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

Noch ein Problem zu oben: Ich kann das nicht auf Byte-Ebene 
durchschleifen, sondern muss immer eine ganze Kommunikation (Frame) 
einlesen, da ich sonst Transmission-End Signale falsch setze.
Also müsste ich wohl erstmal alles was der Master zum Slave senden will 
in einen internen Buffer laden (der TWI-HW Buffer dürfte nicht besonders 
groß sein) und dann das zum Slave senden.
1
#include <Wire.h>
2
#include "SoftwareI2C.h"
3
4
SoftwareI2C softwarei2c;
5
6
void setup()
7
{
8
  // Slave simulation, using internal TWI interface
9
  Wire.begin(0x45); // want 7-Bit address
10
  Wire.onReceive(irq_masterSend);
11
  Wire.onRequest(irq_masterRead); 
12
13
  // Master simulation, using Bitbang
14
  // SDA = Port "D2" (green) wh-vt
15
  // SCL = Port "D3" (blue)  bu-gy
16
  softwarei2c.begin(2, 3); // sda, scl
17
}
18
19
/**
20
 * I2C-Master has send data to slave (write)
21
 */
22
void irq_masterSend(int num_received_bytes)
23
{
24
  // read all bytes received into buffer
25
  uint8_t buffer[255];
26
  int i = 0;
27
  while(Wire.available())
28
  {
29
    uint8_t data = Wire.read();
30
    buffer[i++] = data;
31
  }
32
33
  // relay received message to TDA
34
  softwarei2c.beginTransmission(0x45);
35
  softwarei2c.write(num_received_bytes, buffer);
36
  softwarei2c.endTransmission();
37
}
38
...

Eigentlich wäre es für den I2C-Replicator doch besser die Symbole zu 
erkennen und weiterzuleiten, also z.B. START vom Master erkannt, gebe 
START zum Slave, Byte erhalten, sende Byte zum Slave, STOP erkennt, 
sende STOP, Repeated-START erkannt... usw.

von Olli Z. (z80freak)


Lesenswert?

Dieter W. schrieb:
> Bei so einem "man in the middle" braucht es schon einige Überlegungen
> zur Realisierung.

Ich würde mich mit dem "Replicator" auf jeweils eine Slave-Adresse 
beschränken. Und ja, es gibt einen Zeitversatz, ganz klar.

von Olli Z. (z80freak)


Lesenswert?

Woher kann mein Slave-Code wissen wieviele Bytes der Master denn gern 
lesen möchte? Ich müsste ja beim ersten Receive-Request vom Master 
eigentlich abwechseln selbst den Slave abfragen, dann das Byte senden, 
warten ob der Master noch eines möchte, usw.
Da wüsste ich garnicht wie man das mit der Wire-Lib macht.

von Dieter W. (dds5)


Lesenswert?

Den ganzen Schlunz erst einlesen und dann weitersenden wird sicher 
tricky.

Der originale Master müsste dann zwischen Adresse, Controlword, whatever 
zum Slave schicken und Response lesen die komplette Zeit warten, bis die 
Kommunikation zwischen Replikator und Slave stattgefunden hat. 
Vermutlich ließe sich das mit clockstretching realisieren - wenn der 
Master es denn beherrscht.

von Peter D. (peda)


Lesenswert?

Olli Z. schrieb:
> Ich müsste also sowas wie ein Clock-Streching durchführen können um den
> Master hinzuhalten. Dazu habe ich keine Funktion in den Libs gefunden
> die ich mir so angesehen habe.

Das erfolgt automatisch, solange der I2C-Interrupt nicht abgearbeitet 
wurde. Du mußt also nur den I2C-Interrupt solange disablen.

von Wolfgang (Gast)


Lesenswert?

Spannend wird es mit dem Acknowledge, wenn z.B. der echte Slave nicht da 
ist, aber der µC in der Mitte erstmal munter das Byte bestätigt.

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.