IO60P16 Module driver discussion thread (Contributors)

@ dobova - now that I’m home and have had a chance to study this more closely, I’m not sure it’s doing quite what I need. If I understand correctly, you have demonstrated how to read the values from an [em]input [/em]port. This is certainly something I need but the current problem I’m trying to solve is how to read the current values of an [em]output [/em]port. I don’t think changing the port to input mode is an valid option. I have to be able to read the current values of the output port in order to flip only one of the values. Could you also post your GetPortInput() and SetPortOutput() functions for comparison. Thanks!

@ Ian
Ok, I explained very bad the things going on. Sorry it’s my fault.
I set (see code) Port 0 as output and port 1 as input.
Becouse I wanted some signal to read back on port 1 I simply jumped (with 4 physical wires) the bit 0,1,2,3 of output port 0 to input port 1. So done, the logical condition I set on pin 0,1,2,3 of port 0 is presented on pin0,1,2,3 of port 1, and this condition can be read back. Just to make the thing visually, I’ve 8 leds on pin 0-7 of port 3.
The “amazing useful” program for testing I’ve done make this steps:


void Io60p_Thread()
        {
// 1.setup the port output mode (see datasheet)
            iO60P16.SetPortMode(3, PORT_DRIVEMODE.RES_PULLDOWN);
            iO60P16.SetPortMode(0, PORT_DRIVEMODE.RES_PULLDOWN);
            iO60P16.SetPortMode(1, PORT_DRIVEMODE.HIGH_Z); // important setting to get back signal on port
// 2. set port 0 and port 1 pins to logic 0
            iO60P16.SetPortOutput(1, 0x00); // pull down all bits
            iO60P16.SetPortOutput(0, 0x00); //  pull down al bits
// 3. now set port 1 as input (all the pins input)
            iO60P16.SetPortDirection(1, 0xff); // set port 1 all pin input
            byte rd_val = 0, dummy=0;
            while (true)
            {
                if (cc > 255)
                    cc = 0;
// 4. output on pin 0,1,2,3 the value of cc (i use only 4 bits, I've only jumped pin 0,1,2,3 of port 0 with pin0,1,2,3 of port 1)
                iO60P16.SetPortOutput(0, (byte)(cc & 0x0f));
// 5. now get the logic levels present on port 1 pins
                rd_val = iO60P16.GetPortInput(1);
// 6. now read back register of port 0 (output port) (checked and working fine)
                dummy  =  iO60P16.GetPortInput(0);
                Debug.Print("Val:" + rd_val.ToString() + " - Read back val:" + dummy.ToString());
// 7. to create some more i2c packets, now write the read value of port 1 to port 3 this time, and correspondin leds will light 
                iO60P16.SetPortOutput(3, rd_val);
                cc++;
                Thread.Sleep(50);
            }
        }

The GetPortInput() simply read back the register relative to a port, no special code, just calls ghi driver read register.
I hope to have been more clear. And now I get what you are meaning, reading back a register related to a port that is set as output.
I’m not reading back register, becouse port 1 pins has physical wire connection with port 0.
Now I’m not in lab, but as I can, I’ll put here the code to read an output port value. I’ve done it to poll PWM pin state and it works.
Before any code for writing a driver, I need to understand the chip and the capabilities/limitation of it.


Val:10 - Read back val:10
Val:11 - Read back val:11
Val:12 - Read back val:12
Val:13 - Read back val:13
Val:14 - Read back val:14
Val:15 - Read back val:15
Val:0 - Read back val:0
Val:1 - Read back val:1
Val:2 - Read back val:2
Val:3 - Read back val:3
Val:4 - Read back val:4
Val:5 - Read back val:5
Val:6 - Read back val:6

as you can see, I can read port 1 (“val:”) and port 0 (“read back val:”) with no issue, the value are the same I’ve written in register.


        public void SetPortMode(byte port, PORT_DRIVEMODE mode_reg)
        {
            byte hreg = 0x18;
            WriteRegister(hreg, port);
            byte breg = (byte)mode_reg;
            WriteRegister(breg, 0xff);
        }

        // mask = 0bxxxxxxxx where x=1 input pin, x=0 ouput pin
        public void SetPortDirection(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x1C;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);

        }

        public void EnableInterruptPort(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x19;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);            
        }

        public void SetPortPWM(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x1A;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);            
        }

        public void SetPortOutput(byte port, byte data)
        {
            WritePort(port, data);
        }

        public byte GetPortInput(byte port)
        {
            byte ret;
            ret = ReadPort(port);
            return ret;
        }
        public byte GetStatusPort(byte port)
        {
            byte ret;
            ret = ReadRegister((byte)(0x10 + port));
            return ret;
        }
    }


I’ve complicated all the staff for nothing. lol
if you need to read back a port you need to use[em] “input port” regs 00h-07h NOT “output port” regs 08h-0Fh [/em]

Ah… I was trying to momentarily change the port to be an input port and then read the data. That wasn’t working. I’ll try what you’re suggesting and see if that works.

You don’t need to change the direction bit of the port, just read it as you go with an input port.

Uggg… Could you also post your ReadPort(). I’m still getting 255. That’s the only thing that I can’t see. Everything else we’re doing the same way. My test is very simple. Let me know if you see something I should be doing differently.

            
io60p16.SetPortMode(7, DriveMode.ResistivePullDown);
io60p16.SetPortDirection(7, PortDirection.Output);
io60p16.Write(7, 0x00);           // Bring all pins low.
io60p16.Write(7, 0x02);           // Bring PWM9 high.
Debug.Print(io60p16.Read(7).ToString());        // This should print "2".  It prints "255" instead.

I’m headin’ to Florida tomorrow afternoon for some much needed vacation. If I don’t hear from you tonight, we’ll try again on Monday.

Mhhh so strange … what’s mean … are you sure that the port mode is correctly set to pulldown, sure that you read back the same port you write ?
What mainboard are you using ? I’m using Hydra and 4.1 fw.



using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
using Gadgeteer.Interfaces;

namespace Gadgeteer.Modules.Ledonet
{
    public enum PORT_DRIVEMODE
    {
        RES_PULLUP = 0x1D, //Resistive Pull Up Resistive High, Strong Low (default)
        RES_PULLDOWN = 0x1E, //Resistive Pull Down Strong High, Resistive Low
        OPENDRAIN_HIGH = 0x1F, //Open Drain High Slow Strong High, High Z Low
        OPENDRAIN_LOW = 0x20, //Open Drain Low Slow Strong Low, High Z High
        STRONG_DRIVE = 0x21, //Strong Drive Strong High, Strong Low, FastOutput Mode
        SLOW_STRING_DRIVE = 0x22, //Slow Strong Drive Strong High, Strong Low,Slow Output Mode
        HIGH_Z = 0x23  //High Impedance High Z
    }
    /// <summary>
    /// A IO60P16 module for Microsoft .NET Gadgeteer
    /// </summary>
    public class IO60P16 : GTM.Module
    {        
        private static GTI.SoftwareI2C i2c;
        private static GTI.I2CBus i2c_hw;

        //
        private Socket _socket;
        public Socket Socket
        {
            get { return _socket; }
            set { _socket = value; }
        }

        // event handler holder
        InterruptInput interrupt = null;

        /// <summary>
        /// Address of the device.
        /// </summary>
        public const byte DEV_ADDR = 0x20;
        public const byte DEV_ADDR_EEPROM = 0xA2;


        // Interrupt handler delegate
        public delegate void InterruptHandler(object sender, EventArgs args);

        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public IO60P16(int socketNumber)
        {
            _socket = Socket.GetSocket(socketNumber, true, this, null);
            char[] types = new char[] { 'X', 'Y' };
            _socket.EnsureTypeIsSupported(types, this);

            i2c = new GTI.SoftwareI2C(_socket, Socket.Pin.Five, Socket.Pin.Four, this);
            i2c_hw = new GTI.I2CBus(_socket, 0x20, 100, null);
        }

        // Interrupt event handler setup. (ToDo: set parameters to setup chip interrupt settings)
        public void StartInterruptHandler(InterruptInput.InterruptEventHandler interrupthandler)
        {
            interrupt = new InterruptInput(_socket, GT.Socket.Pin.Three, GlitchFilterMode.Off, ResistorMode.Disabled, InterruptMode.RisingEdge, null);
            interrupt.Interrupt += new InterruptInput.InterruptEventHandler(interrupthandler);
        }

        public void StopInterruptHandler(InterruptInput.InterruptEventHandler interrupthandler)
        {
            if (interrupt != null)
            {
                interrupt.Interrupt -= new InterruptInput.InterruptEventHandler(interrupthandler);
            }
        }
        /// <summary>
        /// Writes a value to a register.
        /// </summary>
        /// <param name="reg">The register to write to.</param>
        /// <param name="value">The value to write.</param>
        public void WriteRegister(byte reg, byte value)
        {
            byte[] data = new byte[] { reg, value };
            int send = i2c.Write(DEV_ADDR, data);
        }
        public void WriteRegisterHw(byte reg, byte value)
        {
            byte[] data = new byte[] { reg, value };
            int send = i2c_hw.Write(data, 1000);

        }
        /// <summary>
        /// Reads a value from a register.
        /// </summary>
        /// <param name="reg">The register to read from.</param>
        /// <returns>The value in the register.</returns>
        public byte ReadRegister(byte reg)
        {
            byte[] data = new byte[1];
            // Bring the pointer to the needed address
            int send = i2c.Write(DEV_ADDR, new byte[] { reg });
            // Read the address
            i2c.Read(DEV_ADDR, data);
            return data[0];
        }

        /// <summary>
        /// Writes a value to the specified port.
        /// </summary>
        /// <param name="port">Port to write to.</param>
        /// <param name="value">Value to write to the port.</param>
        private void WritePort(byte port, byte value)
        {
            // Write data start from register 0x08
            WriteRegister((byte)(0x08 + port), value);
            //WriteRegisterHw((byte)(0x08 + port), value);
        }
        
        /// <summary>
        /// Reads the value of a port.
        /// </summary>
        /// <param name="port">The port to read.</param>
        /// <returns>The value of the port.</returns>
        private byte ReadPort(byte port)
        {
            // Read data start from register 0x00
            return ReadRegister((byte)(0x00 + port));
        }

        /// <summary>
        /// Makes a pin an output pin.
        /// </summary>
        /// <param name="port">The port that the pin is in.</param>
        /// <param name="pin">The pin to make an output pin.</param>
        public void MakePinOutput(byte port, byte pin)
        {
            WriteRegister(0x18, port); // Select port
            byte b = ReadRegister(0x1C); // Return value
            b &= (byte)(~(1 << pin)); // 0 is out put
            WriteRegister(0x1C, b);   // write to register

        }

        /// <summary>
        /// Makes a pin an input pin.
        /// </summary>
        /// <param name="port">The port that the pin is in.</param>
        /// <param name="pin">The pin to make an input pin.</param>
        public void MakePinInput(byte port, byte pin)
        {
            WriteRegister(0x18, port); // Select port
            byte b = ReadRegister(0x1C); // Return value
            b |= (byte)((1 << pin)); // 1 is input
            WriteRegister(0x1C, b); // write to register
        }

        /// <summary>
        /// Makes a pin high.
        /// </summary>
        /// <param name="port">The port that the pin is in.</param>
        /// <param name="pin">The pin to make high.</param>
        public void MakePinHigh(byte port, byte pin)
        {
            // Read port
            byte b = ReadPort(port);
            // Config pin
            b |= (byte)(1 << (pin));
            // Apply
            WritePort(port, b);
        }

        /// <summary>
        /// Makes a pin low.
        /// </summary>
        /// <param name="port">The port that the pin is in.</param>
        /// <param name="pin">The pin to make low.</param>
        public void MakePinLow(byte port, byte pin)
        {
            // Read port
            byte b = ReadPort(port);
            // Config pin
            b &= (byte)~(1 << (pin));
            // Config pin
            WritePort(port, b);
        }

        public void SetPortMode(byte port, PORT_DRIVEMODE mode_reg)
        {
            byte hreg = 0x18;
            WriteRegister(hreg, port);
            byte breg = (byte)mode_reg;
            WriteRegister(breg, 0xff);
        }

        // mask = 0bxxxxxxxx where x=1 input pin, x=0 ouput pin
        public void SetPortDirection(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x1C;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);

        }

        public void EnableInterruptPort(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x19;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);            
        }

        public void SetPortPWM(byte port, byte mask)
        {
            byte hreg = 0x18, breg = 0x1A;
            WriteRegister(hreg, port);
            WriteRegister(breg, mask);            
        }

        public void SetPortOutput(byte port, byte data)
        {
            WritePort(port, data);
        }

        public byte GetPortInput(byte port)
        {
            byte ret;
            ret = ReadPort(port);
            return ret;
        }
        public byte GetStatusPort(byte port)
        {
            byte ret;
            ret = ReadRegister((byte)(0x10 + port));
            return ret;
        }
    }
}

This is my rough testing driver, that inherit code from GHI original drive.

I think we may have a problem with 4.2. That’s what I’m using.

I copied your driver exactly and then created the following test project and I still get 255. Could you try running my test project in 4.1 exactly as it is and see if it works for you? Also, try it on socket #5 just so we have everything exactly the same.


using Gadgeteer.Modules.GHIElectronics.IO60P16;
using Microsoft.SPOT;
using GTM = Gadgeteer.Modules;

namespace Test_Dobova_Driver
{
    public partial class Program
    {
        private static GTM.GHIElectronics.IO60P16.IO60P16_Dobova io60p16;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            io60p16 = new GTM.GHIElectronics.IO60P16.IO60P16_Dobova(5);

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            const byte PORT = 7;

            io60p16.SetPortMode(PORT, PORT_DRIVEMODE.RES_PULLDOWN);
            io60p16.SetPortDirection(PORT, 0xff);
            io60p16.SetPortOutput(PORT, 0x00);                          // This works.
            io60p16.SetPortOutput(PORT, 0x02);                          // This works.
            Debug.Print(io60p16.GetPortInput(PORT).ToString());         // This should print "2".  It prints "255" instead.        
        }
    }
}

Hmm, I think I was elsewhere that people are having issues with SPI and 4.2. Maybe I2C too?

Gus, are you looking into any 4.2 issues with SPI that might also effect I2C?

4.2 is very new and in early beta stages now so anything is possible.

ahhh … ok that can explain the issue … I will analyze 4.2 software i2c protocol and see what happen. In 4.1 no issue. I’ve interrupt runnig with events and no problem at all.
I will try later to use your code on 4.1
I will also try my code on 4.2 cerberus.

PS: I’ve big problems on forum due to “s7.addthis.com” server not responding here … it’s just my problem ?

OMG … I’m getting little bit confused …
But where is SoftwareI2C class in Gadgeteer 4.2 ??? It is included in Gadgeteer.Interface ? I see only one SoftwareI2CBus in GHI.OSH dll…

Ok just FYI, I’ve got SoftwareI2C from source code (same as 4.1; I included it in my 4.2 prj) and my IO64 pseudo-driver works perfectly also on Cerberus with 4.2 (Gadgeteer 2.42.600) including interrupt.
I’ll try to test with GHI.OSH.SoftwareI2CBus, bus need to look at schematic to get right pins on the MCU and socket.

I’m not at a computer where I can check…but I think it’s in Microsoft.SPOT.Hardware.I2C. It’s definitely not in a GHI.OSH assembly as SoftwareI2C is now a 4.2 core class.

Software I2C is found in Gadgeteer.DaisyLink.dll now.

No I can’t find in Microsoft.SPOT.Hardware. I’ve one in GHI.OSH. But this classes aren’t socket friendly …
May be I’ve some installation problem … I will check again.

@ Ian why you still on line ? Forget for few days about gadgeteer :slight_smile:

Steven found it. It also took me a while to find it the first time. That’s not a particularly natural place for it to be.

I haven’t left yet. Still at work… :wink:

Murphy law, obviously ! I looked in all namespaces except DaisyLink … thank you Steven

Anyway I’ve “ported” 4.1 source of SoftwareI2C.cs on 4.2 inlcuding in my prj. I don’t see any problem on Cerberus 4.2 and IO64P board. Tomorrow I will use correct assembly.