Overview
The Inter-Integrated Circuit (
I²C, i2c) is a multi-master, multi-slave serial communication protocol invented by Philips Semiconductor (now NXP Semiconductors), primarily used in embedded systems.
A lot devices like an EEPROM, LCD, GPIO expander, LED drivers and more support the I2c protocol.
It is generally not available for Windows easily, though it is available on the Raspberry PI via Python and C.
That is why I created
Nusbio.net in 2015 that offers in a simple way to talk I2C from Windows and .NET
A lot of devices designed by Adafruit use the I2C protocol and are compatibles with Nusbio and any .NET languages.
Videos:
All our C#, VB.NET, Powershell and F# samples source code are open-source and can be found on
Nusbio.Samples
C# Class
This post explains the I2CEngine .NET class for the
Nusbio device.
Wiring
Since Nusbio has 8 gpio pins, you can technically setup 4 independent I2C buses. But generally one is enough.
I use Gpio0 for SCL and Gpio1 for SDA as convention. Obviously you can connect multiple I2C devices to the bus.
Nusbio does not come with any pull up resistors so it is up you to take care of it.
Generally if you use an I2C breakout it is part of the breakout.
Software
For each I2C device on the bus you must create an object of the class I2CEngine. After that you call the WriteBuffer() method to initiate an I2C write operation and the method ReadBuffer for a I2C read operation.
The methods takes care of the I2C Control byte. You do not have to pass it as part of the buffer.
The methods return true if the operation succeeded. For the Readxxxx methods, if the operation succeeded you can then read the buffer which will be updated with the data.
public I2CEngine(Nusbio nusbio, NusbioGpio sdaOutPin, NusbioGpio sclPin, byte deviceId);
public bool ReadBuffer(int len, byte[] data);
public bool WriteBuffer(byte[] buffer);
public bool WriteBuffer(byte address8bit, byte[] buffer);
Samples
I2C EEPROM
Here is a basic sample reading the first 64 bytes of the I2C EEPROM
24LC256 which is a 32k bytes EEPROM with a page size of 64 byte.
byte EEPROM1_WR = 80; // 0xA0;
var i2c = new I2CEngine(nusbio, NusbioGpio.Gpio1, NusbioGpio.Gpio0, EEPROM1_WR);
var addr = 0;
var buffer = new byte[64];
if (i2c.WriteBuffer(new byte[2] { (byte)(addr >> 8), (byte)(addr & 0xFF) }))
{
var r = i2c.ReadBuffer(64, buffer);
}
We do offer specific classes to handle I2C and SPI EEPROM, see our folder
EEPROM on github.
I2C MCP9808 Temperature Sensor
Here part of our class
MCP9808 _Temperature Sensor.cs available on Github.
The method Begin will return true if the device is detected on the bus else false.
public bool Begin(byte deviceAddress = MCP9808_I2CADDR_DEFAULT)
{
try
{
this._i2c.DeviceId = deviceAddress;
if (read16(MCP9808_REG_MANUF_ID) != MCP9808_REG_MANUF_ID_ANSWER) return false;
if (read16(MCP9808_REG_DEVICE_ID) != MCP9808_REG_DEVICE_ID_ANSWER) return false;
return true;
}
catch (System.Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
return false;
}
}
public double GetTemperature(TemperatureType type = TemperatureType.Celsius)
{
uint16_t t = read16(MCP9808_REG_AMBIENT_TEMP);
double temp = t & 0x0FFF;
temp /= 16.0;
if ((t & 0x1000) == 0x1000) temp -= 256;
switch (type)
{
case TemperatureType.Celsius: return temp;
case TemperatureType.Fahrenheit: return CelsiusToFahrenheit(temp);
case TemperatureType.Kelvin: return temp*CELCIUS_TO_KELVIN;
default:
throw new ArgumentException();
}
}
private UInt16 read16(uint8_t reg)
{
UInt16 value = 0;
if (this._i2c.WriteBuffer(new byte[1] { reg }))
{
var buffer = new byte[2];
this._i2c.ReadBuffer(2, buffer);
value = (System.UInt16)((buffer[0] << 8) + buffer[1]);
}
else throw new ArgumentException();
}
We do offer specific classes to handle I2C and SPI EEPROM, see our folder
EEPROM on github.
Advanced Methods
To improve transfer performance, the class I2CEngine expose the following methods which optimize the number of USB operations to execute the I2C operations (we combine an I2C Read + I2C Write operation in one USB transaction)
public bool Send16BitAddressAnd1Byte(int address16bit, byte b);
public bool Send16BitsAddressAndBuffer(int address16bit, int len, byte[] buffer);
public int Send16BitsAddressAndRead1Byte(short address8bit);
public bool Send16BitsAddressAndReadBuffer(int address16bit, int len, byte[] data);
public bool Send1ByteCommand(byte command);
public bool Send2BytesCommand(byte command0, byte command1);
public bool Send3BytesCommand(byte command0, byte command1, byte command2);
public int Send1ByteRead1Byte(byte address8bits);
public _2BytesOrInt16Result Send1ByteRead2Bytes(byte cmd);
Performance
Using the EEPROM 24LC256 which is an I2C 32k bytes with a max clock of 400 kHz, we can transfer the 32 k byte of data from the EEPROM to the PC at the rate of
15 k bytes per second in batch mode of 64 page
s at a time.
Transferring one page a the time give a transfer rate of 8 k bytes per second.
Output or input batch mode is necessary to increase performance for all I2C devices.
Transfer speed may vary also depending on the speed of the computer.