The Netduino forums have been replaced by new forums at community.wildernesslabs.co.
This site has been preserved for archival purposes only
and the ability to make new accounts or posts has been turned off.
This code is a derivative of the I2CBus class Pavel Bansky developed. This will help you easily manage multiple I2C devices on a single bus. My I2CBus implements the Singleton pattern. Please note this is done this way because you can only have one instance of I2CDevice and it can sometimes be hard to manage multiple I2C devices.
I have attached the I2CBus class, an example I2C sensor driver, and the Program.cs of the example Netduino application.
In the first code snippet below we will need to create an instance of the I2CBus class and also our example I2C sensor driver. When we call the constructor for the example sensor we will pass in the I2C device address of the example sensor.
public class Program
{
// Example I2C sensor.
private static ExampleSensor _exampleSensor;
public static void Main()
{
// Create a new I2C bus instance at startup.
I2CBusi2cBus = I2CBus.GetInstance();
_exampleSensor = new ExampleSensor(0x79);
// write something to a register.
_exampleSensor.WriteSomethingToSpecificRegister();
byte[] data = _exampleSensor.ReadSomething();
}
}
This next code snippet shows what our example I2C sensor driver looks like. The constructor creates an I2CDevice configuration that contains the configuration information for this example sensor.
When we want to perform a read or write transaction on this I2C device, we talk to it through the I2CBus instance. When reading or writing, we will pass the I2CBus Write/Read methods the example sensor I2C device configuration along with the transaction timeout and other bytes to be read or written. We pass the I2C device configuration so that the I2CBus knows what device to read from or write to.
/// <summary>
/// This is an I2C sensor.
/// </summary>
public class ExampleSensor
{
private I2CDevice.Configuration _slaveConfig;
private const int TransactionTimeout = 1000; // ms
private const byte ClockRateKHz = 59;
public byte Address { get; private set; }
/// <summary>
/// Example sensor constructor
/// </summary>
/// <param name="address">I2C device address of the example sensor</param>
public ExampleSensor(byte address)
{
Address = address;
_slaveConfig = new I2CDevice.Configuration(address, ClockRateKHz);
}
public byte[] ReadSomething()
{
// write register address
I2CBus.GetInstance().Write(_slaveConfig, new byte[] {0xF2}, TransactionTimeout);
// get MSB and LSB result
byte[] data = new byte[2];
I2CBus.GetInstance().Read(_slaveConfig, data, TransactionTimeout);
return data;
}
public byte[] ReadSomethingFromSpecificRegister()
{
// get MSB and LSB result
byte[] data = new byte[2];
I2CBus.GetInstance().ReadRegister(_slaveConfig, 0xF1, data, TransactionTimeout);
return data;
}
public void WriteSomethingToSpecificRegister()
{
I2CBus.GetInstance().WriteRegister(_slaveConfig, 0x3C, new byte[2] { 0xF4, 0x2E }, TransactionTimeout);
}
}
And last, but not least, here is the I2CBus class (implements Singleton pattern and is thread safe).
public class I2CBus : IDisposable
{
private static I2CBus _instance = null;
private static readonly object LockObject = new object();
public static I2CBus GetInstance()
{
lock (LockObject)
{
if (_instance == null)
{
_instance = new I2CBus();
}
return _instance;
}
}
private I2CDevice _slaveDevice;
private I2CBus()
{
this._slaveDevice = new I2CDevice(new I2CDevice.Configuration(0, 0));
}
public void Dispose()
{
this._slaveDevice.Dispose();
}
/// <summary>
/// Generic write operation to I2C slave device.
/// </summary>
/// <param name="config">I2C slave device configuration.</param>
/// <param name="writeBuffer">The array of bytes that will be sent to the device.</param>
/// <param name="transactionTimeout">The amount of time the system will wait before resuming execution of the transaction.</param>
public void Write(I2CDevice.Configuration config, byte[] writeBuffer, int transactionTimeout)
{
// Set i2c device configuration.
_slaveDevice.Config = config;
// create an i2c write transaction to be sent to the device.
I2CDevice.I2CTransaction[] writeXAction = new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(writeBuffer) };
lock(_slaveDevice)
{
// the i2c data is sent here to the device.
int transferred = _slaveDevice.Execute(writeXAction, transactionTimeout);
// make sure the data was sent.
if (transferred != writeBuffer.Length)
throw new Exception("Could not write to device.");
}
}
/// <summary>
/// Generic read operation from I2C slave device.
/// </summary>
/// <param name="config">I2C slave device configuration.</param>
/// <param name="readBuffer">The array of bytes that will contain the data read from the device.</param>
/// <param name="transactionTimeout">The amount of time the system will wait before resuming execution of the transaction.</param>
public void Read(I2CDevice.Configuration config, byte[] readBuffer, int transactionTimeout)
{
// Set i2c device configuration.
_slaveDevice.Config = config;
// create an i2c read transaction to be sent to the device.
I2CDevice.I2CTransaction[] readXAction = new I2CDevice.I2CTransaction[] { I2CDevice.CreateReadTransaction(readBuffer) };
lock (_slaveDevice)
{
// the i2c data is received here from the device.
int transferred = _slaveDevice.Execute(readXAction, transactionTimeout);
// make sure the data was received.
if (transferred != readBuffer.Length)
throw new Exception("Could not read from device.");
}
}
/// <summary>
/// Read array of bytes at specific register from the I2C slave device.
/// </summary>
/// <param name="config">I2C slave device configuration.</param>
/// <param name="register">The register to read bytes from.</param>
/// <param name="readBuffer">The array of bytes that will contain the data read from the device.</param>
/// <param name="transactionTimeout">The amount of time the system will wait before resuming execution of the transaction.</param>
public void ReadRegister(I2CDevice.Configuration config, byte register, byte[] readBuffer, int transactionTimeout)
{
byte[] registerBuffer = {register};
Write(config, registerBuffer, transactionTimeout);
Read(config, readBuffer, transactionTimeout);
}
/// <summary>
/// Write array of bytes value to a specific register on the I2C slave device.
/// </summary>
/// <param name="config">I2C slave device configuration.</param>
/// <param name="register">The register to send bytes to.</param>
/// <param name="writeBuffer">The array of bytes that will be sent to the device.</param>
/// <param name="transactionTimeout">The amount of time the system will wait before resuming execution of the transaction.</param>
public void WriteRegister(I2CDevice.Configuration config, byte register, byte[] writeBuffer, int transactionTimeout)
{
byte[] registerBuffer = {register};
Write(config, registerBuffer, transactionTimeout);
Write(config, writeBuffer, transactionTimeout);
}
/// <summary>
/// Write a byte value to a specific register on the I2C slave device.
/// </summary>
/// <param name="config">I2C slave device configuration.</param>
/// <param name="register">The register to send bytes to.</param>
/// <param name="value">The byte that will be sent to the device.</param>
/// <param name="transactionTimeout">The amount of time the system will wait before resuming execution of the transaction.</param>
public void WriteRegister(I2CDevice.Configuration config, byte register, byte value, int transactionTimeout)
{
byte[] writeBuffer = {register, value};
Write(config, writeBuffer, transactionTimeout);
}
}
You will find the I2CBus and other classes attached to this post. Just read the documentation in the I2CBus class, look at the example code, and you should be able to figure it out. If not, then just post a question
I just realized that the I2CBus class should have implemented the Singleton pattern.
I have updated the initial post and also the file attachments to reflect the changes. This Singleton pattern implementation is thread safe too.
Will this code work on a Netduino Plus? I have been trying to get I2C working on my board but have had no success. My programs compile but I never see activity on the I2C pins A4 and A5.
I compiled this code on my NetDuino plus and again, no activity on the pins.
Looking at the schematic how are the MUX1 and MUX2 lines controlled that switch A4 and A5 pins between the I2C hardware analog input hardware on the AT91?
Shawn
Hi Shawn,
I2C communication requires pull-up resistors. The Netduino will pull the voltage "low" to GND to send data, but the pull-up resistors keep the line high the rest of the time. This is how I2C works...
The MUX1/MUX2 lines are automatically switched in the background (to digial/I2C mode or to analog mode).
Chris
This code is a derivative of the I2CBus class Pavel Bansky developed. This will help you easily manage multiple I2C devices on a single bus. My I2CBus implements the Singleton pattern. Please note this is done this way because you can only have one instance of I2CDevice and it can sometimes be hard to manage multiple I2C devices.
Hi phantomtypist, what is the license for your I2CBus class?
Phantomtypist, thanks for your work and for sharing it, but...
...I really can't feel the reason about a singleton would be better than a static declaration.
There are several strange things, IMHO:
a singleton instance never dies, so it makes no sense the IDisposable implementation;
even considering the IDisposable pattern, once you dispose it (calling Dispose), calling Dispose again probably will throw, and does not kill the hosted instance;
the LockObject has no sense, because you are locking just the I2CBus instance creation, but the further usage is not thread-safe at all;
consider two threads using the I2CBus: they don't work properly.
The I2CDevice instance is a mutex by itself, because when you instantiate it, automatically none else can instantiate it again. However it's *NOT* a real mutex, but you are guaranteed that none else can open it.
So, the best way to use the I2C driver is just as it is offered by the Micro Framework.
If you want a different flavor of I2C driver, you may consider hosting the I2CDevice instance in a Disposable class (e.g. ExampleSensor). The ideal usage should be as follows:
using (ExampleSensor sensor = new ExampleSensor())
{
// do something with "sensor"
}
The thread-safety on drivers like I2C or SPI is a much more complex task, and cannot be solved with a simple lock.
Hope it helps.
Cheers
Biggest fault of Netduino? It runs by electricity.
There is a reason why is better to go with singleton instead of static - easier unit testing.
Wait, there is another one - you can you other instances instead of provided default (albeit not in OP's case).
Miha Markic, Microsoft MVP C# Righthand .net consulting and software development http://blog.rthand.com/
About Dispose - even though the static variable never goes out of scope it might be still useful to call Dispose on it.
.net guideline is that Dispose should be multi-call tolerant - IOW you can call it as many times as you wish and it shouldn't throw.
Miha Markic, Microsoft MVP C# Righthand .net consulting and software development http://blog.rthand.com/
I want use your code for an IMU 10DOF device, the GY-80 from dealextreme. It has an accelerometer, gyro, magnetometer and barometer, all in one with i2c bus.
I receive readings from barometer, but only fixed values for the others sensors.
Please, help me to discovery where i'm wrong. I'll attach my project.
i prefere i different i2c bus style, its even easier to use.
its leand to netmf-toolboxes bus systems.
usage:
class test : MultiI2C
{
public test()
:base(address, frequenzy)
{
}
void readstuff()
{
base.Read(...);
}
}
//edit i like your write/read register functions, ill add them to mine, assimilated
//edit: new version added. i havent tested it, but it should work.
//edit: tested, works, could read the identification registers of my hcm module with the new i2c class wia ReadRegister.
thanks for the reply, but addres is OK 0xEF >> 1 = 0x77, ie 0xEF is adress + read bit (0x77<<1+1)
i can read from my devices, but only barometer sensor give me dynamics values, the others only fixed values
You are absolutely correct. Being lazy I went onto Google and typed Online Bitshift calculator and some site came up. Whacked in the values and they shifted wrong. Gotta go back through my browser history and find that site and let them know the shift right doesn't work correctly.
So... back to square one, yep checked them in scientific calculator and all are correct.
I have all of these sensors except my replacement BMP085 which should be in the mailbox today. I'll try the software with my individual sensors to see if it works. I looked through all the code and nothing jumps out at me.
thats the code i use (with my multi i2c i posted earlier)
the readings are fine, every movement is noticed
(ofc thats just a little "preview" - you should implement the interrupt when data is ready and read than!)
its very cool that the register pointer incements automatically, so you need only one read instruction for all 6 data registers
thats the code i use (with my multi i2c i posted earlier)
the readings are fine, every movement is noticed
(ofc thats just a little "preview" - you should implement the interrupt when data is ready and read than!)
its very cool that the register pointer incements automatically, so you need only one read instruction for all 6 data registers
I'm trying to write a little i2c driver for the Macetech centipede shield (64 pin GPIO expander shield) - actually the driver should work with two of them both connected on the same I2C bus. It is intended to drive a small christmas display with 32 bright RGB leds (so, Im going to use 96 outputs to drive the LEDs)
The way the Centipede shield is set up, there are 4 MCP23017 chips on each shield- and when you use two in conjunction on the same i2c bus (you need to set one's address jumper differently) you end up with 8 individual MCP23017 chips on the bus, each with it's own i2c address (0 through 7) and 16 GPIO pins on each port (128 pins total)
I should be able to create 8 multiI2c derived objects (one for each chip) each constructed with the chip's address in the config, right?
Also, this is my first attempt at i2C on the netduino (Netduino Plus 2) platform. I was frustrated today trying to gather initial information, seeing the myriad approaches, and seemingly conflicting info. (I get frustrated easily trying to sort through information like this - it's not a knock on anyone)
From what I can gather, I2C is now possible on the Netduino Plus 2, if:
1] You flash with the latest firmware (4.2.1.2)
2] According to Chris Walker, "if you are using a Netduino Plus 2...please remove the SecretLabs.NETMF.Hardware.NetduinoPlus.dll assembly and add in SecretLabs.NETMF.Hardware.Netduino.dll instead."
3] I need to add my own pullup resistors for pins 4 and 5
I have some other questions already, if anyone here can help me find the correct answers, I would be very appreciative. Im trying to finish this in the next couple of days so I can set it up at work.
a] How do I calculate the correct value for the pullup resistors?
b] What should I pass for the clock i2c clock speed when running on the ND+2
c] I am powering the aux+ of the macetech shields with an external 5V supply, to drive the 32 (i.e. 96) leds, and the ND+2 is powered by its own 5V (currently my USB port) - is there anything i need to watch out for?
If anyone is interested, the display is a small christmas tree that will have 32 RGB lights - the pattern for the tree will be sent to my ND+2, from a small RFID reader that will read a UHF rfid tag placed directly on it. I work in an RFID development company, and everyone has long range (100m XC3 protocol) readers, and the idea is that anyone can transmit from their own devices to that tag and write a new pattern, which my reader will pick up as it polls the tag periodically. Anyone in the building will be able to change the display of the tree using their development readers.
The RFID stuff is a piece of cake. Its this i2c on the ND+2 that is proving to be my bottleneck...
mty: i use 4.7kohm resistors, they are fine, you need them. (pullup, both sda and scl)
iam using mcp23017 too with my multii2c, its fine, even using multiple stuff, and also its threadsafe.
they are really easy to setup for output, but you realla have to read the datasheet carfeully to use them as input ports (a lot to setup) - also you can use one as one 16bit i/o chip or 2x 8bit io chips
clock speed: you get them from your devices datasheet, but i think 400khz is max (at least for my netduino mini)
anything lower than what the datasheet says is fine too.
when you have multiple power sources you connect gnd together, except when they are opto coupled. (dont connevt v+ together!)
...I have all of these sensors except my replacement BMP085 which should be in the mailbox today. I'll try the software with my individual sensors to see if it works. I looked through all the code and nothing jumps out at me...
Thanks mtylerjr!
i have no time to try right now, but soon i'll do it.