DeviceIoControlCE for I2C

I've been banging my head about the problem for several days now. I would like to help.

I am trying to connect to I2C from a Windows CE7 board. The board is Boundary Devices Nitrogen6X.

I am trying to code this in C #.

After a lot of Google mistakes and trial and error, I can now do almost everything with I2C (I mean I wrapped most of the commands in working methods). Of course the only thing I can't do is Read / Write. I tried several different implementations, porting C and C ++ code that supposedly worked. But to no avail. I am currently putting more effort into two implementations which I will be copying here.

None of these implementations work for me. Both are part of the error management part and both indicate error number 87 (ERROR_INVALID_PARAMETER).

Does anyone have any experience with these issues? can someone point out what i am doing wrong?

Edit 1: I should probably mention that I am trying to "see" some signals on the SDA and SCL pins of I2C3: on the board, simply by connecting an oscilloscope to them. There is no corresponding device on the I2C bus. I expect this to give me some error after the first byte (addres + Read / Write) because no acknowledgment bit will be received. However, I can see that there is error 87 in my code and no change in signals visible from the scope (both stay high at idle).

(Below are the code snippets)

The first one uses pointers and stuff and is probably closer to C ++ code:

StructLayout(LayoutKind.Sequential)]
    unsafe public struct UNSAFE_I2C_PACKET
    {
        //[MarshalAs(UnmanagedType.U1)]
        public byte byAddr; //I2C slave device address
        //[MarshalAs(UnmanagedType.U1)]
        public byte byRw; //Read = I2C_Read or Write = I2C_Write
        //[MarshalAs(UnmanagedType.LPArray)]
        public byte* pbyBuf; //Message Buffer
        //[MarshalAs(UnmanagedType.U2)]
        public Int16 wLen; //Message Buffer Length
        //[MarshalAs(UnmanagedType.LPStruct)]
        public int* lpiResult; //Contain the result of last operation
    }
    [StructLayout(LayoutKind.Sequential)]
    unsafe public struct UNSAFE_I2C_TRANSFER_BLOCK
    {
        //public I2C_PACKET pI2CPackets;
        public UNSAFE_I2C_PACKET* pI2CPackets;
        public Int32 iNumPackets;
    }
    [DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
    unsafe internal static extern int DeviceIoControlCE(
        IntPtr hDevice, //file handle to driver
        uint dwIoControlCode, //a correct call to CTL_CODE
        [In, Out]byte* lpInBuffer,
        uint nInBufferSize,
        byte* lpOutBuffer,
        uint nOutBufferSize,
        uint* lpBytesReturned,
        int* lpOverlapped);
    unsafe public void ReadI2C(byte* pBuf, int count)
    {
        int ret;
        int iResult;
        UNSAFE_I2C_TRANSFER_BLOCK I2CXferBlock = new UNSAFE_I2C_TRANSFER_BLOCK();
        UNSAFE_I2C_PACKET i2cPckt = new UNSAFE_I2C_PACKET();

        //fixed (byte* p = pBuf)
        {
            i2cPckt.pbyBuf = pBuf;// p;
            i2cPckt.wLen = (Int16)count;
            i2cPckt.byRw = I2C_READ;
            i2cPckt.byAddr = BASE;
            i2cPckt.lpiResult = &iResult;

            I2CXferBlock.iNumPackets = 1;
            //fixed (I2C_PACKET* pck = &i2cPckt)
            {
                I2CXferBlock.pI2CPackets = &i2cPckt; // pck;
                //fixed (I2C_TRANSFER_BLOCK* tBlock = &I2CXferBlock)
                {
                    if (DeviceIoControlCE(_i2cFile,
                                    I2C_IOCTL_TRANSFER,
                                    (byte*)&I2CXferBlock,//tBlock,
                                    (uint)sizeof(UNSAFE_I2C_TRANSFER_BLOCK),//Marshal.SizeOf(I2CXferBlock),
                                    null,
                                    0,
                                    null,
                                    null) == 0)
                    {
                        int error = GetLastError();
                        diag("Errore nella TRANSFER");
                        diag(error.ToString());
                    }
                }
            }
        }
    }

      

Second option: I am working on marshall files between managed and unmanaged:

[StructLayout(LayoutKind.Sequential)]
    public struct I2C_PACKET
    //public class I2C_PACKET
    {
        //[MarshalAs(UnmanagedType.U1)]
        public Byte byAddr; //I2C slave device address
        //[MarshalAs(UnmanagedType.U1)]
        public Byte byRw; //Read = I2C_Read or Write = I2C_Write
        //[MarshalAs(UnmanagedType.LPArray)]
        public IntPtr pbyBuf; //Message Buffer
        //[MarshalAs(UnmanagedType.U2)]
        public Int16 wLen; //Message Buffer Length
        //[MarshalAs(UnmanagedType.LPStruct)]
        public IntPtr lpiResult; //Contain the result of last operation
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct I2C_TRANSFER_BLOCK
    {
        //public I2C_PACKET pI2CPackets;
        //[MarshalAs(UnmanagedType.LPArray)]
        public IntPtr pI2CPackets;
        //[MarshalAs(UnmanagedType.U4)]
        public Int32 iNumPackets;
    }
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
    internal static extern int DeviceIoControlCE(
        IntPtr hDevice, //file handle to driver
        uint dwIoControlCode, //a correct call to CTL_CODE
        [In] IntPtr lpInBuffer,
        uint nInBufferSize,
        [Out] IntPtr lpOutBuffer,
        uint nOutBufferSize,
        out uint lpBytesReturned,
        IntPtr lpOverlapped);
unsafe public void ReadI2C(byte[] buffer)
    {
        int count = buffer.Length;
        I2C_TRANSFER_BLOCK I2CXFerBlock = new I2C_TRANSFER_BLOCK();
        I2C_PACKET I2CPckt = new I2C_PACKET();

        I2CPckt.byAddr = BASE;
        I2CPckt.byRw = I2C_READ;
        I2CPckt.wLen = (Int16)count;
        I2CPckt.lpiResult = Marshal.AllocHGlobal(sizeof(int));
        I2CPckt.pbyBuf = Marshal.AllocHGlobal(count);
        //GCHandle packet = GCHandle.Alloc(I2CPckt, GCHandleType.Pinned);

        I2CXFerBlock.iNumPackets = 1;
        I2CXFerBlock.pI2CPackets = Marshal.AllocHGlobal(Marshal.SizeOf(I2CPckt)); //(Marshal.SizeOf(typeof(I2C_PACKET)));//  //(sizeof(I2C_PACKET));// 
        Marshal.StructureToPtr(I2CPckt, I2CXFerBlock.pI2CPackets, false);
        //I2CXFerBlock.pI2CPackets = packet.AddrOfPinnedObject();

        //GCHandle xferBlock = GCHandle.Alloc(I2CXFerBlock, GCHandleType.Pinned);
        IntPtr xfer = Marshal.AllocHGlobal(Marshal.SizeOf(I2CXFerBlock)); //(sizeof(I2C_TRANSFER_BLOCK)); //
        Marshal.StructureToPtr(I2CXFerBlock, xfer, false);
        //IntPtr xfer = xferBlock.AddrOfPinnedObject();
        uint size = (uint)sizeof(I2C_TRANSFER_BLOCK);//Marshal.SizeOf(I2CXFerBlock);
        uint getCnt = 0;
        if ((DeviceIoControlCE(_i2cFile,
            I2C_IOCTL_TRANSFER, 
            xfer, 
            size, 
            xfer, //IntPtr.Zero, 
            size, //0, 
            out getCnt, 
            IntPtr.Zero)) == 0)
        {
            int error = GetLastError();
            diag("Errore nella TRANSFER.");
            diag(error.ToString());
        }
        else
        {
            //success
            I2CXFerBlock = (I2C_TRANSFER_BLOCK)Marshal.PtrToStructure(xfer, typeof(I2C_TRANSFER_BLOCK));
            I2CPckt = (I2C_PACKET)Marshal.PtrToStructure(I2CXFerBlock.pI2CPackets, typeof(I2C_PACKET));
            Marshal.Copy(I2CPckt.pbyBuf, buffer, 0, count);
            diag("Success in TRANSFER: " + buffer[0].ToString());
        }
        Marshal.FreeHGlobal(I2CPckt.pbyBuf);
        Marshal.FreeHGlobal(I2CXFerBlock.pI2CPackets);
        Marshal.FreeHGlobal(xfer);
        //packet.Free();
        //xferBlock.Free();
    }

      

Edit 2: The working (presumably) working code I have (which I cannot run) comes from the drivers that were provided to me, which may be partially proprietary (hence I cannot share). However, I found an online header for the I2C bus that contains the following definition:

#define I2C_MACRO_TRANSFER(hDev, pI2CTransferBlock) \
(DeviceIoControl(hDev, I2C_IOCTL_TRANSFER, (PBYTE) pI2CTransferBlock, sizeof(I2C_TRANSFER_BLOCK), NULL, 0, NULL, NULL))

      

At first I tried to give "null" to the parameters like here, but I still have the same error code.

Edit 3: from the same driver, the struct definition:

// I2C Packet
typedef struct
{
    BYTE byAddr;       // I2C slave device address for this I2C operation
    BYTE byRW;         // Read = I2C_READ or Write = I2C_WRITE
    PBYTE pbyBuf;      // Message Buffer
    WORD wLen;         // Message Buffer Length
    LPINT lpiResult;   // Contains the result of last operation
} I2C_PACKET, *PI2C_PACKET;

// I2C Transfer Block
typedef struct
{
    I2C_PACKET *pI2CPackets;
    INT32 iNumPackets;
} I2C_TRANSFER_BLOCK, *PI2C_TRANSFER_BLOCK;

      

Edit 4: I tried to implement the version passing ref

into my structs as @ctacke suggested in his comment. I am still getting the same error, so I guess I should have been doing something different than he thought. Here's a snippet:

[StructLayout(LayoutKind.Sequential)]
    public struct REF_I2C_PACKET //public class REF_I2C_PACKET //
    {
        //[MarshalAs(UnmanagedType.U1)]
        public Byte byAddr; //I2C slave device address
        //[MarshalAs(UnmanagedType.U1)]
        public Byte byRw; //Read = I2C_Read or Write = I2C_Write
        //[MarshalAs(UnmanagedType.LPArray)]
        public IntPtr pbyBuf; //Message Buffer
        //[MarshalAs(UnmanagedType.U2)]
        public Int16 wLen; //Message Buffer Length
        //[MarshalAs(UnmanagedType.LPStruct)]
        public IntPtr lpiResult; //Contain the result of last operation
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct REF_I2C_TRANSFER_BLOCK //public class REF_I2C_TRANSFER_BLOCK //
    {
        //public I2C_PACKET pI2CPackets;
        public IntPtr pI2CPackets;
        //[MarshalAs(UnmanagedType.U4)]
        public Int32 iNumPackets;
        //[MarshalAs(UnmanagedType.LPArray)]
        //[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = 2)]
        //public REF_I2C_PACKET[] pI2CPackets;
    }
    [DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
    unsafe internal static extern int DeviceIoControlCE(
        IntPtr hDevice, //file handle to driver
        uint dwIoControlCode, //a correct call to CTL_CODE
        //[MarshalAs(UnmanagedType.AsAny)]
        //[In] object lpInBuffer, //
        ref REF_I2C_TRANSFER_BLOCK lpInBuffer,
        uint nInBufferSize,
        byte* lpOutBuffer, //ref REF_I2C_TRANSFER_BLOCK lpOutBuffer,
        uint nOutBufferSize,
        out uint lpBytesReturned, //uint* lpBytesReturned,
        int* lpOverlapped);
    unsafe public void RefReadI2C(byte[] buffer)
    {
        int count = buffer.Length;
        REF_I2C_TRANSFER_BLOCK I2CXFerBlock = new REF_I2C_TRANSFER_BLOCK();
        REF_I2C_PACKET[] I2CPckt = new REF_I2C_PACKET[2];
        I2CPckt[0] = new REF_I2C_PACKET();
        I2CPckt[1] = new REF_I2C_PACKET();

        I2CPckt[0].byAddr = BASE;
        I2CPckt[0].byRw = I2C_READ;
        I2CPckt[0].wLen = (Int16)count;
        I2CPckt[0].lpiResult = Marshal.AllocHGlobal(sizeof(int));
        I2CPckt[0].pbyBuf = Marshal.AllocHGlobal(count);
        Marshal.Copy(buffer, 0, I2CPckt[0].pbyBuf, count);

        I2CPckt[1].byAddr = BASE;
        I2CPckt[1].byRw = I2C_READ;
        I2CPckt[1].wLen = (Int16)count;
        I2CPckt[1].lpiResult = Marshal.AllocHGlobal(sizeof(int));
        I2CPckt[1].pbyBuf = Marshal.AllocHGlobal(count);
        Marshal.Copy(buffer, 0, I2CPckt[0].pbyBuf, count);

        I2CXFerBlock.iNumPackets = 2;
        I2CXFerBlock.pI2CPackets = Marshal.AllocHGlobal(Marshal.SizeOf(I2CPckt[0])*I2CPckt.Length);

        uint size = (uint)Marshal.SizeOf(I2CXFerBlock); //sizeof(REF_I2C_TRANSFER_BLOCK);//Marshal.SizeOf(I2CXFerBlock);
        //size += (uint)(Marshal.SizeOf(I2CPckt[0]) * I2CPckt.Length);
        uint getCnt = 0;
        if ((DeviceIoControlCE(_i2cFile,
            I2C_IOCTL_TRANSFER,
            ref I2CXFerBlock,
            size,
            null, //IntPtr.Zero, 
            0, //0, 
            out getCnt,
            null)) == 0)
        {
            int error = GetLastError();
            diag("Errore nella TRANSFER.");
            diag(error.ToString());
        }
        else
        {
            //success

            I2CPckt = (REF_I2C_PACKET[])Marshal.PtrToStructure(I2CXFerBlock.pI2CPackets, typeof(REF_I2C_PACKET[]));
            Marshal.Copy(I2CPckt[0].pbyBuf, buffer, 0, count);
            diag("Success in TRANSFER: " + buffer[0].ToString());
        }
        Marshal.FreeHGlobal(I2CPckt[0].pbyBuf);
        Marshal.FreeHGlobal(I2CPckt[0].lpiResult);
    }

      

Edit 5:
I found online ( http://em-works.googlecode.com/svn/trunk/WINCE600/PLATFORM/COMMON/SRC/SOC/COMMON_FSL_V2_PDK1_9/I2C/PDK/i2c_io.cpp ) The following code:

BOOL I2C_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, 
               DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
               PDWORD pdwActualOut)
{
    /*stuff*/
    case I2C_IOCTL_TRANSFER:
        {
#define MARSHAL 1
#if MARSHAL
            DuplicatedBuffer_t Marshalled_pInBuf(pBufIn, dwLenIn, ARG_I_PTR);
            pBufIn = reinterpret_cast<PBYTE>( Marshalled_pInBuf.ptr() );
            if( (dwLenIn > 0) && (NULL == pBufIn) )
            {
                return FALSE;
            }
#endif
            I2C_TRANSFER_BLOCK *pXferBlock = (I2C_TRANSFER_BLOCK *) pBufIn;
            if (pXferBlock->iNumPackets<=0) 
            {
                return FALSE;
            }

#if MARSHAL
            MarshalledBuffer_t Marshalled_pPackets(pXferBlock->pI2CPackets, 
                                                   pXferBlock->iNumPackets*sizeof(I2C_PACKET), 
                                                   ARG_I_PTR);

            I2C_PACKET *pPackets = reinterpret_cast<I2C_PACKET *>(Marshalled_pPackets.ptr());
            if( (NULL == pPackets) )
            {
                return FALSE;
            }
#else
            I2C_PACKET *pPackets = pXferBlock->pI2CPackets;
#endif

#if MARSHAL
           struct Marshalled_I2C_PACKET
            {
                MarshalledBuffer_t *pbyBuf;
                MarshalledBuffer_t *lpiResult;
            } *Marshalled_of_pPackets;

            Marshalled_of_pPackets = new Marshalled_I2C_PACKET[pXferBlock->iNumPackets];
            if (Marshalled_of_pPackets==0)
            {
                return FALSE;
            }

            MarshalledBuffer_t *pMarshalled_ptr;
            int i;

           // Map pointers for each packet in the array
            for (i = 0; i < pXferBlock->iNumPackets; i++)
            {
                switch( pPackets[i].byRW & I2C_METHOD_MASK )
                {
                case I2C_RW_WRITE:
                    pMarshalled_ptr = new MarshalledBuffer_t(
                                           pPackets[i].pbyBuf,
                                           pPackets[i].wLen,
                                           ARG_I_PTR,
                                           FALSE, FALSE);
                    if (pMarshalled_ptr ==0)
                    {
                        bRet = FALSE;
                        goto cleanupPass1;
                    }
                    if (pMarshalled_ptr->ptr()==0)
                    {
                        bRet = FALSE;
                        delete pMarshalled_ptr;
                        goto cleanupPass1;
                    }
                    break;

                case I2C_RW_READ:
                    pMarshalled_ptr = new MarshalledBuffer_t(
                                           pPackets[i].pbyBuf,
                                           pPackets[i].wLen,
                                           ARG_O_PTR, FALSE, FALSE);
                    if (pMarshalled_ptr ==0)
                    {
                        bRet = FALSE;
                        goto cleanupPass1;
                    }
                    if (pMarshalled_ptr->ptr()==0)
                    {
                        bRet = FALSE;
                        delete pMarshalled_ptr;
                        goto cleanupPass1;
                    }
                    break;

                default:
                    {
                        bRet = FALSE;
                        goto cleanupPass1;
                    }
                }

                pPackets[i].pbyBuf = reinterpret_cast<PBYTE>(pMarshalled_ptr->ptr());
                Marshalled_of_pPackets[i].pbyBuf = pMarshalled_ptr;
            }

            for (i = 0; i < pXferBlock->iNumPackets; i++)
            {
                pMarshalled_ptr = new MarshalledBuffer_t(
                                 pPackets[i].lpiResult, sizeof(INT), 
                                 ARG_O_PDW, FALSE, FALSE);

                if (pMarshalled_ptr ==0)
                {
                    bRet = FALSE;
                    goto cleanupPass2;
                }
                if (pMarshalled_ptr->ptr()==0)
                {
                    bRet = FALSE;
                    delete pMarshalled_ptr;
                    goto cleanupPass2;
                }
                pPackets[i].lpiResult = reinterpret_cast<LPINT>(pMarshalled_ptr->ptr());
                Marshalled_of_pPackets[i].lpiResult = pMarshalled_ptr;
            }
#endif

            bRet = pI2C->ProcessPackets(pPackets, pXferBlock->iNumPackets);

#if MARSHAL
            DEBUGMSG (ZONE_IOCTL|ZONE_FUNCTION, (TEXT("I2C_IOControl:I2C_IOCTL_TRANSFER -\r\n")));

            i = pXferBlock->iNumPackets;
cleanupPass2:
            for (--i; i>=0; --i)
            {
                delete Marshalled_of_pPackets[i].lpiResult;
            }

            i = pXferBlock->iNumPackets;
cleanupPass1:
            for (--i; i>=0; --i)
            {
                delete Marshalled_of_pPackets[i].pbyBuf;
            }

            delete[] Marshalled_of_pPackets;

#endif
            break;
        }
/*stuff*/
}

      

I can't claim I understood it 100%, but from the Windows naming conventions ( http://msdn.microsoft.com/en-us/library/windows/desktop/aa378932(v=vs.85).aspx ) it seemed would that size parameter I have to send the total bytes of my transfer, including everything. I tried to figure this number out, but still couldn't. Alternatively, I think one could try to do something for the structures that I have to turn into a byte array. Only I assume that for this the system must have a certain byte order for the system to understand it. Can anyone participate in this?

+3


source to share





All Articles