AN3291 oddity (problems with I2C device on QG8)

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

AN3291 oddity (problems with I2C device on QG8)

9,497 Views
jarin
Contributor I
Hi all,
I'm trying to connect 24FC512 EEPROM to QG8 device. I downloaded AN3291 and copied routines to read/write from EEPROM with I2C module in HCS08 devices into my project, after a slight changes (like converting IIC1S to IICS and so on) I downloaded code to FLASH and the routines behaved somehow strange. Write routine followed with read routine worked, but reading byte back stopped at waiting for ACK signal after third transmitted byte (first is device address, second and third are memory address bytes). I searched for problem in read routine, but when I placed two write routines subsequently, the second one stopped at the same place, indicating the is something wrong with the write routine. Putting AN in contrast with datasheet showed that IICIF flag should be cleared (by writing 1) after writing data byte into EEPROM.
This is how routine looked originally:

void I2C_write_byte (word addr,byte data)
{
EE_Addr = addr; // address input;
temp = (EE_Addr_H & 0x07) 1;
IIC1C_TX = 1; // Set transmit mode;
//-------start of transmit first byte to IIC bus-----
IIC1C_MST = 1; // Start transfer - Master bit = 1;
IIC1D = sl_addr | temp; // send "Write" + high address;
while (!(IIC1S_IIC1IF));
IIC1S_IIC1IF = 1;
while (IIC1S_RXAK); // wait for ACK from slave;
//-----Slave ACK occurred------------
IIC1D = EE_Addr_L; // send low address byte;
while (!(IIC1S_IIC1IF)); // wait till data transferred;
IIC1S_IIC1IF = 1;
while (IIC1S_RXAK); // wait for ACK from slave;
//-----Slave ACK occurred------------
IIC1D = data; // write data byte into EEPROM;
while (!(IIC1S_IIC1IF)); // wait till data transferred;
while (IIC1S_RXAK); // wait for ACK from slave;
IIC1C_MST = 0; // STOP bit;
}

and this is how the routine works for me:

void I2C_write_byte (word addr,byte data)
{
EE_Addr = addr; // address input;
temp = (EE_Addr_H & 0x07) 1;
IIC1C_TX = 1; // Set transmit mode;
//-------start of transmit first byte to IIC bus-----
IIC1C_MST = 1; // Start transfer - Master bit = 1;
IIC1D = sl_addr | temp; // send "Write" + high address;
while (!(IIC1S_IIC1IF));
IIC1S_IIC1IF = 1;
while (IIC1S_RXAK); // wait for ACK from slave;
//-----Slave ACK occurred------------
IIC1D = EE_Addr_L; // send low address byte;
while (!(IIC1S_IIC1IF)); // wait till data transferred;
IIC1S_IIC1IF = 1;
while (IIC1S_RXAK); // wait for ACK from slave;
//-----Slave ACK occurred------------
IIC1D = data; // write data byte into EEPROM;
while (!(IIC1S_IIC1IF)); // wait till data transferred;
//*****************************here is flag clearing*************************
IIC1S_IIC1IF = 1; //!!!!!!!!!!!ADDED LINE!!!!!!!!!!!!
//***************************************************************************
while (IIC1S_RXAK); // wait for ACK from slave;
IIC1C_MST = 0; // STOP bit;
}

I think it is quite logical to clear flag after it was set. This is also mentioned in datasheet for QG8 device (figure 11-3) - there is difference, described as interrupt routine, but it can be easily converted into polled routines, IICIF flag is cleared after EVERY entry to interrupt routine, it means everytime when IICIF flag is set.
After adding line routines work like expected - I'm able to write/read from external EEPROM.

Is the missed line mistake in document or I have got another problem?


By the way, interesting fact is that the write/read sequence worked well when doing single steps through routine code AND with Data window opened. With Data windows closed, routine hanged on the same place as before. It is probably because I2C module registers were showed in Data window and thus read by BDM POD.
Labels (1)
0 Kudos
33 Replies

663 Views
JimDon
Senior Contributor III

Coffi,

 

I can tell you for sure one very obvious error in your code.

In the spec sheet for the EEPROM you will find that after writing it take a long time to complete the write, because it has to write back a whole sector and writing EEPROM just takes longer than say ram.

It can be 20ms or more. During that time, the device will NACK your operations, and you will indeed read FF.

One way to see if this is that case, comment out the write, and run the code to see if the read works.

 

0 Kudos

663 Views
el_chapi
Contributor I

Hi, anyone has a routine to read 2 bytes? i can't find anyone. Im using a mcf51 and i need it to read 2 bytes from a SGTL5000 Audio Codec register. I try to adapt one of the posted here but i can't make it work. Thanks for your help!

0 Kudos

663 Views
Richly
Contributor III

Like many others who have written to the forums, I've given up on the built-in I2C module and gone back to bit-banging, which just works.

 

If you've never done bit-banging before, I'll tell you the secret, learned the hard way many many years ago: when you write out a byte to the I2C buss, you write the first seven bits in the obvious fashion, but the eighth bit is special.  If it's a 0, you proceed as usual, i.e., write 0 to the port bit and flog the clock, and then you switch the port bit to an input to read the ACK from the receiver.  But if the eighth bit is a 1, you write nothing to the port but switch the port bit to an input, letting the pull-up resistor (there must be one!) pull the line high for the last bit, then flog the clock, after which the receiver can send the ACK.

 

Good luck,

    Richly

0 Kudos

663 Views
jiri_naprstek
Contributor I

Sorry to splitting the message, i've forgotten:

 

while (!(IIC1S_IICIF));     // wait till data transfered;
IIC1S_IICIF = 1;             // clear the flag;        
while (IIC1S_RXAK);        // wait for ACK from slave;

 

I'm finding this piece of code in the example a bit misleading. Waiting for the ACK is IMNSHO not correct.

First, the ACK may or may not arrive (that's why the ACK is there) so if the slave doesn't report the ACK the code will freeze.

Second, based on the scope observation and also the code behavior, the RXAK is a flag, not a trigger. It is available before the interrupt is issued (it is just copy of what has happend on the bus during ACK period). Therefore I believe that just checking the ACK and aborting the rest of the operation (e.g. if ( IICS1_RXAK ) return (FAIL); ) is correct.

0 Kudos

667 Views
Stano
NXP Employee
NXP Employee

Hello,

 

I saw my source code of IIC routines for Read and Write functions, but I turn ON the IIC module once only - in the Init routine. Then I use the Read or Write functions to put or get data to/from IIC device. I think the best way is check your source code - so send it to me please and I will check it. Send me please the type of IIC device used in your application too, otherwise I can check it with IIC memory only.

 

Thanks,

Stano.

0 Kudos

667 Views
Stano
NXP Employee
NXP Employee

Hi irob,

 

This address is really word format, because it is not only lower 8-bits but still upper 3 bits in the control byte. For detail information look at the datasheets of e.g. 24C16 or 24C32 EEPROMs.

Mentioned datasheets can be on the microchip web page.

 

Stano.

0 Kudos

667 Views
FC
Contributor III
Can you use the TCF flag rather than the IICIF flag for byte transfer completion?
 
example:
 
while(! (IICS_TCF));
 
Also, does the simulator work for IIC module? 
 
Thanks
0 Kudos

667 Views
Stano
NXP Employee
NXP Employee
I suggest you use the IBIF flag instead TCF. The communication is relable. On the other site, the IBIF flag you can use for interrupt generation and make the code for IIC device access by the interrupts.
 
The CW Simulator does not simulate IIC comunication because you need the slave answers too and the simulator does not know simulate any slave IIC device.
 
The best way to debug or build the IIC SW code is use the storage scope.
 
Best Regards,
 
Stano.
0 Kudos

667 Views
Stano
NXP Employee
NXP Employee
Hi all,
 
I'm an author of the AN3291.
 
Thank you for your important feedback. I apologize for that mistake - you are right.
 
I have been tested the WRITE and READ routines from my AN3291. One mistake (mentioned previously) was in the write routine and I found another one in the read routine too. Now both routines are correct and they are in follow:
 
void I2C_write_byte (word addr,byte data)
{
    IICS_IICIF = 1;
    EE_Addr = addr;                // address input;
    temp = (EE_Addr_H & 0x07) << 1;
    IICC_TX = 1;                      // Set transmit mode;
       //-------start of transmit first byte to IIC bus-----
    IICC_MST = 1;                  // Start transfer - Master bit = 1;
    IICD = sl_addr | temp;      // send "Write" + high address;
    while (!(IICS_IICIF));
    IICS_IICIF = 1;        
    while (IICS_RXAK);          // wait for ACK from slave;
      //-----Slave ACK occured------------
    IICD = EE_Addr_L;          // send low address byte;
    while (!(IICS_IICIF));        // wait till data transfered;
    IICS_IICIF = 1;        
    while (IICS_RXAK);          // wait for ACK from slave;
      //-----Slave ACK occured------------
    IICD = data;                    // write data byte into EEPROM;
    while (!(IICS_IICIF));       // wait till data transfered;
    IICS_IICIF = 1;      // - ADDED;
    while (IICS_RXAK);        // wait for ACK from slave;
    IICC_MST = 0;               // STOP bit;
}
 
byte I2C_read_byte(word addr)
{
   IICS_IICIF = 1;
   EE_Addr = addr;               // address input;
   temp = (EE_Addr_H & 0x07) << 1;
   IICC_TX = 1;                     // Set transmit mode;
       //-------start of transmit first byte to IIC bus-----
   IICC_MST = 1;                  // Start transfer - Master bit = 1;
   IICD = sl_addr | temp;      // send "Write" + high address;
   while (!(IICS_IICIF));
   IICS_IICIF = 1;        
   while (IICS_RXAK);          // wait for ACK from slave;
       //-----Slave ACK occured------------
   IICD = EE_Addr_L;             // send low address byte;
   while (!(IICS_IICIF));
   IICS_IICIF = 1;        
   while (IICS_RXAK);           // wait for ACK from slave;
       //-----Slave ACK occured------------
   IICC_RSTA = 1;               // Generate repeat START condition;
   IICD = sl_addr | temp | 1; // Send combined address & "Read" command;
   while (!(IICS_IICIF));
   IICS_IICIF = 1;        
   while (IICS_RXAK);         // wait for ACK from slave;
       //-----Slave ACK occured------------
   IICC_TX = 0;                   // Set receive mode;
   IICC_TXAK = 1;              // Last byte will be read; - ADDED;
   temp = IICD;                   // read IIC data
   while (!(IICS_IICIF));
   IICS_IICIF = 1;
   IICC_MST = 0;               // STOP bit;
   rd_data = IICD;
   return rd_data;
}
I made tests on my 9S08QG8 MCU with the 24LC16B serial EEPROM. The code in the AN3291 will be corrected as soon as possible.
 
I will make tests with the mentioned EEPROMs 24FC512 and QG8 MCU after I receive this type of EEPROM. I guess it could be finished next week - then I will place the message to this forum.
 
Sorry once again for this mistake.
 
Stano.
 
0 Kudos

667 Views
irob
Contributor V

Stano, thanks for the clarification.  I see what you're saying now.  I was just compariing the diagram in your AN to your code and didn't come up with the right amount of bits.  But I see what you're saying now.

 

I've got my software written now for a 1-byte addressable I2C peripheral, but I'm having a curious problem with it.  I'm using the MC9S08QG8 and I have found that only in the debugger will the I2C module work correctly.

 

For instance, if I step through my code, I am able to trace through and over a complete I2C Write and I2C Read function, both working as expected.  But if I let the program run, it getshung up in the interrupt flag polling while().

 

Another strange thing is that the entire I2C module won't seem to turn on without the use of this interrupt poll.  I attempted -- like user "FC" above suggested -- to use the transfer complete (TOF) flag instead of the interrupt flag to wait for completion.  In that iteration, the SCL won't even start.  It's like the whole module is just dead.  Can you explain why that would be?  What is the TOF bit for then?

0 Kudos

667 Views
irob
Contributor V

Stano wrote:
 
I'm an author of the AN3291...
 
void I2C_write_byte (word addr,byte data)
{
    IICS_IICIF = 1;
    EE_Addr = addr;                // address input;
    temp = (EE_Addr_H & 0x07) << 1;
    IICC_TX = 1;                      // Set transmit mode;
       //-------start of transmit first byte to IIC bus-----
    IICC_MST = 1;                  // Start transfer - Master bit = 1;
    IICD = sl_addr | temp;      // send "Write" + high address;
    while (!(IICS_IICIF));
    IICS_IICIF = 1;       
    while (IICS_RXAK);          // wait for ACK from slave;
      //-----Slave ACK occured------------
    IICD = EE_Addr_L;          // send low address byte;
    while (!(IICS_IICIF));        // wait till data transfered;
    IICS_IICIF = 1;       
    while (IICS_RXAK);          // wait for ACK from slave;
      //-----Slave ACK occured------------
    IICD = data;                    // write data byte into EEPROM;
    while (!(IICS_IICIF));       // wait till data transfered;
    IICS_IICIF = 1;      // - ADDED;
    while (IICS_RXAK);        // wait for ACK from slave;
    IICC_MST = 0;               // STOP bit;
}

 


Stano, I'm confused in several areas with your app note and your code snippet (let's focus on the write routine.

 

First of all, the AN shows a nice write format in Figure 4 (for one byte address) and in Figure 5 (for two byte address) on page 4.  However, in Figure 4, the address is described as "Word Address", even though it's an 8-bit word.  Same goes for Figure 5.

 

Your example code seems to be the 2-byte address variety with low and high bytes.  Yet, the 4-bit control code section seems to be mixed up with the high byte.  Shouldn't the control bits + the lower three device address bits + Write bit be sent first to the device?  Then, you send the high and low address bytes?

0 Kudos

667 Views
Alban
Senior Contributor II
Thanks Jarin,
Reported to AN3291 author with the link to your description.
 
 
0 Kudos

667 Views
ddz
Contributor I
I'm experiencing same problem with the code in that AN. I will try your fix tomorrow...
 
0 Kudos