How to use the I2C interface?

Yesterday I got my fez spider kit (it takes a long time to germany ;))
After my first “hello world” test I’ve wired a DS1307 on the extender module connected to slot 3: GND,5V,SDA->P8,SCL->P9.
Unfortunately I can’t find any documentation or example on how to get access to the I2C bus.
After several tries I’m using the following initialization now:

m_I2C = new I2CBus(GT.Socket.GetSocket(3, true, m_Extender, "I"), 0x68 /* DS1307 */, 1 /* kHz */, m_Extender);

This seems to work, at least I got no exception :wink:
Now I’m trying to read the first 7 bytes from the DS1307:

byte[] time = new byte[7];
m_I2C.Read(time, 200 /* ms??? */);

But now I got a System.ArgumentException in Microsoft.SPOT.Hardware.dll without a text.
Any ideas?

PS: My plan is to create a controller for my reef tank (at first lightning control)

Looks like an exciting first project. can you try to use I2C directly? Do not wory about sockets used. Connect your I2C device and use is exactly how it is explained here http://wiki.tinyclr.com/index.php?title=I2C_-_EEPROM

Welcome to the community.

Thanx Gus.
At my first attempt (with I2CDevice now) I got exactly the same exception.
But then I change the clock rate from 1 KHz to 100 KHz.
And then it works.
It’s quite obvious that 1 will result in an exception.
So I’ve tested my first approach (with I2CBus) again.
No exception anymore, the correct length as return code, but only zeros as result.
Curious…

But it’s working now with I2CDevice, thanks :wink:

Great, can you post more details on this? You are the first one to show how gadgeteer can be expanded to things other than modules. We would love to tweet about this if you post some details.

Perhaps new post explaining what you did or a wiki page :slight_smile:

Watch out for the magic smoke! :slight_smile:

Where’s the DS1307 in the picture? Hiding behind the extender or battery? And what is the battery for? RTC?

I’ve had good luck with serial com using the extender module, but haven’t tried I2C yet. Some breakout modules for the more common components like this would be nice to have. GHI has promised us a whole bunch of new open source module design to be released soon. Maybe we’ll see something like an I2C breakout (as well as UART, SPI, CAN, etc).

The DS1307 is on the other side :wink:
I’ve bought it somewhere. It’s nice for using it with a breadboard.
In the picture one can see the current time.
At the moment, my class can read and set the time only.
Later I will use the battery backed 56 bytes of ram for saving my settings.
But my next step is adding helper classes for time based events.
Then dimming my 64 cree leds, moonlight routines and so on…

Edit: I’ve completed the class, see below…

Did you wrote the code for the DS1307 from scratch?
Shame on you…Should have looked on the Code section: http://code.tinyclr.com/project/30/rtc1307/ could have saved you some time.

I was on the wrong track by my intention to use the gadgeteer class I2CBus instead of I2CDevice.
I didn’t found any example, so I create a new class based on my old C++ implementation running on an ATMega.
I will replace some parts with your implementation.
It’s better to use the common DateTime class, I like it :wink:
Thanks for the link!

Hi Belgah,

Its James Scott from the .NET Gadgeteer team at Microsoft here. Thanks for trying out the Gadgeteer.Interfaces.I2CBus class!

The Gadgeteer.Interfaces classes, in general, are supposed to help module developers by providing a layer of abstraction between modules and sockets/pins. So they (a) take the socket number and internally sort out which pins/interfaces correspond to that socket number, (b) throw helpfully worded exceptions when e.g. a user plugs in a module to an incompatible socket, avoiding the need to do most error case checks in module code (c) tell GadgeteerCore to reserve used pins, which is necessary since it is permissible for mainboards to share the same pin across multiple sockets, if there is a shortage of pins (which FEZ Spider by and large doesnt do).

Having said that, the I2C bus is a funny case since there is a single shared I2C bus for all the I2C-supporting sockets. So it works fine to just use the Microsoft.SPOT.Hardware.I2CDevice; indeed the Gadgeteer.Interfaces.I2CBus class mostly just wraps I2CDevice. But one thing we failed to wrap was the transactions functionality (e.g. a read followed by a write), which it seems is what you need. We’ll fix this in the next release of .NET Gadgeteer Core (which will be version 2.41.400). No release date set yet, sorry, but your code using I2CDevice below works fine due to the singleton nature of the I2C bus.

We’d be happy to get any more feedback either here or directly at gadgeteer@ microsoft.com .

Welcome to the forum James!

Nice to see Gadgeteer people here. ;D

I can’t express how happy I am to see the gadgeteer team going far and beyond to help us and and the community into building up the gadgeteer world. As gadgeteer being so new, the input we get from the gadgeteer team is priceless and really shows how they really care to build gadgeteer with community input.

So THANK you James :slight_smile: I am sure the whole community would appreciate you directing us in the right direction.

Hi James,
thanks for your explanation :).
It was exactly my intention to use the gadgeteer approach of hiding hardware specific things.
In case of I2C it might be senseless, 'cause a processor has usually one I2C interface in hardware.
But maybe sometimes a software I2C will be supported by I2CBus, too :wink:

I have completed the DS1307 class now.
Some parts are used from Eric’s implementation (I’ve fixed some minor things there).
In addition I’ve added the RAM functions.
They are working well, too.


  public class DS1307 : IDisposable
  {

    #region Constants

    public const ushort ADDRESS = 0x68;       // DS1307
    public const ushort I2C_CLOCK = 100;      // kHz
    public const ushort I2C_TIMEOUT = 1000;   // ms
    public const int START_RAM_ADDRRESS = 0x08;
    public const int MAX_RAM_ADDRRESS = 0x3F;

    #endregion

    #region Attributes

    private readonly I2CDevice m_I2cDevice;

    #endregion

    #region Methods

    #region public DS1307()
    public DS1307()
    {
      m_I2cDevice = new I2CDevice(new I2CDevice.Configuration(ADDRESS, I2C_CLOCK));
    }
    #endregion

    #region public void Dispose()
    public void Dispose() 
    {
      m_I2cDevice.Dispose();
    }
    #endregion

    #region public static byte BcdToDec(byte val)
    public static byte BcdToDec(byte val)
    {
      return (byte)(((val >> 4) * 10) + (val & 0xF));
    }
    #endregion

    #region public static byte DecToBcd(byte val)
    public static byte DecToBcd(byte val)
    {
      return (byte)(((val / 10) << 4) + (val % 10));
    }
    #endregion

    #region public DateTime GetTime()
    public DateTime GetTime()
    {
      I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[2];
      xActions[0] = I2CDevice.CreateWriteTransaction(new byte[1] { 0x00 });
      byte[] rtcTime = new byte[7];
      xActions[1] = I2CDevice.CreateReadTransaction(rtcTime);

      int ret = m_I2cDevice.Execute(xActions, I2C_TIMEOUT);
      if (0 == ret)
      {
        throw new Exception("Failed to perform I2C transaction");
      }

      int sec = BcdToDec(rtcTime[0]); 
      int min = BcdToDec(rtcTime[1]);
      int hour = BcdToDec(rtcTime[2]);
      int day = BcdToDec(rtcTime[4]);
      int month = BcdToDec(rtcTime[5]);
      int year = BcdToDec(rtcTime[6]) + 2000;

      // the DS1307 allows day 0 which results in an exception in DateTime
      if (0 == day)
        ++day;

      // the DS1307 allows month 0 which results in an exception in DateTime
      if (0 == month)
        ++month;

      return new DateTime(year, month, day, hour, min, sec);
    }
    #endregion

    #region public void SetTime(DateTime newTime)
    public void SetTime(DateTime newTime)
    {
      I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[1];
      byte[] sb = new byte[8] { 
        0x00, 
        DecToBcd((byte)newTime.Second), 
        DecToBcd((byte)newTime.Minute), 
        DecToBcd((byte)newTime.Hour), 
        DecToBcd((byte)newTime.DayOfWeek), 
        DecToBcd((byte)newTime.Day), 
        DecToBcd((byte)newTime.Month), 
        DecToBcd((byte)(newTime.Year - 2000)) };
      xActions[0] = I2CDevice.CreateWriteTransaction(sb);

      int ret = m_I2cDevice.Execute(xActions, I2C_TIMEOUT);
      if (0 == ret)
      {
        throw new InvalidOperationException();
      }
    }
    #endregion

    #region public void GetRAM(ref byte[] data, int address)
    public void GetRAM(ref byte[] data, int address)
    {
      byte realAddress = (byte)(address + START_RAM_ADDRRESS);

      // check range
      int len = data.Length;
      if (len + realAddress >= MAX_RAM_ADDRRESS)
        throw new ArgumentException();

      I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[2];
      xActions[0] = I2CDevice.CreateWriteTransaction(new byte[1] { realAddress });
      xActions[1] = I2CDevice.CreateReadTransaction(data);

      int ret = m_I2cDevice.Execute(xActions, I2C_TIMEOUT);
      if (0 == ret)
      {
        throw new InvalidOperationException();
      }
    }
    #endregion

    #region public void SetRAM(byte[] data, int address)
    public void SetRAM(byte[] data, int address)
    {
      byte realAddress = (byte)(address + START_RAM_ADDRRESS);
      
      // check range
      int len = data.Length;
      if (len + realAddress >= MAX_RAM_ADDRRESS)
        throw new ArgumentException();

      byte[] transferData = new byte[len + 1];

      // copy data
      transferData[0] = realAddress;
      for (int i = 0; ++i <= len; )
        transferData[i] = data[i-1];

      I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[1];
      xActions[0] = I2CDevice.CreateWriteTransaction(transferData);

      int ret = m_I2cDevice.Execute(xActions, I2C_TIMEOUT);
      if (0 == ret)
      {
        throw new InvalidOperationException();
      }
    }
    #endregion

    #endregion

  }

The RAM functions are nice. Maybe add a length to them so you can read a specific area.
Do you mind if I add the modifications to the code share?

Of course you can add it there :wink:

Hi Belgah,

The release 2.41.400 is now up at http://gadgeteer.codeplex.com/ - I’m sure GHI will be releasing a new “package” installer soon, but if you wanted to you could use the GadgeteerCore installer on codeplex now and it will upgrade correctly (and it will be fine to run GHI’s package installer later on top of it).

This includes the I2C transactions API on GTI.I2CBus that you highlighted as missing. Of course, as we discussed the I2C case is special as there is only one I2C bus so your module code works without using the GTI abstraction, but if you wanted to use it you now can (and you will for example see some nicely formatted error messages in case of pin conflicts or incorrect sockets that GadgeteerCore takes care of). Thanks for your feedback and I hope it’s useful to you and others.

Ta
James