Netduino home hardware projects downloads community

Jump to content


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.
Photo

Using multiple I2C devices. (I2CDevice)


  • Please log in to reply
7 replies to this topic

#1 loveelectronics

loveelectronics

    Member

  • Members
  • PipPip
  • 11 posts

Posted 27 June 2011 - 12:41 PM

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.

How can I get round this?

#2 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 27 June 2011 - 01:40 PM

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.

Regards,

Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#3 loveelectronics

loveelectronics

    Member

  • Members
  • PipPip
  • 11 posts

Posted 27 June 2011 - 01:44 PM

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);
        }
    }
}


#4 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 27 June 2011 - 01:51 PM

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.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#5 loveelectronics

loveelectronics

    Member

  • Members
  • PipPip
  • 11 posts

Posted 27 June 2011 - 02:04 PM

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.

#6 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 27 June 2011 - 02:50 PM

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.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#7 loveelectronics

loveelectronics

    Member

  • Members
  • PipPip
  • 11 posts

Posted 27 June 2011 - 03:00 PM

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.

#8 aalmada

aalmada

    Advanced Member

  • Members
  • PipPipPip
  • 44 posts
  • LocationPortugal

Posted 13 September 2012 - 11:44 AM

I created a I2CBus class that fixes many of the I2CDevice issues. It is a singleton, thread-safe and supports repeated start bit.

Source is available at https://bitbucket.or...dware/I2CBus.cs

I hope it will be useful to whoever runs into these issues. Feedback is welcome!

aalmada




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.