Close

I2C Monitor/Sniffer, part 2

A project log for Breadboard enhancement

Project to add features to an ELV EXSB-1 breadboard.

just-me-nlJust Me NL 05/20/2021 at 17:530 Comments

Well that was a bit presumptuous: “Now to integrate this into the main program”. Yes, we can get the timing information this way, but it won’t be the correct time. I added a delay(address); to the Arduino Uno sketch to check the timing; this way each address-scan delays an additional millisecond. This way the time-information should increase with one ms each stop/start cycle. Alas this was not the case. It seems the DMA channel for the time misses triggers. Thinking some more about this it is obvious: a DMA routine to transfer 1 byte from a peripheral to SRAM takes 16 cycles of the F_BUS and transferring 4 bytes takes (probably?) 19 cycles, so both DMA request back-to-back take between 32 and 37 cycles. At 72MHz and a F_BUS of 36MHz this means that the system is at least 890ns busy with processing the DMA requests. Should be doable right? Wrong. A 400kHz I2C bus means the SCL toggles 800.000 times a second so that’s every 1250ns. Ok, so still doable right? Nope. Because although strictly speaking we only need to interrupt on the rising SCL edges, in order to dsetect start, restart and stop signalling we need to trigger on any changes of the SDA line. A rising SDA line right before a rising SCL line will lead to missed clocksignals. In order for both changes to be detected and processed they would to be more than 890ns apart and that just isn’t the case when SCL is low and a one is put on the bus.

Using interrupts I was able to capture timing information at 100kHz. Maybe with overclocking we can get the speed we need? I wrote a simple testprogram to interrupt as any change of pin 5, in the interrupt routine I set pin 6 high, processed the data and time and set pin 6 low again. On a oscilloscope we can now see how long the interrupt processing takes, from trigger to interrupt routine and how long the interruptroutine takes. At 72MHz entering the interrupt took about 900ns, the processing took another xxxns. Enough for an I2C bus @100kHz but not fast enough for 400kHz and faster. Overclocking to 144MHz reduced the needed time to 500ns and xxxns but still not fast enough if there are multiple interrupts close together. 

So, mission impossible? No, not really. We just need a way to filter out the unwanted interrupts. We are only interested in SDA changes if SCL is high, so if we convert a SDA change into a pulse and filter this so it only reaches the Teensy when SCL is high. The last part is easy: just put it through an AND-gate. The first part is tricky but doable: if we delay the SDA signal a bit, say 50ns and feed both the undelayed signal and the delayed signal into an XOR gate, we get a 50ns wide puls each time the SDA signal changes. We can make an XOR gate with 3 NAND gates and 2 inverters (or 5 NAND gates). If we use the 74HC00 and the 74HC14, we get 4 NAND gates and 6 inverters. We can use the inverters to delay the signal, just put a RC network between two inverters.

Using this “filtering circuit” we can capture signals and time information for an I2C bus @ 400kHz, with overclocking to 144MHz, we can probably get up to 1MHz.

Discussions