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
Warum willst du die Nachricht durchschleifen? Monitor doch einfach die Pegel der Leitungen und gib das aus.
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.
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.
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
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
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
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
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.
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.
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.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.