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

Multiple I2C devices


  • Please log in to reply
10 replies to this topic

#1 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 12 April 2012 - 05:16 PM

I have a question on the how to read/write data from more than 1 I2C sensors. The problem I faced was: Every time I need to read a sensor, I have to create the I2CDevice and then dispose it after I finish with the read/write transaction. Otherwise, I cannot read from the other I2C devices (cannot create more than 1 I2CDevice at any given time).

I2CDevice.Configuration Myconfig = new I2CDevice.Configuration(addr, clkrate);  
I2CDevice MyI2Cdevice= new I2CDevice(Myconfig);                                
// create the transaction and execute here                                     
MyI2Cdevice.Dispose();                                                         

This create-dispose way seems to be rather inefficient, as I need to poll the sensors sequentially. I have no problem with getting all my readings if I do it this way, but is this the best way to write the code?

#2 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 12 April 2012 - 07:18 PM

Hi,
a class like the following will most possibly fit your requirement:

using System;
using System.Reflection;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace Netduino.Extender.TransferAdapters
{
	/// <summary>
	/// Wire-up the I2C device to the Netduino as follows:
	/// SDA -> Analog Pin 4;
	/// SCL -> Analog Pin 5;
	/// GND -> GND.
	/// </summary>
	public sealed class I2CAdapter
	{
		private static readonly Object _I2CLock = new object();
		private static readonly I2CAdapter _Instance = new I2CAdapter();

		private readonly I2CDevice _I2CDevice;

		/// <summary>Creates a new <see cref="I2CAdapter"/> instance </summary>
		/// <remarks>
		/// At this time the .NET Micro Framework only supports a single I2C bus. 
		/// Therefore, creating more than one I2CAdapter instance will generate an
		/// exception. 
		/// </remarks>
		private I2CAdapter()
		{
			//Initialize the I2CDevice with a dummy configuration
			_I2CDevice = new I2CDevice(new I2CDevice.Configuration(0, 0));
		}

		public static I2CAdapter Instance
		{
			get { return _Instance; }
		}

		private I2CDevice.I2CWriteTransaction CreateInternalAddressWriteTransaction(byte[] buffer, uint internalAddress,
		                                                                            byte internalAddressSize)
		{
			I2CDevice.I2CWriteTransaction writeTransaction = I2CDevice.CreateWriteTransaction(buffer);

			ModifyToRepeatedStartTransaction(internalAddress, internalAddressSize, writeTransaction,
			                                 typeof (I2CDevice.I2CWriteTransaction));

			return writeTransaction;
		}

		private I2CDevice.I2CReadTransaction CreateInternalAddressReadTransaction(byte[] buffer, uint internalAddress,
		                                                                          byte internalAddressSize)
		{
			I2CDevice.I2CReadTransaction readTransaction = I2CDevice.CreateReadTransaction(buffer);

			ModifyToRepeatedStartTransaction(internalAddress, internalAddressSize, readTransaction,
			                                 typeof (I2CDevice.I2CReadTransaction));

			return readTransaction;
		}

		/// <summary>
		/// To use the new I2C InternalAddress feature (repeated start bit) in the v4.1.1 alpha,
		/// add the following method to your code...
		/// and then call it instead of built-in I2CDevice.CreateWriteTransaction/CreateReadTransaction functions when appropriate.
		/// </summary>
		/// <param name="internalAddress"></param>
		/// <param name="internalAddressSize">The InternalAddressSize parameter defines
		/// the # of bytes used to represent the InternalAddress. The InternalAddress is a set of bytes sent to a device before the
		/// buffer--often a register # or memory location to write/read. When used in an I2C ReadTransaction, an extra start bit will
		/// be sent to the I2C device after the InternalAddress (followed by the traditional buffer read/write).</param>
		/// <param name="transaction"></param>
		/// <param name="transactionType"></param>
		private static void ModifyToRepeatedStartTransaction(uint internalAddress, byte internalAddressSize,
		                                                     I2CDevice.I2CTransaction transaction, Type transactionType)
		{
			FieldInfo fieldInfo = transactionType.GetField("Custom_InternalAddress",
			                                               BindingFlags.NonPublic | BindingFlags.Instance);
			fieldInfo.SetValue(transaction, internalAddress);

			fieldInfo = transactionType.GetField("Custom_InternalAddressSize", BindingFlags.NonPublic | BindingFlags.Instance);
			fieldInfo.SetValue(transaction, internalAddressSize);
		}

		public int WriteInternalAddressBytes(I2CDevice.Configuration i2CConfiguration, byte[] bytesToWrite,
		                                     uint internalAddress, byte internalAddressSize)
		{
			int bytesTransfered = ExecuteI2CTransactions(i2CConfiguration,
			                                             CreateInternalAddressWriteTransaction(bytesToWrite, internalAddress,
			                                                                                   internalAddressSize));

			// I2CDevice.Execute returns the total number of bytes transfered in BOTH directions for all transactions
			if (bytesTransfered < (bytesToWrite.Length))
			{
				Debug.Print("WriteInternalAddressBytes: I2C expected + '" + bytesToWrite.Length + "' but could write + '" +
				            bytesTransfered + "'.");
			}

			return bytesTransfered;
		}

		public int ReadInternalAddressBytes(I2CDevice.Configuration i2CConfiguration, byte[] bytesToRead, uint internalAddress,
		                                    byte internalAddressSize)
		{
			int bytesTransfered = ExecuteI2CTransactions(i2CConfiguration,
			                                             CreateInternalAddressReadTransaction(bytesToRead, internalAddress,
			                                                                                  internalAddressSize));

			// I2CDevice.Execute returns the total number of bytes transfered in BOTH directions for all transactions
			if (bytesTransfered < (bytesToRead.Length))
			{
				Debug.Print("ReadInternalAddressBytes: I2C expected + '" + bytesToRead.Length + "' but could read + '" +
				            bytesTransfered + "'.");
			}

			return bytesTransfered;
		}

		public int WriteBytes(I2CDevice.Configuration i2CConfiguration, byte[] bytesToWrite)
		{
			int bytesTransfered = ExecuteI2CTransactions(i2CConfiguration, I2CDevice.CreateWriteTransaction(bytesToWrite));

			// I2CDevice.Execute returns the total number of bytes transfered in BOTH directions for all transactions
			if (bytesTransfered < (bytesToWrite.Length))
			{
				Debug.Print("WriteBytes: I2C expected + '" + bytesToWrite.Length + "' but could write + '" + bytesTransfered + "'.");
			}

			return bytesTransfered;
		}

		public int ReadBytes(I2CDevice.Configuration i2CConfiguration, byte[] bytesToRead)
		{
			int bytesTransfered = ExecuteI2CTransactions(i2CConfiguration, I2CDevice.CreateReadTransaction(bytesToRead));

			// I2CDevice.Execute returns the total number of bytes transfered in BOTH directions for all transactions
			if (bytesTransfered < (bytesToRead.Length))
			{
				Debug.Print("ReadBytes: I2C expected + '" + bytesToRead.Length + "' but could read + '" + bytesTransfered + "'.");
			}

			return bytesTransfered;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="configuration">Netduino needs only the 7 most significant bits for the address e.g. 0x91 >> 1 = 0x48.</param>
		/// <param name="transaction"></param>
		/// <returns></returns>
		private int ExecuteI2CTransactions(I2CDevice.Configuration configuration, I2CDevice.I2CTransaction transaction)
		{
			lock (_I2CLock)
			{
				_I2CDevice.Config = configuration;

				// Execute the read or write transaction, check if byte was successfully transfered
				int bytesTransfered = _I2CDevice.Execute(new[] {transaction}, 100);
				return bytesTransfered;
			}
		}
	}
}
This class takes a I2CDevice.Configuration for each read and write and can be used multiple times for diffrent devices. It should be tread-safe. To get an instance of this class please call "I2CAdapter.Instance" (singleton pattern). The Read/WriteInternalAddressBytes methods are used in Firmware 4.1.1 beta 1 and 4.2.0 RCx. They won't work in 4.0. If there are errors in my code or if there are suggestions for improvement please let me know.
Now you should be able to use the one I2C bus of your Netduino with ease for diffrent devices.

Regards
Guido
  • teo likes this

#3 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 13 April 2012 - 12:47 AM

Guido, Thanks for the (in my view, very complicated) code! :D I'll try it out real soon. Meanwhile, I am trying to understand it... I am not very experienced in C# and some parts of the code like "sealed class" and "lock" doesn't ring a bell! I'll read more and find out.

#4 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 13 April 2012 - 08:05 AM

Hi teo,
the most important thing is that the class will be used like this:
I2CAdapter adapter = I2CAdapter.Instance;
adapter.WriteBytes(...);
instead of something like that:
Picture picture = new Picture();
picture.Load(...);

Regards
Guido
  • teo likes this

#5 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 18 April 2012 - 11:13 PM

Guido,

I tried to use your driver and I can't seem to get it to work. An exception pops up. Odds are, I have used your code the wrong way.

    #### Exception System.NullReferenceException - CLR_E_NULL_REFERENCE (1) ####
    #### Message: 
    #### Netduino.Extender.TransferAdapters.I2CAdapter::ModifyToRepeatedStartTransaction [IP: 0013] ####
    #### Netduino.Extender.TransferAdapters.I2CAdapter::CreateInternalAddressReadTransaction [IP: 0013] ####
    #### Netduino.Extender.TransferAdapters.I2CAdapter::ReadInternalAddressBytes [IP: 000a] ####
    #### NetduinoPlusApplication1.Program::Main [IP: 003a] ####

Can you please see the following code? I have 3 I2C devices on the bus, and I am just trying to read one of them (device_addr = 104) in an infinite loop. The internal registers to read from is 27 to 34 (8 registers to read).

        public static void Main()
        {
            I2CAdapter device = I2CAdapter.Instance;

            I2CDevice.Configuration i2cconf1 = new I2CDevice.Configuration(83, 400);//accl
            I2CDevice.Configuration i2cconf2 = new I2CDevice.Configuration(104, 400);//gyro
            I2CDevice.Configuration i2cconf3 = new I2CDevice.Configuration(30, 400);//mag

            byte[] rbuf = new byte[8];

            while (true)
            {
                device.ReadInternalAddressBytes(i2cconf2, rbuf, 27, 8);
                Debug.Print("");//print results to debug window here
                Thread.Sleep(500);
            }
        }

Thanks for taking the time!

#6 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 19 April 2012 - 07:38 AM

Hi teo, I will try your code at a later time but in the meantime please answer me: which Netduino firmware are you running? Guido P.S: I've tested your code. Works well on firmware 4.2.0.0 RC4. I have none of your devices of course but the exception should occur without them...
  • teo likes this

#7 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 20 April 2012 - 07:02 AM

Well, I don't really know how to tell the firmware version. I have never flashed any of them in since I purchased the board. Can you tell me which firmware version works? And I'll go get that version.

#8 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 20 April 2012 - 10:28 PM

Hi teo,
most possibly your firmware is something like 4.0.6 or so.
A good firmware for repeated start bit support is Version: 4.1.1 BETA 1 (version 4.1.1.0 b1)
You have to update the tiny booter decrompressor too. A superb how to is in the wiki.
After that you can update the firmware of your Netduino.

Version 4.1.1.0 b1 is really stable. My under floor heating runs on 4.1.1.0 b1 till january. The 4.2.0.0 will be great too but until now the same program gets stuck after 6 to 12 hours if it runs on 4.2.

Regards
Guido
  • teo likes this

#9 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 21 April 2012 - 05:56 AM

I have updated my firmware and now there are no errors!

However, the readings I got back are not quite expected. Can you please check the implemention, especially this line:
device.ReadInternalAddressBytes(i2cconf2, rbuf, 27, 8);
I am trying to read 8 registers from one of the devices, starting from register number 27 (or 0x1B). Is this the right way to do it?

#10 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 21 April 2012 - 07:39 AM

Hi teo,
without the data sheet of the gyro I would guess something like that:
device.ReadInternalAddressBytes(i2cconf2, rbuf, 27, 1);
because the fourth parameter is the count of bytes that are used for the internal address and your internal address fits nicely in on byte.
But only the data sheet of your gyro sensor can answer your question.

Guido
  • teo likes this

#11 teo

teo

    Member

  • Members
  • PipPip
  • 12 posts
  • LocationEdmonton, AB

Posted 21 April 2012 - 07:48 AM

I see! Yep, that worked perfect! :lol: Thanks alot!




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.