At the same as SPI, analyzed in a previous article, the I2C (Inter-Integrated Circuit) is a synchronous communication bus used to connect and exchange data between a microprocessor and external devices; it was developed by Philips, now NXP, today it is "de facto" standard.
Bus description
The I2C bus is also known as two-wire as it is characterized in all the effects of only two "wires":
The lines abovelines are always characterized by a pull-up resistor that has the task of maintaining the signal "high" (logic 1) in conditions of idle while the interconnected components (master and slave) have the task of lowering the level to transfer a logic 0 and release it to bring it back to idle and transfer a logic 1, this behavior is typical of the open-drain lines.
Similarly to SPI, you can have multiple slaves connected to the bus and a single master to communicate with, the main difference is that there is no signal SS (Slave Select) but the master selects the slave with which communicate through addressing. In fact, the master transmits the slave address on the SDA line before starting the transfer of real data, and this is typically 7-bit address (up to 128 slaves) but is planning to extend up to 10 bits (up to 1024 slave).
I2C has a fundamental characteristic that allows the presence of more than one master on the bus (multi master mode).
The communication protocol
The communication protocol is characterized by the following steps:
The clock is always driven by the master but in some cases the slave can maintain low value to introduce delay and prevent the master sends other data (maybe it needs more time to process the data already received): This feature is called "clock stretching".
.Net Micro Framework : the supported classes for the bus
The. Net Micro Framework considerably simplifies the use of the I2C bus by using the I2CDevice class (namespace Microsoft.SPOT.Hardware, Microsoft.SPOT.Hardware.dll assemblies), whose constructor expects a parameter of type I2CDevice.Configuration to be suitably configured, this configuration allows you to set:
All the above configuration parameters are always closely tied to the device with which you want to communicate and to be found in the datasheet.
1: I2CDevice.Configuration config =
2: new I2CDevice.Configuration(I2C_ADDRESS, I2C_CLOCK_RATE_KHZ);
3:
4: I2CDevice i2c = new I2CDevice(config);
The I2CDevice class provides a single method Execute() to be able to do one or more "transactions" on the bus representing the operations of reading and writing with the slave. This method requires as input an array of objects I2CDevice.I2CTransaction and a timeout. The I2CDevice.I2CTransaction class is the base class for the I2CDevice.I2CReadTransaction class, in the case of a read transaction, and for I2CDevice.I2CWriteTransaction class, in the case of a write transaction.
Creating an instance for each of the two classes above can be carried out through the following two static methods of the class I2CDevice:
Ultimately, the procedure for using I2C plans to create an array of "transactions" of reading and / or writing (obviously mixed) and execute these transactions in a single stroke, finding himself the transmitted data to the slave and buffers reception with the required data.
Imagine having a component I2C characterized by a series of internal registers and wanting to read the content of one of them. This type of communication is characterized by two I2C "transactions" , the first writing in order to send the slave address of the register to be read (be careful! Do not speak of the address slave which is sent before) and the second reading to be able to read the content.
1: byte write = { REG_ADDRESS };
2: byte read = new byte[1];
4: // create I2C write and read transaction
5: I2CDevice.I2CTransaction i2cTx = new I2CDevice.I2CTransaction[2];
6: i2cTx[0] = I2CDevice.CreateWriteTransaction(write);
7: i2cTx[1] = I2CDevice.CreateReadTransaction(read);
8:
9: // execution
10: i2c.Execute(i2cTx, I2C_TIMEOUT);
Deep into the HAL
With I2C (as already seen for the SPI), each OEM must implement a layer (HAL and PAL) to serve as a "bridge" between the (CLR managed code to high-level) and the particular hardware below.
We take as reference the Netduino board (generation 1), which has as a microcontroller Atmel AT91. By downloading the source code of the firmware (they are open source) from the official site, we can find the implementation code in managed C # class I2CDevice (Framework \ Core \ Native_Hardware \ I2C.cs) in which the GetI2CPins() method is invoked on the HardwareProvider current instance.
1: public I2CDevice(Configuration config)
2: {
3: this.Config = config;
4:
5: HardwareProvider hwProvider = HardwareProvider.HwProvider;
6:
7: if (hwProvider != null)
8: {
9: Cpu.Pin scl;
10: Cpu.Pin sda;
11:
12: hwProvider.GetI2CPins(out scl, out sda);
13:
14: if (scl != Cpu.Pin.GPIO_NONE)
15: {
16: Port.ReservePin(scl, true);
17: }
18:
19: if (sda != Cpu.Pin.GPIO_NONE)
20: {
21: Port.ReservePin(sda, true);
22: }
23: }
24:
25: Initialize();
26:
27: m_disposed = false;
28: }
After a series of invocations to cascade through the CLR to the implementation of the HAL, the GetPins() method is invoked on the AT91_I2C_Driver class (DeviceCode \ Targets \ Native \ AT91 \ DeviceCode \ AT91_I2C \ AT91__I2C.cpp) that returns the identifiers of pins of the processor associated with the I2C port.
1: void AT91_I2C_Driver::GetPins(GPIO_PIN& scl, GPIO_PIN& sda)
3: NATIVE_PROFILE_HAL_PROCESSOR_I2C();
5: scl = AT91_TWI_SCL;
6: sda = AT91_TWI_SDA;
7: }
In the case of Netduino board (generation 2) that has a STM32 processor, the reading function of the I2C pins is I2C_Internal_GetPins() (DeviceCode \ Targets \ Native \ STM32 \ DeviceCode \ STM32_I2C \ STM32_i2c_functions.cpp).
1: void I2C_Internal_GetPins(GPIO_PIN& scl, GPIO_PIN& sda)
3: scl = I2Cx_SCL_Pin;
4: sda = I2Cx_SDA_Pin;
5: }
Conclusion
The I2C bus unlike the SPI is obviously not full duplex being characterized by a single data line and is even slower in terms of speed. The main advantage is that you do not have the complexity of a selection signal of the slave and be able to work in multi master mode.
The. NET Micro Framework enables you to use this bus very easily with a single class and the concept of read / write I2C "transactions" in order to perform the communication in a single "shot".
Very soon you will see a real example of application of this bus (like the SPI bus) with a managed driver I developed for an NFC chip from NXP !
View this page in another language: