Sitemap
Shreya Veni

Shreya Veni

Just trying to figure life out

I2C- Part 2

6 min readJun 14, 2026

--

Yes sir, part 2 because there’s much more to the story! To be honest, I just want to expand on certain concepts and clarify stuff. Here goes!

Open Drain Architecture

Open drain architecture solves the problem that may arise when two devices try to drive the line simultaneously.

A standard push-pull output has two transistors: one pulling to VCC and one pulling to GND so it can actively drive the line both HIGH and LOW. An open drain output, however, only has the pull-to-GND transistor. When it’s off, the pin is floating i.e. connected to nothing and can’t drive the line high on its own. A shared pull-up resistor connects the line to VCC and passively pulls it high when no device is sinking current.

This gives us wired-AND logic: the bus is high if and only if every device has released it. Any device can pull it low, but since no device can actively drive it high, there’s no possibility of a short circuit, no VCC-to-GND fight between two transistors.

Multi- Master system and Arbitration

So a good thing about I2C is it can also be multi- master. This means that more than one device capable of controlling the bus can be connected to it. The controlling device is usually a microcontroller.

Press enter or click to view image in full size
Multi- Master I2C

The two masters can communicate with each other and with peripherals on the same bus. Microcontroller A can address Microcontroller B as a target and send or receive data, just like it would with any peripheral and then terminate the transfer.
The problem arises when more than one controller tries to initiate a transfer at the same time. To handle this, I2C has an arbitration procedure that relies on wired-AND. Each master continuously reads back what’s on the bus and compares it to what it sent. When one master releases SDA (sends a 1) while the other holds it low (sends a 0), the first master reads back a 0 where it expected a 1, it detects the conflict, backs off, and retries once the bus is free. The master pulling low never saw any contradiction, so it continues uninterrupted.
Importantly, no information is lost in this process for the winning master. The losing master immediately shuts off its SDA output the moment it detects the conflict, ensuring it doesn’t corrupt the winner’s data, and simply waits for the bus to be free again to retry.
There’s also something worth noting- if a master that also has a target address loses arbitration during addressing, the winning master might actually be trying to talk to it. In that case it must immediately switch to target mode and respond.
Each controller generates its own clock signal while transferring data. These can only be altered by a slow target holding SCL low (clock stretching) or by another controller during arbitration.

Clock Stretching

Right, so we saw a multi master configuration? There can be a situation where the controller and peripheral are unable to communicate properly and coordinate with each other.
The master generates SCL, but sometimes a target isn’t ready, it might still be processing the last byte or writing to internal memory. Normally this would be a problem because the master would keep clocking and the target would keep missing the data.
But because SCL is open drain, the target can hold SCL low even while the master tries to release it. The master checks whether SCL actually went high after releasing it. If it didn’t, it simply waits. Once the target is done and releases SCL, the line goes high via the pull-up resistor and communication resumes normally. Clock stretching pauses a transaction by holding the SCL line LOW. The transaction cannot continue until the line is released HIGH again.
This is clock stretching where the target is literally stretching the clock period to buy itself time.

Bus Capacitance

Bus capacitance is the total invisible capacitance of all the wires, traces, and device pins connected to the bus. Every single thing we have on our PCB adds capacitance.

Get Shreya Veni’s stories in your inbox

Join Medium for free to get updates from this writer.

Time for a refresher on the previous blog! Because I2C is open-drain, the data and clock lines are pulled HIGH passively by the pull-up resistor (Rp). SO! Every time the line transitions from LOW to HIGH, that resistor has to physically charge up the bus capacitance. Basically a huge RC circuit. If that capacitance is too high, the line takes too long to charge. Our crisp, square clock and data waves turn into sluggish, rounded shark fins. Our discrete 0 and 1 levels poof.
The formula for rise time is:

How 0.8473? The time required for the signal to transition from 30% to 70% of the supply voltage V0 and also if you remember 12th grade:

Now take log both sides etc. etc. etc. we’re all smart people we can derive a formula surely. The rise time is essentially the difference between time taken to reach 70% and 30%.

Then subtract and you get the rise time formula. Boom. Maths.

Sorry for the tangent, now let’s get on with it.
To prevent the shark fin situation, the I2C spec caps total bus capacitance at 400pF. This is the point after which rise times start exceeding the 300ns maximum allowed at fast mode.
We might want to use a smaller resistor here, but no we can’t because standardly I2C pins handle only 3mA. If the resistor is toooooo small, the microcontroller’s tiny internal transistor physically cannot pull the bus voltage down to zero to send a ‘0’. I2C devices are only guaranteed to sink a limited amount of current while still pulling the line LOW enough to be recognised as a logic ‘0’. Beyond 400pF, you cannot find a resistor that is both- large enough to keep the current under the 3 mA limit, and small enough to charge the line fully before the next bit is clocked in (or rather, there may come a value of capacitance for which we cannot find a suitable resistor).
This is why I2C doesn’t scale well onto long cables or large numbers of devices. Every connection eats into it.

Number of Nodes

I2C is a multi-slave system so we can have manyyy peripherals connected. But. How many, realistically?
Standard I2C uses 7-bit addressing. So, 2⁷ = 128 possible unique addresses. But, the protocol strictly has reserved 16 of those addresses for special functions. That leaves us with exactly 112 valid addresses for our peripherals. So, mathematically, 112 devices can be connected.

Realistically though, it’s all governed by our 400pF limit. Every single device we attach to the bus adds its own parasitic capacitance. A standard IC pin adds about 10pF. Even if our PCB traces were made of absolute magic and added zero capacitance we hit the 400pF cap at around 40 devices. (FYI- copper traces do add a lot of capacitance).
Factor in the capacitance of the physical traces connecting everything, being generous, on a standard board we can have ~15–20 nodes MAX. Trying to connect 112 nodes will definitely give the shark fin situation.

Reserved Addresses

So the 16 reserved addresses I mentioned a second ago. Specifically, two blocks of 8 addresses each at the top and bottom are reserved. If we try to assign one of these to a standard temperature sensor or EEPROM, the bus will either ignore us, or we will accidentally trigger a bus-wide event.

Press enter or click to view image in full size
Reserved Addresses- IDK how to add tables in medium so here’s a screenshot from Obsidian

So the usable addresses are from 0x08 to 0x77.

And that’s (hopefully) the full picture of why I2C works the way it does. See why I said it’s my favourite protocol? It. Is. Gorgeous.

And, that is it from my side about I2C. If you read till here, I am eternally grateful! Do let me know what you think, I really appreciate everything!

Ok byeee

--

--

Shreya Veni
Shreya Veni

Written by Shreya Veni

Just trying to figure life out