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.
I have two I2C devices in my project, both are of type I2CDevice, they look like this:
/// <summary>
/// Encapsulates communication and management of the Analog Devices ADXL345 3-axis accelerometer.
/// </summary>
public class ADXL345 : I2CDevice
/// <summary>
/// Encapsulates communication and management of the ST Microelectronics L3G4200D 3-axis gyroscope.
/// </summary>
public class L3G4200D : I2CDevice
/// <summary>
/// Create an ADXL345 on a custom address.
/// </summary>
/// <param name="address"></param>
public ADXL345(ushort address, int clockRateKhz)
: base(new Configuration(address, clockRateKhz))
{
m_Address = address;
}
/// <summary>
/// Create an L3G4200D on a custom address.
/// </summary>
/// <remarks>CS is tied to VDD high to enable I2C.</remarks>
/// <param name="address"></param>
public L3G4200D(ushort address, int clockRateKhz)
: base(new Configuration(address, clockRateKhz))
{
m_Address = address;
}
When I try to instansiate another two I2C devices, I get the following error:
#### Exception System.InvalidOperationException - CLR_E_INVALID_OPERATION (1) ####
#### Message:
#### Microsoft.SPOT.Hardware.Port::ReservePin [IP: 0000] ####
#### Microsoft.SPOT.Hardware.I2CDevice::.ctor [IP: 0021] ####
#### LoveElectronics.Sensors.Gyroscopes.L3G4200D::.ctor [IP: 0012] ####
#### LoveElectronics.Tutorials.Accelerometers.MicroApp.Program::Main [IP: 007c] ####
A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.SPOT.Hardware.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.SPOT.Hardware.dll
I belive this is because the I2CDevice class is trying to reserve pins A4 and A5 to use the I2C bus, but really it should share these pins, and it should be my responsibility to ensure thread saftey.
I belive this is because the I2CDevice class is trying to reserve pins A4 and A5 to use the I2C bus, but really it should share these pins, and it should be my responsibility to ensure thread saftey.
How can I get round this?
My guess is that you have a class which creates and I2CDevice instance and you then try to do this again in another class. If this is the case you will need to share the single instance of the bus. If you have two devices then you would simply change the config of the I2CDevice before reading / writing to in order to switch the device.
That's how it's done with SPI - you have one SPI instance and several configs. You just switch the configs when before talking to different devices. The online docs suggest the same is true for I2C so I'd be trying this method.
Yeah, I created a new class called SharedI2CDevice which my devices now inherit from rather than I2CDevice, which uses a static I2CDevice to manage access to the bus:
public sealed class L3G4200D : SharedI2CDevice
/***
This file is created by Love Electronics Ltd
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License.
If not, see <http://www.gnu.org/licenses/>.
***/
using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
namespace LoveElectronics.Resources
{
/// <summary>
/// An I2CDevice wrapper which shares one I2C Device between multiple devices.
/// Also provides a simplified helper methods to read/write to the device.
/// <remarks>The user must ensure thread safety!</remarks>
/// </summary>
public abstract class SharedI2CDevice
{
/// <summary>
/// The static shared I2C device.
/// </summary>
private static I2CDevice m_Device;
/// <summary>
/// The timeout of read/writes.
/// </summary>
protected int m_Timeout = 1000;
/// <summary>
/// The configuration of the current device.
/// </summary>
private I2CDevice.Configuration m_Configuration;
/// <summary>
/// Creates an instance of a shared I2C device.
/// </summary>
/// <param name="address"></param>
/// <param name="clockRateKhz"></param>
public SharedI2CDevice(ushort address, int clockRateKhz)
{
m_Configuration = new I2CDevice.Configuration(address, clockRateKhz);
if (m_Device == null)
{
m_Device = new I2CDevice(m_Configuration);
}
}
/// <summary>
/// Executes the passed transactions.
/// </summary>
/// <param name="transactions"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public int Execute(I2CDevice.I2CTransaction[] transactions, int timeout)
{
m_Device.Config = m_Configuration;
return m_Device.Execute(transactions, timeout);
}
/// <summary>
/// Simple read of device.
/// </summary>
/// <param name="memoryAddress">Initial register address.</param>
/// <param name="responseLength">Optional read length.</param>
/// <returns></returns>
protected virtual byte[] Read(byte memoryAddress, int responseLength = 1)
{
var buffer = new byte[responseLength];
I2CDevice.I2CTransaction[] transaction;
transaction = new I2CDevice.I2CTransaction[]
{
I2CDevice.CreateWriteTransaction(new byte[] { memoryAddress }),
I2CDevice.CreateReadTransaction(buffer)
};
int result = Execute(transaction, m_Timeout);
return buffer;
}
/// <summary>
/// Simple write to the device.
/// </summary>
/// <param name="memoryAddress">Register address to write.</param>
/// <param name="value">Byte to write.</param>
protected virtual void Write(byte memoryAddress, byte value)
{
I2CDevice.I2CTransaction[] transaction;
transaction = new I2CDevice.I2CTransaction[]
{
I2CDevice.CreateWriteTransaction(new byte[] { memoryAddress, value })
};
int result = Execute(transaction, m_Timeout);
}
}
}
Yeah, I created a new class called SharedI2CDevice which my devices now inherit from rather than I2CDevice, which uses a static I2CDevice to manage access to the bus:
I'd be thinking about trying setting the config in the shared class though a parameter. The device class would pass it's config to the shared class as part of any read write classes. The shared class would switch the config m_Device.Config = m_Configuration prior to the operation.
As, I say, I've done this with SPI not I2C but the docs suggest they are dealt with in the same manner.
A new instance of a shared class is created for each device that inherits from it (it is abstract), it is just the I2CDevice that is static and so shared between the devices.
The config is stored in the SharedI2CDevice and is set by calling the base() operator in the constructor of the derived class. You can see it sets m_Configuration when it is constructed.
Then when your derived classes perform an Execute (read/write to the I2CDevice) it changes the config at that point.
public int Execute(I2CDevice.I2CTransaction[] transactions, int timeout)
{
m_Device.Config = m_Configuration;
return m_Device.Execute(transactions, timeout);
}
It should not be up to the derived classes to implement this, as it is putting knowlage about the limitations of the implementation in the wrong place.
A new instance of a shared class is created for each device that inherits from it (it is abstract), it is just the I2CDevice that is static and so shared between the devices.
Yes - see what you mean - nice class by the way.
You do not seem to have a constructor to match the following:
public ADXL345(ushort address, int clockRateKhz) : base(new Configuration(address, clockRateKhz))
Looking at the shared class I'd expect it to work if all of your I2C devices are using this. I'll get VS out later to look if no one else responds.
And just noticed this was your first post - welcome.
Yes, I changed the derived class so they no longer pass an I2CDevice.Configuration into the base class.
This is because, with the helper Read/Write classes the dervied classes do not need to know about I2CDevice and that portion of Microsoft.SPOT.Hardware.
They now call:
/// <summary>
/// Create an ADXL345 on a custom address.
/// </summary>
/// <param name="address">The address of the device.</param>
/// <param name="clockRateKhz">The rate at which communication will occur.</param>
public ADXL345(ushort address, int clockRateKhz)
: base(address, clockRateKhz)
{ }
Thanks for the welcome.
You do not seem to have a constructor to match the following:
public ADXL345(ushort address, int clockRateKhz) : base(new Configuration(address, clockRateKhz))
Looking at the shared class I'd expect it to work if all of your I2C devices are using this. I'll get VS out later to look if no one else responds.
Sorry - I was not clear enough, this does now work. I was presenting a fix incase anyone else experiences this.
Although it increases read time by 0.12ms, it is better than creating/disposing the I2CDevice everytime, which increases read time by 1.2ms.