MQX 4.1.1 I2C->EEPROM

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MQX 4.1.1 I2C->EEPROM

2,021 Views
josephchen
Contributor III

I try to use MK70 to control an EEPROM via I2C Interface. I use the MQX I2C sample code. The code below is how it polls in i2c_write_eeprom() function.

      /* Wait for EEPROM write cycle finish - acknowledge */

      result = 0;

      do

      {  /* Sends just I2C bus address, returns acknowledge bit and stops */

         fwrite (&result, 1, 0, fd);

        

         if (I2C_OK != ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param))

         {

            printf ("  ERROR during wait (flush)\n");

         }

        

         if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

         {

            printf ("  ERROR during wait (stop)\n");

         }

         result++;

         printf("param:%d", param);

      } while ((param & 1) || (result >= 1));

It will fail if I use i2c_read_eeprom() function immediately after i2c_write_eeprom() function. The terminal shows "Write to address 0x001a ... ERROR". However, if I add _time_delay(1) at the bottom of i2c_write_eeprom(), then the i2c_read_eeprom() will work successfully. Is there any solution to check whether EEPROM write cycle is finished since I don't want to use _time_delay()? Like check the flag or something else.

29 Replies

988 Views
josephchen
Contributor III

Sorry, the MCU I use is MK61.

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

It depends on EEPROM memory which you want program.

For example I2C EEPROM 24LC32 offers two ways how to wait for write finish:

  1. Datasheet says that you should wait 5ms per page of 8 bytes. Multiply by the number of pages loaded into the write cache for total time.
  2. Chapter 5 says: Since the device will not acknowledge during a write cycle, this can be used to determine when the cycle is complete (this feature can be used to maximize bus throughput).

So, you can send control byte (start+control byte with write command (R/W = 0)) in the loop and waits for ACK.

Now, how to do it?

I would like to recommend add code where we set slave address by IO_IOCTL_I2C_SET_DESTINATION_ADDRESS ioctl command and create loop where we send this byte and check ACK by IO_IOCTL_FLUSH_OUTPUT ioctl command. IO_IOCTL_FLUSH_OUTPUT ioctl command returns I2Cx_S[RXAK] bit as parameter and this parameter could be used in loop condition. See code below:

//

/*[Code for write to EEPROM]*/

//

/* Wait for EEPROM write cycle finish - acknowledge */

      do

      {  /* Sends just I2C bus address, returns acknowledge bit and stops */

        fwrite (&result, 1, 0, fd);

    

        if (I2C_OK != ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param))

        {

            printf ("  ERROR during wait (flush)\n");

        }

    

        if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

        {

            printf ("  ERROR during wait (stop)\n");

        }

      } while ((param & 1) || (result <= 1));


Have a great day,
RadekS

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

988 Views
josephchen
Contributor III

This was exactly the code I used, but it failed. IO_IOCTL_FLUSH_OUTPUT returns 0 to the parameter from the beginning, so it doesn't get ack bit at all. The EEPROM I use is AT24C512C. Here's what its data sheet says:

Byte Write: A Byte Write operation requires two 8-bit data word addresses following the device address word and

acknowledgment. Upon receipt of this address, the EEPROM will again respond with a zero, and then the part is to

receive an 8-bit data word. Following receipt of the 8-bit data word, the EEPROM will output a zero. The addressing

device, such as a microcontroller, then must terminate the write sequence with a Stop condition. At this time, the

EEPROM enters an internally-timed write cycle, t WR , to the nonvolatile memory. All inputs are disabled during this write

cycle, and the EEPROM will not respond until the write is complete).

Acknowledge Polling: Once the internally-timed write cycle has started and the EEPROM inputs are disabled,

Acknowledge Polling can be initiated. This involves sending a Start condition followed by the device address word. The

read/write select bit is representative of the operation desired. Only if the internal write cycle has completed will the

EEPROM respond with a zero, allowing the read or write sequence to continue.

How should I do?

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

It is correct that IO_IOCTL_FLUSH_OUTPUT returns 0 to the param from the beginning.

We have to wait until it returns 1 (until EEPROM sends ACK back).

I see that your code contains this line:

} while ((param & 1) || (result >= 1));

However my code contains line:

} while ((param & 1) || (result <= 1));

Please check result value.

0 Kudos

988 Views
josephchen
Contributor III

I still have questions. First, should the result be set to zero before loop? Second, doesn't the result need ++result within the loop? The question that confuses me the most is that since we're waiting until it return 1 to the param from IO_IOCTL_FLUSH_OUTPUT, how come the condition in while loop is "(param & 1)". If the param becomes 1, then 1 & 1 will result in true so it can't get off the loop. Why it's not "while (param != 1) "?

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

You are right. I made mistakes and in my both previous answers. I forget delete result from while loop.

And you are right IO_IOCTL_FLUSH_OUTPUT returns 1 when no acknowledge signal detected.

So, it should be:

result = 0;

      do

      {  /* Sends just I2C bus address, returns acknowledge bit and stops */

        fwrite (&result, 1, 0, fd);

        if (I2C_OK != ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param))

        {

            printf ("  ERROR during wait (flush)\n");

        }

        if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

        {

            printf ("  ERROR during wait (stop)\n");

        }

        result++;

      } while ((param & 1) || (result <= 1));

Is it possible check EEPROM behavior by oscilloscope/logic analyzer?

0 Kudos

988 Views
josephchen
Contributor III

Hello Radek,

     I found out another solution. If I change the line "fwrite(&result, 1, 0, fd)" to ""fwrite(&result, 1, 1, fd)", it will successfully return ack bit 1 and then eventually return 0 after a few poll times. Is it the right way to solve the problem since if I use "fwrite(&result, 1, 0, fd)", it will always return 0 which represents that it get ack bit, but in fact it doesn't. What's your opinion?

Joseph

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

I am not sure, this has to be checked on oscilloscope - if "fwrite(&result, 1, 0, fd)" sends first byte (with slave address) and there is error in driver (return that ACK was received) or if only "fwrite(&result, 1, 1, fd)" sends this first byte.

If first option is valid, we have to modify I2c driver code.

If second option is valid, we have to modify I2c example code.

0 Kudos

988 Views
josephchen
Contributor III

/*FUNCTION****************************************************************

*

* Function Name    : _ki2c_int_tx

* Returned Value   : number of bytes written

* Comments         :

*   Returns the number of bytes written.

*   Writes the data provided into transmition buffer if available.

*

*END*********************************************************************/

uint32_t _ki2c_int_tx

   (

      /* [IN] the address of the device specific information */

      IO_I2C_INT_DEVICE_STRUCT_PTR int_io_dev_ptr,

      /* [IN] The array characters are to be read from */

      unsigned char                    *buffer,

     

      /* [IN] number of bytes to output */

      uint32_t                      length

   )

{ /* Body */

   VKI2C_INFO_STRUCT_PTR           io_info_ptr;

   I2C_MemMapPtr                   i2c_ptr;  

   uint32_t                        tmp;

  

   io_info_ptr  = int_io_dev_ptr->DEV_INFO_PTR;

   i2c_ptr = io_info_ptr->I2C_PTR;

   /* Critical section + avoiding spurious interrupt */

   _int_disable ();

   _bsp_int_disable (io_info_ptr->VECTOR);

   _int_enable ();

  

   /* If beginning of transmission, set state and send address (master only) */

   io_info_ptr->OPERATION &= (~ I2C_OPERATION_READ);

   io_info_ptr->TX_BUFFER      = buffer;

   io_info_ptr->TX_BUFFER_SIZE = length;

   io_info_ptr->TX_INDEX       = 0;

  

   tmp = io_info_ptr->STATE;

   if (I2C_MODE_MASTER == io_info_ptr->MODE)

   {

      if ((I2C_STATE_READY == tmp) || (I2C_STATE_REPEATED_START == tmp))

      {

         i2c_ptr->C1 |= I2C_C1_TX_MASK;

         i2c_ptr->S |= I2C_S_IICIF_MASK;

         if (I2C_STATE_REPEATED_START == tmp)

         {

            i2c_ptr->C1 |= I2C_C1_RSTA_MASK;

         }

         else

         {

            i2c_ptr->C1 |= I2C_C1_MST_MASK;

         }

         io_info_ptr->OPERATION |= I2C_OPERATION_STARTED;

         i2c_ptr->D = (io_info_ptr->ADDRESSEE << 1) | I2C_OPERATION_WRITE;

         io_info_ptr->STATISTICS.TX_PACKETS++;

      }

      else if(I2C_STATE_TRANSMIT == tmp)

      {

         if(length != 0)

         {

            /* send first byte */

            i2c_ptr->D = io_info_ptr->TX_BUFFER[io_info_ptr->TX_INDEX++];   /*  transmit data */

            io_info_ptr->STATISTICS.TX_PACKETS++;

         }

         else

            return 0;

      }

   }

   /* Interrupt enable - end of critical section */

   _bsp_int_enable (io_info_ptr->VECTOR);

  

   /* Wait for tx complite */

   _lwsem_wait((LWSEM_STRUCT_PTR)(&(io_info_ptr->LWSEM)));

   return io_info_ptr->TX_INDEX;

} /* Endbody */

The code above is the function implementation called by fwrite written in mqx/source/io/i2c/int/i2c_int_ki2c.c. I guess it's probably what I need to modify since I'm using interrupt I2C. The code is from MQX version 4.1.1. I notice that it contains

else if(I2C_STATE_TRANSMIT == tmp)

      {

         if(length != 0)

         {

            /* send first byte */

            i2c_ptr->D = io_info_ptr->TX_BUFFER[io_info_ptr->TX_INDEX++];   /*  transmit data */

            io_info_ptr->STATISTICS.TX_PACKETS++;

         }

         else

            return 0;

      }

Maybe it's the reason why I use "fwrite(&result, 1, 0, fd)" and it sends nothing. If so, how to change it?

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

I could confirm that fwrite (&result, 1, 0, fd); will generate one byte at I2C pins.

Additionally  I also test that IO_IOCTL_FLUSH_OUTPUT correctly returns 0x00 in case of ACK and 0x01 in case of NACK.

So, it should work.

Could you please check it on your side by oscilloscope?

Please check also I2C pull-ups and cable length. I would like to recommend for example 2K2 pull-ups and length (between MCU and EEPROM) below 0.5m.    

988 Views
tongjianwu
Contributor III

Hello RadekS,

          I ran into the similar problem the I2C driver can successfully operate accelerometer,but for E2PROM operation,the program ran into "Write to address 0x001a ... ERROR"

          so,what should I do for this,can you give me some advice?

          Thks!

          Have a good day !

Best Regards

0 Kudos

988 Views
josephchen
Contributor III

Hello Jianwu,

     The final way I solve this problem is to replace the I2C driver to the one in MQX 4.1.1. You may try on this.

Kind Regards,

Joseph Chen

0 Kudos

988 Views
tongjianwu
Contributor III

hello,Joseph

      Thks for your reply!

     now,I use MQX 4.1.1,but the problem still exist.

     I find one program for accelerometer,using the I2C driver of MQX 4.1.1,can ran normally,but not working for E2PROM .

     I think no matter what I2C device is used, it should comply with the I2C protocol.

     now , I don't find one breakpoint.

     I use the MQX 4.1.1 example for I2C,but I find it's not work for E2PROM.

     Can you give me some example for operating the I2C E2PROM?

     Thks!

Best Regards

0 Kudos

988 Views
josephchen
Contributor III

/*HEADER**********************************************************************

*

* Copyright 2008 Freescale Semiconductor, Inc.

*

* This software is owned or controlled by Freescale Semiconductor.

* Use of this software is governed by the Freescale MQX RTOS License

* distributed with this Material.

* See the MQX_RTOS_LICENSE file distributed for more details.

*

* Brief License Summary:

* This software is provided in source form for you to use free of charge,

* but it is not open source software. You are allowed to use this software

* but you cannot redistribute it or derivative works of it in source form.

* The software may be used only in connection with a product containing

* a Freescale microprocessor, microcontroller, or digital signal processor.

* See license agreement file for full license terms including other

* restrictions.

*****************************************************************************

*

* Comments:

*

*   This file contains read/write functions to access I2C EEPROMs

*   using I2C driver.

*

*

*END************************************************************************/

#include <mqx.h>

#include <bsp.h>

#include <i2c.h>

#include "I2C_EEPROM.h"

/*FUNCTION****************************************************************

*

* Function Name    : i2c_write_eeprom

* Returned Value   : void

* Comments         :

*   Writes the provided data buffer at address in I2C EEPROMs

*

*END*********************************************************************/

void i2c_write_eeprom

   (

      /* [IN] The file pointer for the I2C channel */

      MQX_FILE_PTR   fd,

      /* [IN] The address in EEPROM to write to */

      uint32_t       addr,

      /* [IN] The array of characters are to be written in EEPROM */

      unsigned char  *buffer,

      /* [IN] Number of bytes in that buffer */

      _mqx_int       n

   )

{ /* Body */

   uint32_t    param;

   _mqx_int    length;

   _mqx_int    result;

   uint8_t     mem[I2C_EEPROM_MEMORY_WIDTH];

   /* Protect I2C transaction in multitask environment */

   _lwsem_wait (&lock);

   printf ("Writing %d bytes to address 0x%08x ...\n", n, addr);

   do

   {

      /* I2C bus address also contains memory block index */

      param = I2C_EEPROM_BUS_ADDRESS;

      printf ("  Set I2C bus address to 0x%02x ... ", param);

      if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param))

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

      length = (_mqx_int)(I2C_EEPROM_PAGE_SIZE - (addr & (I2C_EEPROM_PAGE_SIZE - 1)));

      if (length > n) length = n;

      /* Write address within memory block */

#if I2C_EEPROM_MEMORY_WIDTH == 2

      mem[0] = (uint8_t)(addr >> 8);

      mem[1] = (uint8_t)addr;

      printf ("  Write to address 0x%02x%02x ... ", mem[0], mem[1]);

      result = fwrite (mem, 1, 2, fd);

      if (2 == result)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

#else

      mem[0] = (uint8_t)addr;

      printf ("  Write to address 0x%02x ... ", mem[0]);

      result = fwrite (mem, 1, 1, fd);

      if (1 == result)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

#endif

      /* Page write of data */

      printf ("  Page write %d bytes ... ", length);

      result = fwrite (buffer, 1, length, fd);

      if (result == length)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

     

      /* Wait for completion */

      printf ("  Flush ... ");

      result = fflush (fd);

      if (MQX_OK == result)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

      /* Stop I2C transfer - initiate EEPROM write cycle */

      printf ("  Stop transfer ... ");

      if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

      /* Wait for EEPROM write cycle finish - acknowledge */

      result = 0;

      do

      {  /* Sends just I2C bus address, returns acknowledge bit and stops */

         fwrite (&result, 1, 0, fd);

        

         if (I2C_OK != ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param))

         {

            printf ("  ERROR during wait (flush)\n");

         }

        

         if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

         {

            printf ("  ERROR during wait (stop)\n");

         }

         result++;

      } while ((param & 1) || (result <= 1));

     

      printf ("  EEPROM polled %d times.\n", result);

      /* Next round */

      buffer += length;

      addr += length;

      n -= length;

       

   } while (n);

   /* Release the transaction lock */

   _lwsem_post (&lock);

} /* Endbody */

  

/*FUNCTION****************************************************************

*

* Function Name    : i2c_read_eeprom

* Returned Value   : void

* Comments         :

*   Reads into the provided data buffer from address in I2C EEPROM

*

*END*********************************************************************/

void i2c_read_eeprom

   (

      /* [IN] The file pointer for the I2C channel */

      MQX_FILE_PTR   fd,

      /* [IN] The address in EEPROM to read from */

      uint32_t       addr,

      /* [IN] The array of characters to be written into */

      unsigned char  *buffer,

      /* [IN] Number of bytes to read */

      _mqx_int       n

   )

{ /* Body */

   _mqx_int    param;

   _mqx_int    result;

   uint8_t     mem[I2C_EEPROM_MEMORY_WIDTH];

   printf ("Reading %d bytes from address 0x%08x ...\n", n, addr);

   if (0 == n)

   {

      printf ("  Nothing to do.\n");

      return;

   }

   /* Protect I2C transaction in multitask environment */

   _lwsem_wait (&lock);

  

   /* I2C bus address also contains memory block index */

   param = I2C_EEPROM_BUS_ADDRESS;

   printf ("  Set I2C bus address to 0x%02x ... ", param);

   if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param))

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

   /* Write address within memory block */

#if I2C_EEPROM_MEMORY_WIDTH == 2

      mem[0] = (uint8_t)(addr >> 8);

      mem[1] = (uint8_t)addr;

      printf ("  Write to address 0x%02x%02x ... ", mem[0], mem[1]);

      result = fwrite (mem, 1, 2, fd);

      if (2 == result)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

#else

      mem[0] = (uint8_t)addr;

      printf ("  Write to address 0x%02x ... ", mem[0]);

      result = fwrite (mem, 1, 1, fd);

      if (1 == result)

      {

         printf ("OK\n");

      } else {

         printf ("ERROR\n");

      }

#endif

   /* Wait for completion */

   printf ("  Flush ... ");

   result = fflush (fd);

   if (MQX_OK == result)

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

   /* Restart I2C transfer for reading */

   printf ("  Initiate repeated start ... ");

   if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_REPEATED_START, NULL))

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

   /* Set read request */

   param = n;

   printf ("  Set number of bytes requested to %d ... ", param);

   if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_SET_RX_REQUEST, &param))

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

   /* Read all data */

   printf ("  Read %d bytes ... ", n);

   result = fread (buffer, 1, n, fd);

   if (result == n)

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

     

   /* Stop I2C transfer - initiate EEPROM write cycle */

   printf ("  Stop transfer ... ");

   if (I2C_OK == ioctl (fd, IO_IOCTL_I2C_STOP, NULL))

   {

      printf ("OK\n");

   } else {

      printf ("ERROR\n");

   }

  

   /* Release the transaction lock */

   _lwsem_post (&lock);

} /* Endbody */

0 Kudos

988 Views
tongjianwu
Contributor III

Hello Joseph,

          I have made a mistake,the I2C device address in MQX should be below format:

          for example, AT24C02, addr = 1 0 1 0 A2 A1 A0 R/W,this should be shift right one bit (addr>>1),then this parameter pass to MQX I2C driver .

          Thks for your help!

Best Regards!

0 Kudos

988 Views
josephchen
Contributor III

Hello Jianwu,

     Glad to hear that. Yes, the R/W bit shouldn't be included in the address. The MQX I2C driver will add this R/W bit itself in its low level driver.

Kind Regards,

Joseph

0 Kudos

988 Views
tongjianwu
Contributor III

hello Joseph,

             Thks !

             I'm a beginner of Freescale MCU.

   

             Can you give some advice to use MQX or some staff I should read ? For example, the ioctl,fwrite,fread etc.

             From the doc "MQX_IO_User_Guide",I can get little info for these function.

Best Regards!

0 Kudos

988 Views
RadekS
NXP Employee
NXP Employee

Hi,

I am glad that it works now.

I would like to recommend for example this course:

www.freescale.com/webapp/sps/site/training_information.jsp?code=WBT_MQX_RTOS_COURSE

Documentation you can find in MQX root dir/Doc

You can also additionally look at some Application notes:

Freescale MQX™ Software Solutions|Freescale

I hope it helps you.

0 Kudos

988 Views
tongjianwu
Contributor III

Hello RadekS,

             Thks for your link,

             Great,I'll learn these course .

Best Regards!

0 Kudos

988 Views
josephchen
Contributor III

Hello Jianwu,

     You can look into those example projects provided by Freescale. You may take them as reference.

Kind Regards,

Joseph

0 Kudos