I2C
I²C (pronounced eye-squared-cee or eye-two-cee) was originally developed by Philips as a synchronous serial bus for communication between integrated circuits. A single master shares two wires with one or more slaves. Instead of dedicating a chip-select line to each slave (like SPI does), I²C selects the target by sending its address on the bus — saving one I/O pin per slave.
A transfer starts with a start condition from the master, followed by the slave's 7-bit address and a single bit indicating whether the master will send or receive data. The addressed slave acknowledges, the data transfer happens, and the master ends the exchange with a stop condition.
The two wires are:
- SDA — Serial Data
- SCL — Serial Clock
Common I²C bus speeds are 100 kbit/s (standard mode) and 400 kbit/s (fast mode).
Two APIs
TinyCLR offers two I²C APIs that coexist on the same hardware. Both namespaces ship in a single NuGet package — install one and you have both.
- .NET IoT API — Microsoft's standard
System.Device.I2cnamespace. Use this for new code and for portability with desktop .NET IoT applications. - TinyCLR API — the original
GHIElectronics.TinyCLR.Devices.I2cnamespace. Keeps v2 code working without changes.
NuGet packages:
GHIElectronics.TinyCLR.Devices.I2c— contains both theSystem.Device.I2c(.NET IoT) andGHIElectronics.TinyCLR.Devices.I2c(TinyCLR) namespaces.GHIElectronics.TinyCLR.Pins— pin name constants for SITCore boards.
.NET IoT bus numbering: the busId argument to I2cConnectionSettings is the controller number minus one — I2c1 → busId: 0, I2c3 → busId: 2.
I²C master
The master is the host that initiates every transfer. A TinyCLR board is the master in this mode.
- .NET IoT API
- TinyCLR API
using System.Device.I2c;
using GHIElectronics.TinyCLR.Pins;
var settings = new I2cConnectionSettings(FEZBit.I2cBus.EdgeBusId, 0x1D);
using I2cDevice device = I2cDevice.Create(settings);
device.Write(new byte[] { 1, 2 }); // Send-only.
device.WriteRead(write, read); // Good for reading registers (write address, then read).
using GHIElectronics.TinyCLR.Devices.I2c;
using GHIElectronics.TinyCLR.Pins;
var settings = new I2cConnectionSettings(0x1D, 100_000); // Slave address and bus speed.
var controller = I2cController.FromName(FEZBit.I2cBus.EdgeBusId);
var device = controller.GetDevice(settings);
device.Write(new byte[] { 1, 2 }); // Send-only.
device.WriteRead(write, read);
I²C slave
In slave mode, the board responds to a master initiating transfers — used when the board needs to look like an I²C peripheral to a host.
I²C slave mode is a TinyCLR API extension — it's not part of the Microsoft .NET IoT standard, which assumes you're always the master.
using System.Diagnostics;
using GHIElectronics.TinyCLR.Devices.I2c;
using GHIElectronics.TinyCLR.Pins;
var i2cController = I2cController.FromName(FEZBit.I2cBus.Edge);
i2cController.ReadBufferSize = 1024; // Default 256
i2cController.WriteBufferSize = 1024; // Default 256
i2cController.ClearReadBuffer();
i2cController.ClearWriteBuffer();
var i2cSettings = new I2cConnectionSettings(0x48, I2cMode.Slave);
i2cSettings.EnableClockStretching = true;
var i2cDevice = i2cController.GetDevice(i2cSettings);
i2cDevice.ErrorReceived += (sender, args) => Debug.WriteLine("Error received " + args.Error);
i2cDevice.FrameReceived += (sender, args) => {
switch (args.Event)
{
case I2cTransaction.MasterWrite:
Debug.WriteLine("Master is writing");
break;
case I2cTransaction.MasterRead:
Debug.WriteLine("Master is reading");
sender.Write(data);
break;
case I2cTransaction.MasterStop:
Debug.WriteLine("Done a frame!");
break;
}
};
Software I²C
You can drive ("bit-bang") an I²C bus in software on any GPIO pins, regardless of which ones the hardware controllers can use.
Software I²C is a TinyCLR API extension.
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.I2c;
using GHIElectronics.TinyCLR.Pins;
var sda = GpioController.GetDefault().OpenPin(FEZBit.GpioPin.P0);
var scl = GpioController.GetDefault().OpenPin(FEZBit.GpioPin.P1);
var controller = I2cController.FromName(FEZBit.I2cBus.Software, sda, scl);
The internal pull-ups on the SDA and SCL pins can be enabled with a fourth argument. This is usually enough, but external 2.2 kΩ pull-ups are better for reliable operation.
var controller = I2cController.FromName(FEZBit.I2cBus.Software, sda, scl, true);
Software-driven buses are slower than hardware I²C and use more CPU time, but they free you from the pin restrictions of the hardware controllers.
API reference
| Namespace | Description |
|---|---|
| GHIElectronics.TinyCLR.Devices.I2c | TinyCLR I²C controller and device classes |
| System.Device.I2c | .NET IoT standard I²C API (included in the same NuGet) |