It is well-known that increasing the number of pins on an integrated circuit (IC) increases its cost. To reduce the number of pins, several serial protocols have been developed to transfer data between IC's. One such protocol, I2C, uses only 2 bidirectional open-drain wires.
For a recent application, I needed to write a quick class to communicate to a Newhaven LCD display over the I2C bus. I'll demonstrate how simple it was to get up and running using the Micro Framework.
In a previous blog, I discussed how easy it is to use the .NET Micro Framework on an embedded target (such as the GHI Embedded Master). Microsoft has made the I2C bus very simple to access on an embedded device. All of this functionality can be accessed using the Microsoft.SPOT.Hardware.I2CDevice class.
The first step is to create our I2C class:
///<summary>
/// A class for writing text to an LCD Display over the I2C bus
///</summary>
public class I2CLCDDisplay
{
private I2CDevice i2c;
///<summary>
/// Initializes a new instance of the I2CLCDDisplay class.
///</summary>
public I2CLCDDisplay()
{
this.i2c = new I2CDevice(
new I2CDevice.Configuration(0x50 >> 1, 100)
);
}
There's an important item to note here. From the data sheet of the display, the default I2C address of the device is 0x50. However, the I2C bus uses the least significant bit of the address byte to specify if the command is a read or write command. Thus, we must bit shift the address once to the right to account for this.
The next step is to write a public function to write text to the display:
///<summary>
/// Writes a string of text to the LCD.
/// Returns true if the write succeeds.
///</summary>
///<param name="strText">The text to display</param>
public bool WriteText(String strText)
{
// First, convert the string of text into an array of bytes
byte[] buffer = Encoding.UTF8.GetBytes(strText);
// Create a write transaction with the text as our data
I2CDevice.I2CTransaction transaction =
this.i2c.CreateWriteTransaction(buffer);
// Create a list of transactions to execute.
// In this case, there is only this one transaction.
I2CDevice.I2CTransaction[] transationList =
new I2CDevice.I2CTransaction[] { transaction };
// Execute the transaction.
// This call actually sends the data on the I2C bus.
// We use a timeout of 100ms here, since the data
// shouldn't take too long to transmit.
int nSent = this.i2c.Execute(transationList, 100);
// If the return parameter from "i2c.Execute()" call equals
// the length of the data that we were trying to send, then
// the data was sent successfully.
return (nSent == transaction.Buffer.Length);
}
It's just that simple! Keep in mind that if your application required a number of commands to be sent in quick succession, you can build a transaction list (I2CDevice.I2CTransaction[]) of the commands, and the I2CDevice class will send them back to back in native code.
Learn more about DMC's embedded design and programming services.