Module 'IO60P16' - Requires socket 'X' ('Software I2C') not 'I' ('Hardware I2C')?

Sorry for may be a stupid Question:

Why will the ‘IO60P16’ Module ‘Requires socket type X’ (‘Software I2C’), when the used Chip ‘CY8C9560A’ uses a (‘Hardware’) I2C interface and ‘TINYCLR WIKI > Software I2C’ says ‘… When using I2C, it is highly recommended to use the built-in hardware I2C. …’

Thanks in advance

Techno

Reference Links :

.NET-MF FEZ > .NET Gadgeteer > Module > IO60P16 Module
http://www.ghielectronics.com/catalog/product/363

CY8C9560A - Datasheet
http://www.cypress.com/?docID=31413

TINYCLR WIKI- Hardware I2C
GHI Electronics – Where Hardware Meets Software

TINYCLR WIKI- Software I2C
http://wiki.tinyclr.com/index.php?title=Software_I2C

The module uses built-in software I2C which run natively so it is fairly quick enough for this module. GHI provides the drivers for the module so you do not need to worry about it but code is open if you want to check out or change.

Welcome to the community.

Gus,

thanks for your quick reply.

Techno

Does it meen this module can be conected to the Cerberus mainboard which has no X socket?
Thank you for your answer.

The hardware I2C support in NET MF is limited and there are often situations in which it can’t be used. Often vendors tweak the protocol to make a better use of it, like sending multiple start commands, not start-stop as it would be expected. Software I2C implementations gives you more flexibility. I don’t know if you can interact with this module using standard NET MF I2C libraries, Ian has to speak about that (he writes the driver).

Check socket diagram on Cerberus product page. All red ones are X and Y as well.

Absolutely! Any Y socket is also an X socket. Cerberus has plenty of Y sockets.

I’m currently in the process of writing the driver for this module. So, if you have any specific needs/requests please post them on this thread. If you want to make contributes, that’s certainly awesome too. Reading the other thread will let you have a good idea what I’m planning to include. I’m hoping to be mostly done with the single module driver by the end of next week and then I’ll start working on making it where you can chain up to eight modules together.

http://www.tinyclr.com/forum/21/6860/#/2/msg67600

@ Ian
I get a couple of this IO boards and it seems a nice all porpouse interface.
Is there any driver code on Codeplex now ?
Thanks in advance …

I’ve promised Gus a first version before Monday morning. So, it should be coming out with the next SDK release. If you like, I could send you a copy to help us test at the same time. Shoot me your email to ian [at] houseoflees [dot] net.

@ ianlee74 - If you are willing to share your code i would be happy to take a look at it. I remember the datasheet i read to find your bug and I’m wondering how you implemented the protocol.

Gralin, I certainly have no problem sharing it. Gus had just asked that I email it to him rather than post on CodePlex. I’ll post something here for you guys to grab and start playing with by the end of the weekend. Most of this week was blown by install issues with 4.2. So, I’m planning to do a lot of catch-up this weekend and will put something out once it’s cleaned up.

The hardest problem and one I’m still trying to perfect is the automatic clock selection for PWM. I want the public API to not have to deal with the different clocks so that you only have to provide the period (ns) & pulsewidth (ns) or duty cycle (%). This has proven to be more difficult than I would have imagined. I’ll post my low-level PWM functions up here tonight for you to start looking at. I feel like there’s probably a better approach than what I’m doing.

Also, I’ve found that often my calculated period & pulsewidths are not always what I would consider “close enough” to what is requested. I’m wondering if this is due to inaccuracies in the clock timings and if I should bother trying to soft-calibrate the period values based on measured differences or just let it go with what the chip gives. I don’t know if these variances are global or will differ from module to module. Once I get this first version done, my next steps will be to hook up a second module and do some of these types of comparisons. Everything is a struggle to try and keep the API feeling natural but also keep the CPU cycles down.

@ Gralin - See post here…
http://www.tinyclr.com/forum/topic?id=6860&page=4#msg68587

I thank you very much Ian. I will email to you today but I’m not in a hurry take your time.

This is sadly incorrect. The hardware implementation CAN send multiple start commands. In C# this is done via a multi part transaction. This does NOT work in the software implementation, causing issues as seen by me and Ian.

@ GMod(Errol) - I remember that when i wrote the driver for ST CRX14 card reader I had to use software I2C because the API of the hardware version didn’t allow me to implement the protocol. You can see the details in the source code:

http://crx14.codeplex.com

But I didn’t use the GHI distributed software I2C implementation either. I used the port made by @ MarkH. Maybe this is an idea to also use this library to check out if I2C library is the problem. I know that C# implementation makes it very slow but it works and may be useful.

//
//                  Software I2C Driver - 19 April 2011.
//                               Al Moyle
//
//   Based on original I2C Bus Master Code from http://en.wikipedia.org/wiki/I%C2%B2C -
//                Ported to C# by Gus and modified by MarkH.
//               See http://www.tinyclr.com/forum/1/1647/#/1/
//

using System;
using Microsoft.SPOT.Hardware;

namespace Gralin.NETMF.ST.CRX14.SoftwareI2C
{
    class TimeOutException : ApplicationException
    {
        public TimeOutException()
            : base("Timeout Error")
        {
        }

        public TimeOutException(string message)
            : base(message)
        {
        }
    }

    public class SoftwareI2C
    {
        TristatePort scl, sda;
        int i2cSpeed = 100;              // This variable isn't used ... Remove it?
        int timeOut = 10;                // Clock stretching time out interval in msec.
        long timeOutTicks = 0;           // Number of ticks in time out interval.

        bool start;

        public SoftwareI2C(Cpu.Pin SCL, Cpu.Pin SDA, int clock)
        {
            start = false;
            timeOutTicks = (long)timeOut * TimeSpan.TicksPerMillisecond;
            //
            scl = new TristatePort(SCL, false, false, Port.ResistorMode.PullUp);
            sda = new TristatePort(SDA, false, false, Port.ResistorMode.PullUp);
            // scl and sda default to input, the next 2 lines make this explicit.
            MakePinInput(scl);
            MakePinInput(sda);
            //i2cSpeed = clock/ 1111????????????????
        }

        static void MakePinOutput(TristatePort port)
        {
            if (!port.Active)
                port.Active = true;
        }

        static void MakePinInput(TristatePort port)
        {
            if (port.Active)
                port.Active = false;
        }

        //
        // I left this routine in place, but didn't add any delay code.  With
        //  no additional delay beyond the overhead associated with a call to
        //  I2CDELAY, the serial clock frequency is approximately 1.5 KHz.
        //
        void I2CDELAY(int delay)
        {
            //add code for delay
        }

        void ClearSCL()
        {
            MakePinOutput(scl);
        }

        void ClearSDA()
        {
            MakePinOutput(sda);
        }

        bool ReadSCL()
        {
            MakePinInput(scl);
            return scl.Read();
        }

        bool ReadSDA()
        {
            MakePinInput(sda);
            return sda.Read();
        }

        bool ReadBit()
        {
            // "ReadSDA" makes SDA an input - processor lets go of pin and internal
            //  pull-up resistor makes it high.  Now slave can drive the pin.
            ReadSDA();

            I2CDELAY(i2cSpeed);

            // Clock stretching - Makes SCL an input and pull-up resistor makes
            //  the pin high.  Slave device can pull SCL low to extend clock cycle.
            long endStretch = Utility.GetMachineTime().Ticks + timeOutTicks;
            while (!ReadSCL())
            {
                // How long have we been stuck in the while loop?
                if (Utility.GetMachineTime().Ticks >= endStretch)
                    throw new TimeOutException();           // Too long, so bail out by throwing an exception.
            }

            // At this point, SCL is high and SDA is valid - so read the bit.
            bool bit = sda.Read();

            I2CDELAY(i2cSpeed);

            ClearSCL();     // Pull the serial clock line low ...

            return bit;     //  and return.
        }

        bool WriteBit(bool bit)
        {
            if (bit)
            {
                ReadSDA();      // Make SDA an input ... so pin is pulled up.
            }
            else
            {
                ClearSDA();     // Make SDA an output ... so pin is pulled low.
            }

            I2CDELAY(i2cSpeed);
            // Clock stretching - Makes SCL an input and pull-up resistor makes
            //  the pin high.  Slave device can pull SCL low to extend clock cycle.
            long endStretch = Utility.GetMachineTime().Ticks + timeOutTicks;
            while (!ReadSCL())
            {
                // How long have we been stuck in the while loop?
                if (Utility.GetMachineTime().Ticks >= endStretch)
                    throw new TimeOutException();           // Too long, so bail out by throwing an exception.
            }
            // SCL is high and SDA is valid ...
            //  Check that nobody else is driving SDA
            if (bit && !ReadSDA())
            {
                return false;       // Lost arbitration
            }

            I2CDELAY(i2cSpeed);
            ClearSCL();

            return true;           // Success!
        }

        bool SendStartCondition()
        {
            if (start)
            {
                // set SDA to 1 
                ReadSDA();
                I2CDELAY(i2cSpeed);
                //
                // Clock stretching - Makes SCL an input and pull-up resistor makes
                //  the pin high.  Slave device can pull SCL low to extend clock cycle.
                long endStretch = Utility.GetMachineTime().Ticks + timeOutTicks;
                while (!ReadSCL())
                {
                    // How long have we been stuck in the while loop?
                    if (Utility.GetMachineTime().Ticks >= endStretch)
                        throw new TimeOutException();           // Too long, so bail out by throwing an exception.
                }
            }

            if (!ReadSDA())
            {
                return false;
            }

            // SCL is high, set SDA from 1 to 0 
            ClearSDA();
            I2CDELAY(i2cSpeed);
            ClearSCL();

            start = true;

            return true;
        }

        bool SendStopCondition()
        {
            // set SDA to 0 
            ClearSDA();
            I2CDELAY(i2cSpeed);
            //
            // Clock stretching - Makes SCL an input and pull-up resistor makes
            //  the pin high.  Slave device can pull SCL low to extend clock cycle.
            long endStretch = Utility.GetMachineTime().Ticks + timeOutTicks;
            while (!ReadSCL())
            {
                // How long have we been stuck in the while loop?
                if (Utility.GetMachineTime().Ticks >= endStretch)
                    throw new TimeOutException();           // Too long, so bail out by throwing an exception.
            }
            //
            // SCL is high, set SDA from 0 to 1 
            if (!ReadSDA())
            {
                return false;
            }

            I2CDELAY(i2cSpeed);
            start = false;

            return true;
        }

        /// <summary>
        /// Sends data to the remote device
        /// </summary>
        /// <param name="sendStartCondition">Perform a HIGH to LOW transition of SDA line while the SCL is high.</param>
        /// <param name="sendStopCondition">Perform a LOW to HIGH transition of SDA line while the SCL is high.</param>
        /// <param name="byteToSend">Byte to transmit</param>
        /// <returns></returns>
        public bool Transmit(bool sendStartCondition, bool sendStopCondition, byte byteToSend)
        {
            if (sendStartCondition)
            {
                try
                {
                    SendStartCondition();
                }
                catch (TimeOutException e)
                {
                    throw new TimeOutException(e.Message);
                }
            }
            for (int bit = 0; bit < 8; bit++)
            {
                try
                {
                    WriteBit((byteToSend & 0x80) != 0);
                }
                catch (TimeOutException e)
                {
                    throw new TimeOutException(e.Message);
                }

                byteToSend <<= 1;
            }
            bool nack;
            try
            {
                nack = ReadBit();
            }
            catch (TimeOutException e)
            {
                throw new TimeOutException(e.Message);
            }
            //
            if (sendStopCondition)
            {
                try
                {
                    SendStopCondition();
                }
                catch (TimeOutException e)
                {
                    throw new TimeOutException(e.Message);
                }
            }
            // Return value is "true" for NAK
            //  "false" for ACK.
            return nack;
        }

        /// <summary>
        /// Receive data from remote device.
        /// </summary>
        /// <param name="acknowledgeBit">
        /// Each device when addressed to has to generate an acknowledge signal after the reception of each byte. 
        /// The master generates an extra clock pulse which is associated with the <c>acknowledgeBit</c>. 
        /// The device that acknowledges pulls down the SDA line during the acknowledge clock pulse.
        /// </param>
        /// <param name="sendStopCondition">Perform a LOW to HIGH transition of SDA line while the SCL is high.</param>
        /// <returns></returns>
        ///
        //  18 April 2011 - Changed parameter "acknowledgeBit" to "sendAcknowledgeBit" to make it consistent with
        //   "sendStopCondition" and parameters of the "Transmit" routine.
        public byte Receive(bool sendAcknowledgeBit, bool sendStopCondition)
        {
            byte d = 0;
            bool b;

            for (int bit = 0; bit < 8; bit++)
            {
                d <<= 1;

                try
                {
                    b = ReadBit();
                }
                catch (TimeOutException e)
                {
                    throw new TimeOutException(e.Message);
                }
                if (b)
                    d |= 1;
            }
            //
            try
            {
                WriteBit(!sendAcknowledgeBit);
            }
            catch (TimeOutException e)
            {
                throw new TimeOutException(e.Message);
            }
            //
            if (sendStopCondition)
            {
                try
                {
                    SendStopCondition();
                }
                catch (TimeOutException e)
                {
                    throw new TimeOutException(e.Message);
                }
            }
            //
            return d;
        }

    }
}

And this is how i use it:

using Microsoft.SPOT.Hardware;

namespace Gralin.NETMF.ST.CRX14.SoftwareI2C
{
    public class SoftwareI2CDevice : IDevice
    {
        private readonly SoftwareI2C _device;
        private readonly byte _readCmd;
        private readonly byte _writeCmd;

        public SoftwareI2CDevice(byte address, Cpu.Pin sdaPin, Cpu.Pin sclPin)
        {
            _device = new SoftwareI2C(sclPin, sdaPin, 400);
            _writeCmd = (byte) ((address << 1) + 0);
            _readCmd = (byte) ((address << 1) + 1);
        }

        #region IDevice Members

        public bool Write(byte regAddress, byte[] data)
        {
            var noData = data == null || data.Length == 0;

            _device.Transmit(true, false, _writeCmd);
            _device.Transmit(false, noData, regAddress);

            if (!noData)
                for (var i = 0; i < data.Length; i++)
                    _device.Transmit(false, i + 1 == data.Length, data[i]);

            return true;
        }

        public byte[] Read(byte regAddress, byte dataLength)
        {
            return WriteRead(regAddress, null, dataLength);
        }

        public byte[] WriteRead(byte regAddress, byte[] data, byte readDataLength)
        {
            Write(regAddress, data);

            var noData = readDataLength == 0;
            var result = new byte[readDataLength];

            _device.Transmit(true, noData, _readCmd);

            if (!noData)
                for (var i = 0; i < readDataLength; i++)
                    result[i] = _device.Receive(i + 1 < readDataLength, i + 1 == readDataLength);

            return result;
        }

        #endregion
    }
}

I used that same software I2C lib for my cap sense key pad on my CANxtra, because the keyboard header doesn’t have I2C and the GHI software implementation didn’t have restart.

The hardware does have restart and I have used it. You use it like this:

Microsoft.SPOT.Hardware.I2CDevice EEPROM = new Microsoft.SPOT.Hardware.I2CDevice(new Microsoft.SPOT.Hardware.I2CDevice.Configuration(0x11, 400));
I2CDevice.I2CTransaction[] Read = new I2CDevice.I2CTransaction[2];
byte[] Data = new byte[10];

Read[0] = I2CDevice.CreateWriteTransaction(new byte[] { 1 });
Read[1] = I2CDevice.CreateReadTransaction(Data);
EEPROM.Execute(Read,100);

This sends a Start for the first transaction and an End after the last transaction, in between it sends Restarts…

@ gralin - thanks. I’ll get that a shot tonight.

@ gmod - thanks. I tried that. However, my hardware I2C implementation isn’t working well enough that I could really get to that point. My initial write to the register to set the pin to output mode is failing. So, I can’t really get far enough to try a transaction yet. See the code I checked in last night from the other thread. I can definitely see the advantage of allowing transactions like this. It would be nice to have this added to SoftwareI2C.