don't understand i2c control commands

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

don't understand i2c control commands

5,863 Views
wne
Contributor I

Hi,

Could you explain the control commands, for example:

IO_IOCTL_I2C_SET_STATION_ADDRESS
IO_IOCTL_I2C_SET_DESTINATION_ADDRESS
IO_IOCTL_I2C_SET_RX_REQUEST
IO_IOCTL_I2C_GET_STATISTICS
IO_IOCTL_FLUSH_OUTPUT

what are they for? what things are going on behind them? what registers do they change, or what data are they sending? which part I2C protocol they are acting? Thanks.

0 Kudos
23 Replies

2,310 Views
eGuy
Contributor IV

Hi,

 

I recently finished some code which uses I2C driver.   The I2C driver implementation  in MQX is different from what I used before. 

I added a thin layer. So far, I didn't find any problems yet. (  I use I2C to talk to an one-wire device controller )

 

Below is the code:

 

////////////////////////////////////////////////////////////

uint_32 I2C_Init(void)

{

   // Open the I2C driver

   i2c_fd = fopen ("i2c0:", NULL);

   if (i2c_fd == NULL)

   {      // Error Handling   }

 

   // Set up ....................

}

 

 

////////////////////////////////////////////////////////////

uint_32 I2C_stop(void)

{

    errorMQX = ioctl (i2c_fd, IO_IOCTL_I2C_STOP, NULL);

   if (I2C_OK != errorMQX)

   {  // Error Handling   }

   return errorCode;

}

 

uint_32 I2C_start()

{

   return I2C_OK;

}

 

////////////////////////////////////////////////////////////

uint_32 I2C_send_address(unsigned char addr)

{

   uint_32                param;

   uint_8                  mem;

 

    param = addr;

   if (I2C_OK != (errorMQX = ioctl(i2c_fd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param)) )

   {       // Error Handling   }

 

   param = 0x00;

   if (I2C_OK != (errorMQX = ioctl (i2c_fd, IO_IOCTL_I2C_GET_DESTINATION_ADDRESS, &param)) )

   {  // Error Handling   }

 

 

   /* Initiate start and send I2C bus address */

   fwrite (&mem, 1, 0, i2c_fd);

 

   /* Check ack (device exists) */

   if (I2C_OK == (errorMQX = ioctl(i2c_fd, IO_IOCTL_FLUSH_OUTPUT, &param)) )

   {

      if (param)

      {

         /* Stop I2C transfer */

         if (I2C_OK != (errorMQX = ioctl(i2c_fd, IO_IOCTL_I2C_STOP, NULL)) )

         {    // Error Handling         }

        // Error Handling

      }

   }

   else

   {       // Error Handling

   }

   return errorCode;

}

 

 

////////////////////////////////////////////////////////////

uint_32 I2C_rep_start()

{

   errorMQX = ioctl (i2c_fd, IO_IOCTL_I2C_REPEATED_START, NULL);

   if (I2C_OK != errorMQX )

   {       // Error Handling   }

   return errorCode;

}

 

 

////////////////////////////////////////////////////////////

uint_32 I2C_write_byte(unsigned char data, int expect_ack)

{

   uint_32  param;

   uint_32  n;

 

   n = fwrite (&data, 1, 1, i2c_fd);

   if (1 != n)

   {       // Error Handling   }

 

   /* Check ack */

   if (I2C_OK == (errorMQX = ioctl(i2c_fd, IO_IOCTL_FLUSH_OUTPUT, &param)) )

   {

      if (param != expect_ack)

      {

          if (expect_ack == EXPECT_ACK)

          {    // Error Handling        }

          else

         {     // Error Handling         }

      }

   }

   else

   {  // Error Handling

  }

   return errorCode;

}

 

 

////////////////////////////////////////////////////////////

uint_32 I2C_read_byte(uint_8 * buf, int requestBytes)

{

   uint_32                n;

 

   if (I2C_OK != ( errorMQX = ioctl (i2c_fd, IO_IOCTL_I2C_SET_RX_REQUEST, &requestBytes)) )

   {       // Error Handling  }

 

   /* Read all data */

   n = fread (buf, 1, requestBytes, i2c_fd);

   if (n != requestBytes)

   {    // Error Handling   }

 

   /* Wait for completion */

   if (MQX_OK != (errorMQX = fflush (i2c_fd)) )

   {       // Error Handling   }

   return errorCode;

}

 

 

// example  /////////////////////////////////////////////////////////

 I2C_start();

 I2C_send_address(DS2482_I2C_ADDRESS);

 I2C_write_byte(DS2482_CMD_1WT, EXPECT_ACK);

 I2C_write_byte(search_direction ? 0x80 : 0x00, EXPECT_ACK);

 I2C_stop();

 

 

///////////////////////////////////////////////

  I2C_start();

  I2C_send_address(DS2482_I2C_ADDRESS);

  I2C_write_byte(DS2482_CMD_DRST, EXPECT_ACK);

  I2C_rep_start();

  I2C_read_byte(&status, 1);

  I2C_stop();

0 Kudos

2,310 Views
ARQuattr
Contributor IV

Thank you eGuy.  What I'm doing is not structured as yours, but I think it's functionally equivalent.  For some reason I still get clocks for an extra byte when I do a read.  One thing I'm not doing is the 0-byte write - is this critical?  For a read operation without a repeated start, would I do this...?

  I2C_start();

  I2C_send_address(DS2482_I2C_ADDRESS);

  I2C_read_byte(&status, 1);

  I2C_stop();

 

Can I do two SET_RX_REQESTS with only one stop bit, so that I can check a byte in the message before reading the rest?

 

Also, I have a I2C slave device that will take up to 80ms to ack a read command under certain conditions.  I'm not sure if this meets the I2C protocol but is there a way to have the I2C driver wait a certain time for the ack before aborting the transmission?

 

 

0 Kudos

2,310 Views
eGuy
Contributor IV

a)  0-byte write is critical for later I2C write ( in my case )  ------ actually, in the lower level, the driver will secretly send out destination address when you do 0-byte write.

 

b) You definitly need a repeated start  if you change from I2C write to I2C read in one I2C session----- some I2C devices may not distinguish between "Start" and "Repeat Start", that is not right according to I2C protocol.

 

c) You need I2C_stop between the two SET_RX_REQUESTS.  The lower level driver will send out ACK signal to I2C device when it receives requested amount of bytes.....

 

You should do this: ( remove the send_address() part.)

  I2C_start();

  I2C_read_byte(&status, 1);

  I2C_stop();

0 Kudos

2,310 Views
ARQuattr
Contributor IV

Thanks again for your reply.

 

1. Doesn't a write with non-zero bytes also send the address byte first?  This is what it seems to do for me.  Adding the zero-byte write first doesn't seem to change anything, as subsequent writes just avoid sending the address.  (I have already set the destination address previously.)

 

2. I'm used to using I2C on other platforms (eg PICs) and am familiar with using the repeated start, but this is my first time using I2C under MQX.  According to the manufacturer of this device (SL030 RFID reader from StrongLink: http://www.stronglink.cn/download/SL030-User-Manual.pdf) I need to send a complete write command (with stop), followed by a read request.  It seems strange, and the data sheet is not the best I've seen, but their example code seems to work the same way.  I've tried with a repeated start also and it didn't help.  I'll try it again with your code example.

 

3. Thanks for confirming this.  I guess I will just have to read some maximum number of bytes.  What I found interesting is that when receiving the last byte requested, MQX doesn't ack.  I'm not sure if that's normal, or if that's a problem for the slave.  At the same time, if I do two reads on one SET_RX_REQUEST, the first read clocks one extra byte, and the second read clocks one less byte (and it still sends no ack on the last byte).  It's OK if I just do one read, aside from having no ack on the last byte.

 

It's frustrating.  I'm suspicious that there might be some bugs in the MQX I2C driver, so I'm considering rewriting it to use the MCF registers, but would prefer to avoid that.

 

Thanks again for any support,

Angelo

 

 

0 Kudos

2,310 Views
eGuy
Contributor IV

 


----- You are welcome.

 

1.

----- Yes. When I2C bus is in IDLE or already STOPPED state. The lower level driver will  first send the address when you call  fwrite (....., i2c_fd).  I did not like this. and made the address sending explicit. That is why I use 0-byte write first

 

 

2.

----- I quickly looked throuh the SL030 manul. Just want to confirm that the I2C device address you specified is 0x50 ( or 0x51, 0x52, 0x53). The driver will shift 1bit left and change bit0 to '0' or '1' based on Write or Read

 

3.

----- The master ACKs to each byte it receives in the middle of Reading session except the last one ( For the last byte, it sends out STOP instead.  Your manual is correct)

 

4.

---- You can not do two SET_READ_RX_REQUEST from the SL030 manual.

When you do the first RX_REQUEST read, the I2C driver will not ACK when it receives "Len" byte.  The driver think you already finished this Read session ( thus NO ACK) ( see i2c_pol_mcf52xx.c line 739 ~ 749 )

 

 

 


 

0 Kudos

2,310 Views
ARQuattr
Contributor IV

1. OK, makes sense.

 

2. Yes, I'm using 0x50 as the address (and the jumpers are clear).  It seems to be addressing properly since the unit does respond.

 

3. OK, if that's expected behaviour then I'm good.

 

4. OK, I'll settle with one long read for now. 

 

>>When you write a command. you already know the length of corresponding response message from the manual. So you really do not need the "Len" in the Read package.

 

The main command I will use is 0x01 (Select card).  This will return only three bytes if there is no card, and either 8 or 11 bytes if there is a card, depending on the card type.  So I don't know in advance how many bytes to expect.

 

 

My only remaining issue then is how to make the driver wait a longer period for the slave to ACK before aborting.  When there is no card the slave ACKs the read fine and I get the short 'no card' response.  According to the mfgr. when there is a card present, the processor on the slave is busy communicating with it and there can be up to 80 ms delay in ACK'ing the read request.

 

Regards

0 Kudos

2,308 Views
eGuy
Contributor IV

Angelo,

 

4) hope that one long read works.

 

Regarding the longer waiting  period, I think you can try to do this:

ioctl(i2c_fd, IO_IOCTL_FLUSH_OUTPUT, &param) will return to you in param that if or not slave device ACKed or NO-ACKed.

 

If NO-ACKed, that you wait a small amount of time, and then try to interrogate it again........  ( in a WHILE loop, sending Select Card command, sending Read response command, If NO-ACK, wait and repeat.....   Timeout  after 80 ms and  report error)

 

Jeff

 

 

 

 

 

 

0 Kudos

2,308 Views
ARQuattr
Contributor IV

I had tried previously to keep retrying the read and flush which didn't seem to work.  I tried looping over the full write and read on your suggestion which didn't fix it, but if I just loop over read transaction (SET_RX_REQUEST, read, stop) I do eventually get an ACK from the slave.  So it looks like I'll be able to get this to work.  Thanks again for your help.

 

I noticed you used fflush() in I2C_read_byte.  Is this different than sending the IO_IOCTL_FLUSH_OUTPUT ioctl in this context?

 

 

0 Kudos

2,308 Views
eGuy
Contributor IV

 

Glad to hear that things work out.
fflush is upper level call, it will eventually call IO_IOCTL_FLUSH_OUTPUT. Basically both are same.

 

0 Kudos

2,308 Views
oldone1
Contributor II

FYI,

 

I just had problems with the Freescale mpl115 device on the TWR-SEN pack and found the part did not hold the ACK long enough, with the default I2C baud rate for the K60 I am using, I would see a NACK.

 

Changed to the largest speed possible and the part started working.

0 Kudos

2,310 Views
eGuy
Contributor IV

Just curious.

When you write a command. you already know the length of corresponding response message from the manual. So you really do not need the "Len" in the Read package.

0 Kudos

2,310 Views
CarlFST60L
Senior Contributor II

I dont understand why the I2C fwrite command fails to return when the I2C slave device doesnt ack the first data byte after the address i.e. I2C slave ACK's address, then master sends first data byte, I2C slave does NOT ack the first data byte, fwrite NEVER returns locking up the thread...

 

Is this normal? How do I stop this from happening?

0 Kudos

2,310 Views
PetrM
Senior Contributor I

The only reason I can think of is that driver is waiting for the bit flag IIF which is set after ninth bus clock and it doesn't come, because the slave device hold's the bus down (and locks) after eighth clock to be able to react with ack/nack.

 

PetrM

 

0 Kudos

2,310 Views
CarlFST60L
Senior Contributor II

I had a quick look at the driver and it does wait 'forever' if the slave device locks the line up... Should there be some consideration for a timeout ioctl option to allow the driver to return an error during failure?

 

Currently my software has a higher priority thread which that will restart any I2C task that locks up, it also reboots the slave devices attached to the I2C bus.

0 Kudos

2,310 Views
ARQuattr
Contributor IV

CarlFST60L,

  I have been seeing very similar behaviour.  I'm wondering if you figured out what was causing it?

 

Thanks

0 Kudos

2,310 Views
PetrM
Senior Contributor I

IO_IOCTL_I2C_SET_STATION_ADDRESS

Describes I2C address of this device in slave mode (only). Can be set anytime. Processor reacts to this address sent over the bus since then. Register I2ADR.

 

IO_IOCTL_I2C_SET_DESTINATION_ADDRESS

Describes I2C address used when initiating transfer as master (only). Can be set anytime. Processor sends this address at the beginning of any data transfer since then. No register.

 

IO_IOCTL_I2C_SET_RX_REQUEST

This IOCTL must be called before each read transfer. The driver has to know the amount of bytes to be read in advance for proper ack/nack handling. No register.

 

IO_IOCTL_I2C_GET_STATISTICS

This allows application to retrieve statistics counters for tx/rx bytes, interrupts, naks etc. No register.

 

IO_IOCTL_FLUSH_OUTPUT

This IOCTL must be used by the application before issuing STOP to ensure that all bytes have been clocked in/out over the bus before releasing it. Waits for transfer completion. No register.

 

Typical I2C usage:

 

i2cfd = open ("i2c0:", NULL);    // open driver as master

param = 0x60; ioctl (i2cfd, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);   // my I2C address is 0x60

param = 0x50; ioctl (i2cfd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param);   // call device with address 0x50

param = 0x10; ioctl (i2cfd, IO_IOCTL_I2C_SET_RX_REQUEST, &param);    // want to read 16 bytes in total

fread (buffer, 1, 10, i2cfd);    // initiate transfer, send destination address (first use of read/write), read 10 bytes from device
fread (buffer, 1, 6, i2cfd);    // read remaining 6 bytes
ioctl (i2cfd, IO_IOCTL_FLUSH_OUTPUT, &param);    // wait for completion, retrieve ack in param

ioctl (i2cfd, IO_IOCTL_I2C_STOP, NULL);    // stop transfer, release the bus

For more information, please see I2C example (mqx\examples\i2c).

 

PetrM

 

 

 

0 Kudos

2,310 Views
ARQuattr
Contributor IV

PetrM,

  You outlined the general steps for reading data via I2C using IO_IOCTL_I2C_SET_RX_REQUEST.  What would be the recommended approach if I don't know the length ahead of time.  The message I'm getting from the slave starts with the message length, followed by that number of bytes. 

 

  For now I'm just trying to set the request length to some assumed upper limit, reading one byte (to get the length) then reading that many more bytes.  Aside from the fact this doesn't seem right to me, for some reason I always read out two bytes instead of one.  fread(buf, 1, 1, fd) returns 1, but the chip actually clocks in two bytes (after sending the address byte).  Similarly, fred(buf, 1, 0, fd) returns 0 but it actually clocks in one byte.  Any thoughts as to why this would be?

 

Thank you

0 Kudos

2,310 Views
wne
Contributor I

In demo i2c m52259evb project, eeprom_polled.c file, line 88, function call "fwrite (&mem, 1, 0, fd)", per the comment above it, has two proposes: 1. initiate start I2C. 2. send I2C bus address. Similar function call appear in line 113, which is "fwrite (&mem, 1, 1, fd)". Does it also initiate a I2C start first?

In manual Freescale MQX I/O drivers users guide, 10.6 I/O control command, the description for IO_IOCTL_FLUSH_OUTPUT is "Flushes output buffer, waits for transfer to finish". What does the "output buffer" mean here? Where is it?

Sorry for so many questions!

0 Kudos

2,310 Views
wne
Contributor I

PetrM,

In file eeprom_polled.c, which is in i2c m52259evb demo project, I am little confused by the fwrite function, For example, in line 88, "fwrite (&mem, 1, 0, fd)" will write the content in "&mem" to file device "fd", could you explain:

1. how "mem" was set before calling this function?

2. what do the 2nd parameter "1" and 3rd "0" mean? As I guess, in line 113, the "fwrite" send memory internal address out, and in line 123, the "fwrite" send data which is to be written to the memory out. Why, between the two "fwrite's", there is no ACK checking?

3. What is the behind scene of the function "fflush", for example in line 133? Could you explain the command "ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param))", how this command check the ACK? Thanks.

0 Kudos

2,310 Views
PetrM
Senior Contributor I

I2C master mode is default, so calling this IOCTL command does not change anything in the example.

 

fwrite (mem, 1, 0, fd) is dummy send of 0 units of 1 byte size from mem buffer (so the buffer doesn't have to be initialized), this only generates START condition on the bus (as it's first read/write call) and sends the I2C destination address.

 

There is kind of automatic checking of the ACK - the tx is stopped by the driver right after it gets NACK. You can check it by number of bytes returned by fwrite or by IO_IOCTL_I2C_GET_STATE command. Also using ioctl (fd, IO_IOCTL_FLUSH_OUTPUT, &param) command, the ACK bus state of last byte transmitted is returned in param variable.

 

fflush (fd) is the same as IO_IOCTL_FLUSH_OUTPUT command, it just doesn't return the ACK value.

The command waits until all data are transmitted/received from/to the buffer you passed to the functions fwrite/fread. Then it's safe to call IO_IOCTL_I2C_STOP command (to generate STOP condition on the bus).

 

Functions fwrite/fread generate START condition and send destination address, only when bus is IDLE (the first time they are used until STOP condition).

 

PetrM

 

0 Kudos