I2C Write with Register Address

Try to send the data 0xFF to the register address 0x0D to an IC with 7-bit address 0x32. This fails with a timeout error (see code below how I do this).

If I send everything else then 0xFF I don’t get any error. If I use 16 bit register address size I don’t get any error (but it doesn’t work)

Notes:

  • Without a register I can write without problems.
  • C# works RegisterAddrSize 8bit address size and 0xFF data. Same DLL library, so it isn’t a hardware fault.
  • I use the prerelease version (DLL and Libs).

Is this a bug? I’am doing something wrong? RegisterAddrSize should be 8 bit not?

Test code:

 int wmain(int argc, wchar_t *argv[])
    {
    	
    	tVersion ver;
    	I2c_GetVersion(&ver);
    	printf("Demo Application for I2cLib\r\n");
    	printf("Library Version %d.%d Build %d\r\n\r\n", ver.Major, ver.Minor, ver.Build);
    
    
    
    	HANDLE i2cH = TegI2c_Init(L"I2C1");
    	if (i2cH == NULL)
    	{
    		std::cout << "can't init i2c";
    		return 0;
    	}
    
    
    	if (!TegI2c_Open(i2cH))
    	{
    		int errCode = GetLastError();
    		std::cout << std::endl << "(1) can't open i2c. Erro code: " << errCode;
    		return 0;
    	}
    
        // Set I2C speed to 400KHz
    	bool succ1 = TegI2c_SetConfigInt(i2cH, L"BitRateHz", 400000, StoreVolatile);  

        // Set 7 bit Slave address
    	bool succ2 = TegI2c_SetConfigInt(i2cH, L"SlaveAddrSize", 7, StoreVolatile);

        //I2C address (7-bit)
    	bool succ3 = TegI2c_SetConfigInt(i2cH, L"SlaveAddr", 0x32, StoreVolatile); 
    	
        //8 bit register address
    	bool succ4 = TegI2c_SetConfigInt(i2cH, L"RegisterAddrSize", 8, StoreVolatile); 

       //set register address to 13 (0x0D)
    	bool succ5 = TegI2c_SetConfigInt(i2cH, L"RegisterAddr",0x0D, StoreVolatile);   
    	
    
    	BYTE i2cDataWriteData[1] = { 0x00 };
    	i2cDataWriteData[0] = 0xFF;
    
    	if (TegI2c_Write(i2cH, (DWORD*)i2cDataWriteData, 1) == -1)
    	{
    		int errCode = GetLastError();
    		ce::string errorMsg = "unkown error";
    		switch (errCode)
    		{
    		case ERROR_BUSY:
    			errorMsg = "bus is busy";
    			break;
    		case ERROR_INVALID_PARAMETER:
    			errorMsg = "invalid parameter";
    			break;
    		case ERROR_TIMEOUT:
    			errorMsg = "timeout";
    			break;
    		case ERROR_IO_DEVICE:
    			errorMsg = "I/O device error";
    			break;
    		case ERROR_INVALID_ADDRESS:
    			errorMsg = "invalid address";
    			break;
    		case ERROR_OPERATION_ABORTED:
    			errorMsg = "byte not acknowledged";
    			break;
    		}
    
    		std::cout << std::endl << "Error writing I2C:" << errorMsg;
    	}
    	else
    	{
    		std::cout << std::endl << "I2C bytes successful written";
    	}
    
    
    	TegI2c_Close(i2cH);
    	TegI2c_Deinit(i2cH);
    
    	std::cout << std::endl << "I2C closed. Press enter to exit";
    	fflush(stdin);
    	getchar();
    
    	return 0;
    }

@TriUrs: I quickly checked this and can confirm this. The reason is, that the T30 library had a interrupt reason missing. After adding the “No acknolege” interrupt, there is no more timeout error. I think there is a further issue, nevertheless I share with you a version that fixes this issue. You can doanload the version here. After that fix, do you still see any issues with the device you are communicating?

Hi @samuel.tx Thanks.

I’ve used the release lib, unfortunately it doesn’t work. I think I need the DLL too?

I have discovered another error but is hard to reproduce:
Sometimes the I2C bus is “locked”. An OpenI2C always returns ‘false’, GetLastError() returns 0.
A cold reboot doesn’t unlock the I2C bus. I need to switch the T30 module completely off and on.

I’m not sure if it has something to do with the upper issue. I try to find out if the upper issue is solved :slight_smile:

@TriUrs: Do you have a linker error or an run time issue? Actually there is no need for the DLL. I have tested this with CE700.

@samuel.tx: no compiler or linker error. But I need the DLL at runtime. Otherwise I get a “unable to import library TdxAllLibraries.DLL.dll” error. I would say this is a normal behavior. There are tricks to include the DLL but this is not the intention of a DLL :slight_smile:
btw: I use CE800, but should not be different to CE700. If you rename or delete the DLL it shouldn’t run.

@samuel.tx: is it possible to download a prerelease DLL? Or is a official release near in the future?

@TriUrs: Sorry, I missed to send you the files. Here they are.

No problem. Thank you @samuel.tx.

(Maybe this would be in a new ticket?)
I’ve tested it with the new DLL and C++ and now I can write to I2C slaves with address register. Unfortunately nothing happens at the chip. Under C# it works, so no hardware fault. Probably I’ve made a mistake somewhere but until now I haven’t discovered any mistake.

The other issue I found: I use a C# (r) and C++ (r/w) application at the same time accessing the I2C bus. Now if I run both applications I get always a timeout error (code 1460) for write commands under C++, but read commands are working. In my C# application I only read at 1 second intervals. Tried to higher timeout at my C++ application for a test without success: TegI2c_SetConfigInt(i2cHandle, L"Timeout", 500, StoreVolatile);

So I have still two unsolved problems. I hope you guys can help me.

Btw: do I need to use all I2C commands with ‘Teg’ in front or not?

@TriUrs: You don’t have any code already there, that you could share with us to reproduce the concurrency C++ / C# issue directly?

You don’t need to use the Teg prefixes. If you use them, the code will be called directly, otherwise it will go through an abstraction layer in order to support multiple platforms in the same code.

@samuel.tx: I don’t have any code I can share right now. I will prepare an example tomorrow.

I still try to read/write data with a register address with the new pre-release library. So I took a new Ixora carrier board V1.0A which has an EEPROM (latest doesnt’) and took this example and modified it a little bit.

  • Library: latest prelease provided in this thread
  • OS: WinCE 2013 V2.0
  • Module: Apalis T30 1GB IT V1.1A

I can read the RTC but I can’t write and read the EEPROM. Return value gives me the right number of bytes for read and write operation, but its wrong. To verify if any data are written I used the example with the old library. With the old example I’m able to read and write (so no hardware fault) successful.

Test code #1:

 private void Test1()
        {
            IntPtr i2CHandler;

            //get handle to I2C device
            i2CHandler = HwInterface.i2c.I2c_Init("I2C1");

            if (i2CHandler == IntPtr.Zero)
            {
                MessageBox.Show("Can't initialize I2C");
                return;
            }


            //open I2c for communication
            if (!HwInterface.i2c.I2c_Open(i2CHandler))
            {
                MessageBox.Show("An error occurred while opening the I2C bus.");
                return;
            }

            uint returnValue;
            uint bufferLenght = 0;
            Byte i2cEepromAddress = 80;
            Byte[] i2cEepromDataAddress = new Byte[2] { 00, 00 };
            const int eepromAddressWriteOffset = 2;

            string text = "Hello";

            //Get lenght of text entered into textbox
            bufferLenght = (uint)text.Length;


            Byte[] i2cEepromWrite = new Byte[bufferLenght + eepromAddressWriteOffset + 1];        //Make buffer for writing to eeprom chip
            Byte[] temp = new Byte[bufferLenght];                                               //Temporary buffer to hold data from textbox
            temp = Encoding.ASCII.GetBytes(text);                                //Convert string to byte array


            Buffer.BlockCopy(i2cEepromDataAddress, 0, i2cEepromWrite, 0, eepromAddressWriteOffset);///< Copy eeprom starting address 0x00 in starting of buffer
            Buffer.BlockCopy(temp, 0, i2cEepromWrite, eepromAddressWriteOffset, (int)bufferLenght);///< Concatenate data from textbox to buffer
            try
            {
                HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "SlaveAddr", i2cEepromAddress, HwInterface.TdxCommon.ParamStorageType.StoreVolatile);
                unsafe
                {
                    fixed (byte* for_Casting_Intptr_to_Byte = i2cEepromWrite)
                    {
                        returnValue = HwInterface.i2c.I2c_Write(i2CHandler, (IntPtr)for_Casting_Intptr_to_Byte, bufferLenght + eepromAddressWriteOffset); ///< write buffer to EEPROM starting address 0X00
                        if (returnValue == 0)
                            throw new Exception();                                            ///< If Write operation returns 0
                    }
                }
            }
            catch
            {
                MessageBox.Show("Write failed");
                return;
            }


            //now read the written data from the EEPROM
            Byte[] i2cEepromRead = new Byte[bufferLenght];
            try
            {
                HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "SlaveAddr", i2cEepromAddress, HwInterface.TdxCommon.ParamStorageType.StoreVolatile);
                unsafe
                {

                    fixed (byte* for_Casting_Intptr_to_Byte = i2cEepromDataAddress)
                    {
                        returnValue = HwInterface.i2c.I2c_Write(i2CHandler, (IntPtr)for_Casting_Intptr_to_Byte, eepromAddressWriteOffset);  ///< Write starting address
                        if (returnValue == 0)
                            throw new Exception();                                                  ///< If Write operation returns 0
                    }
                    System.Threading.Thread.Sleep(10);
                    fixed (byte* for_Casting_Intptr_to_Byte = i2cEepromRead)
                    {
                        returnValue = HwInterface.i2c.I2c_Read(i2CHandler, (IntPtr)for_Casting_Intptr_to_Byte, bufferLenght);///< Read data written above from EEPROM
                        if (returnValue == 0)
                            throw new Exception();                                                  //< If Read operation returns 0

                        MessageBox.Show(System.Text.Encoding.ASCII.GetString(i2cEepromRead, 0, i2cEepromRead.Length));
                    }
                }
            }
            catch
            {
                MessageBox.Show("Read failed");
            }

            if (i2CHandler != IntPtr.Zero)
            {
                HwInterface.i2c.I2c_Close(i2CHandler);                                           ///< Close I2C library
                HwInterface.i2c.I2c_Deinit(i2CHandler);                                          ///< Deinit I2C Handle
            }
        }

Now if I read the help there are parameters “RegisterAddr” and “RegisterAddrSize”. Should I not set these parameters? Tried this with a second code but it doesn’t work either. At read operation it’s blocked, write doesn’t write (but tells me correct number of bytes written).

Test code 2:

 private void Test2()
        {
            IntPtr i2CHandler;

            //get handle to I2C device
            i2CHandler = HwInterface.i2c.I2c_Init("I2C1");

            if (i2CHandler == IntPtr.Zero)
            {
                MessageBox.Show("Can't initialize I2C");
                return;
            }


            //open I2c for communication
            if (!HwInterface.i2c.I2c_Open(i2CHandler))
            {
                MessageBox.Show("An error occurred while opening the I2C bus.");
                return;
            }

            string text = "World";
            Byte i2cEepromAddress = 80;

            uint returnValue = 0; 
            bool success;
            uint numberOfBytes = (uint)text.Length;
            
            uint address = 0;

            address = BitConverter.ToUInt16(new Byte[2] { 00, 00 }, 0);
            success = HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "RegisterAddrSize", 16, HwInterface.TdxCommon.ParamStorageType.StoreVolatile); // 16 bit register address. 
            success = HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "RegisterAddr", address, HwInterface.TdxCommon.ParamStorageType.StoreVolatile); //address register to write



            //write EEPROM
            Byte[] writeBytes = new Byte[numberOfBytes];                                               //Temporary buffer to hold data from textbox
            writeBytes = Encoding.ASCII.GetBytes(text);

            try
            {
                HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "SlaveAddr", i2cEepromAddress, HwInterface.TdxCommon.ParamStorageType.StoreVolatile);
                unsafe
                {
                    fixed (byte* for_Casting_Intptr_to_Byte = writeBytes)
                    {

                        //returnValue is number of written bytes
                        returnValue = HwInterface.i2c.I2c_Write(i2CHandler, (IntPtr)for_Casting_Intptr_to_Byte, (uint)writeBytes.Length);
                        if (returnValue == -1)
                        {
                            MessageBox.Show("Write failed");
                            return;
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("Write failed" + exp.Message);
                return;
            }





            //now read written date from EEPROM
            Byte[] i2cDataRead = new Byte[numberOfBytes];
            byte[] readBytes = new Byte[numberOfBytes];

            try
            {
                HwInterface.i2c.I2c_SetConfigInt(i2CHandler, "SlaveAddr", i2cEepromAddress, HwInterface.TdxCommon.ParamStorageType.StoreVolatile);
                unsafe
                {
                    fixed (byte* for_Casting_Intptr_to_Byte = i2cDataRead)
                    {
                        returnValue = HwInterface.i2c.I2c_Read(i2CHandler, (IntPtr)for_Casting_Intptr_to_Byte, numberOfBytes);

                        if (returnValue == -1)
                        {
                            MessageBox.Show("Read failed");
                            success = false;
                        }
                    }
                }

                readBytes = i2cDataRead;
            }
            catch (Exception exp)
            {
                MessageBox.Show("Read failed" + exp.Message);
                
            }


            if (i2CHandler != IntPtr.Zero)
            {
                HwInterface.i2c.I2c_Close(i2CHandler);                                           ///< Close I2C library
                HwInterface.i2c.I2c_Deinit(i2CHandler);                                          ///< Deinit I2C Handle
            }
        }

So I’m confused what’s correct way to use the new library and I still think there is an issue in the new library.

Regarding the concurrency C++ / C# issue: I think I made a mistake. I couldn’t reproduce it with a much simple example.

@TriUrs: Thanks for the code snipped. We will check that.

@samuel.tx: any (good) news? :slight_smile:

@samuel.tx: Is there still no news? Or time schedule predictions? Sorry I ask again but sooner or later I must decide if I go back again to the old I2C library.

@roman.tx: good news?

@TriUrs: Sorry for the delay. We have been working on the lib. This was done in a other team at Toradex. I will quickly check that here on our side.

@TriUrs: I tried to reproduce this with our library demo. If you set the RegisterAddr, I am not able to reproduce this issue. I have used this C-Code:

//*****************************************************************************
/// Main function
int wmain(int argc, wchar_t *argv[])
{
	DWORD i;
	DWORD returnValue;

	tVersion ver;
	HANDLE i2c = NULL;
	BYTE data[8] = { 1 };

	I2c_GetVersion(&ver);
	printf("Demo Application for I2cLib\r\n");
	printf("Library Version %d.%d Build %d\r\n\r\n", ver.Major, ver.Minor, ver.Build);

	// Initialize I2Cx, without affecting hardware registers
	i2c = I2c_Init(L"I2C1");
	if (i2c == NULL)
		printf("Error in I2c_Init()\r\n");



	// ====== RTC Read and Write Example ======

	//  Override the default io which is defined by the portName "I2C1"..."I2C4"
	//  Io configurations need to be done *before* calling I2c_Open() !
	//{ /* Need to include gpio.h for uIo structure */
	//  uIo ioScl = COLIBRI_PIN(74); /*Colibri Pin 74*/
	//  uIo ioSda = COLIBRI_PIN(50); /*Colibri Pin 50*/
	//          /*  or  */
	//  uIo ioScl;
	//  uIo ioSda;
	//  ioScl.GenericDefinition = IOCOLIBRIPIN(74);
	//  ioSda.GenericDefinition = IOCOLIBRIPIN(50);
	//  returnValue = I2c_SetConfigInt(i2c, L"ioScl", ioScl.GenericDefinition, StoreVolatile);
	//  returnValue = I2c_SetConfigInt(i2c, L"ioSda", ioSda.GenericDefinition, StoreVolatile);
	//}

	// Open the i2c port
	I2c_Open(i2c);

	// Change configuration as required
	// Instead of calling this function, you can place default values in the registry
	//   [HKLM\SOFTWARE\Toradex\I2C1]
	//   BitRateHz        = dword:00061a80             ; = decimal 400000
	//   SlaveAddrSize    = dword:00000007             ; = decimal 7
	//   RegisterAddrSize = dword:00000000             ; = decimal 0
	I2c_SetConfigInt(i2c, L"BitRateHz", 400000, StoreVolatile);  // Set I2C speed to 400KHz
	I2c_SetConfigInt(i2c, L"SlaveAddrSize", 7, StoreVolatile);  // Set 7 bit Slave address
	I2c_SetConfigInt(i2c, L"RegisterAddr", 0, StoreVolatile);  // Set 7 bit Slave address
	I2c_SetConfigInt(i2c, L"RegisterAddrSize", 8, StoreVolatile);  // No Register address to send


																   // Set RTC address into handle, it will be valid for all future Read/Write calls
																   // 0x68 is the 7 bit address of the RTC present on Colibri Evaluation Board
	I2c_SetConfigInt(i2c, L"SlaveAddr", 0x50, StoreVolatile);

	// I2C Read
	returnValue = I2c_Read(i2c, (DWORD*)data, 8);

	printf("\nReading the RTC: %s\r\n", (returnValue == I2C_RW_FAILURE ? "failed." : "successful."));

	// I2C Write
	for (i = 0; i < 8; i++)
		data[i] = 2;

	returnValue = I2c_Write(i2c, (DWORD*)data, 8);
	printf("\nResetting the RTC: %s\r\n", (returnValue == I2C_RW_FAILURE ? "failed." : "successful."));

	// Close and deinit I2C port
	I2c_Close(i2c);
	I2c_Deinit(i2c);

	// Finish demo application
	printf("\nPress enter to exit");
	fflush(stdin);
	getchar();

	return(TRUE);
}

I tried to get your code running but stopped due to some missing refrences. Could you quickly check once with the simple example I shared with you? Are you still able to reproduce the issue? Just to make sure, I share again the lib I have used.

I can read over I2C. Problem is when I try to read/write with an register address. As I understand in your example you don’t need any register address, only i2c slave address. E.g. for the EEPROM you need to give slave address and register address (where the data should be written).

Oh, I guess the “HwInterface.” is missing. Just delete it. The class i2c is from TdxAllLibraries.cs (see below).

// Dll Imports for i2c.h functions
internal class i2c
{
public const UInt32 I2C_RW_FAILURE = UInt32.MaxValue;

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern IntPtr I2c_Init(string portName);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern bool I2c_Deinit(IntPtr hPort);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern bool I2c_Open(IntPtr hPort);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern bool I2c_Close(IntPtr hPort);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern UInt32 I2c_Read(IntPtr hPort, IntPtr pbuf, UInt32 numberOfFrames);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern UInt32 I2c_Write(IntPtr hPort, IntPtr pbuf, UInt32 numberOfFrames);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern UInt32 I2c_GetConfigInt(IntPtr hPort, string paramName, ref UInt32 Value);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern bool I2c_SetConfigInt(IntPtr hPort, string paramName, UInt32 value, TdxCommon.ParamStorageType storageType);

        [DllImport("TdxAllLibrariesDll.dll")]
        public static extern void I2c_GetVersion(ref TdxCommon.tVersion ver);
    }

or in short: try to read/write the EEPROM from Ixora carrier board V1.0A. I can’t get it work with the new library under Apalis T30/WinCE 2013.