I2C issue on 521x

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

I2C issue on 521x

2,188 Views
EricT
Contributor I
Hi - I am running into an I2C issue on an MCF5212.  Using the sample code from Freescale for an I2C solution using interrupts, I get stuck in infinite loops.  It happens sometimes (not every time, but more than a third of the time) and only with the first message I try to transmit.  Here's the sample code fragment:

        /* Make sure bus is idle */
        while (MCF_I2C_I2SR & MCF_I2C_I2SR_IBB)
        ;
        /* Put module in master TX mode (generates START) */
        MCF_I2C_I2CR |= (0 | MCF_I2C_I2CR_MSTA | MCF_I2C_I2CR_MTX);
        /* Put target address into I2DR */
        MCF_I2C_I2DR = (uint8)( 0 | slave_address | mode);
        /* Wait for I2SR[IBB] (bus busy) to be set */
        while (!(MCF_I2C_I2SR & MCF_I2C_I2SR_IBB))
        ;
   
        /* Wait for bus to become free before continuing */
        while (MCF_I2C_I2SR & MCF_I2C_I2SR_IBB)
        ;

OK, here, we first make sure the bus is idle, then we put ourselves in master transmit mode, and write the address and read/write mode to the data register.  Then we wait until the bus gets busy (meaning that we are transmitting) and then wait for the bus to free up, indicating that the address has been successfully transmitted.

I have run into problems with getting stuck at all three of those while loops, especially the second one where I wait until the bus becomes busy after writing the address to the data register.  I am running at 100KHz, so I am using an appropriate speed.  I have had a scope hooked up when it got stuck in that second while loop and at that point and the busy bit (IBB) is clear, meaning that the bus is not busy, but the scope showed that SDA was high while SCL was low, which by definition means that the bus is busy, right?  If the bus is idle, both of them should be high.  Therefore, I wonder how much I can trust IBB.

I've also tried to use ICF (I can't use IIF because I am using interrupts, right?) in place of the last two uses of IBB - after all, it makes much more sense to me to key on a bit telling me that a transfer is in progress rather than just checking whether the bus is idle or not.  However, ICF doesn't seem to work at all.

Is there something subtle that I don't know that would make those two bits behave properly?  This problem has been very frustrating.

First of all, has anyone else seen something similar and found a way around it?
Labels (1)
0 Kudos
4 Replies

501 Views
mccPaul
Contributor I
Hi
 
Here's what I do on the 5282 - should be pretty similar. Init code:
 
Code:
// Set 100kHzMCF5282_I2C_I2FDR = MCF5282_I2C_I2FDR_IC(0x15); // Set module's I2C addressMCF5282_I2C_I2ADR = 0x60;// Enable the i2c moduleMCF5282_I2C_I2CR = MCF5282_I2C_I2CR_IEN | MCF5282_I2C_I2CR_IIEN;if (MCF5282_I2C_I2SR & MCF5282_I2C_I2SR_IBB){ // If the bus is busy send a STOP command MCF5282_I2C_I2CR = 0; cpu_pause(10000); // 10ms wait MCF5282_I2C_I2CR = MCF5282_I2C_I2CR_IEN | MCF5282_I2C_I2CR_MSTA; cpu_pause(10000);    // Dummy read of IBDR to signal the module is ready for the next byte dummy_read = MCF5282_I2C_I2DR; cpu_pause(10000); MCF5282_I2C_I2SR = 0; cpu_pause(10000); MCF5282_I2C_I2CR = 0; cpu_pause(10000); MCF5282_I2C_I2CR = MCF5282_I2C_I2CR_IEN | MCF5282_I2C_I2CR_IIEN}

 
Transmit:
 
Code:
Code:// Generation of i2c START// Wait for the bus to become free.while (MCF5282_I2C_I2SR & MCF5282_I2C_I2SR_IBB) ; // Set transmit modeMCF5282_I2C_I2CR |= MCF5282_I2C_I2CR_MTX;  // Set master mode (generates START)MCF5282_I2C_I2CR |= MCF5282_I2C_I2CR_MSTA;  // Transmit the slave address// Set the R/W bit for RX, otherwise clear it.MCF5282_I2C_I2DR = SLAVE_ADDRESS;// Don't bother to wait for bus busy, just wait until the ISR signals// that the I2C transaction is completeWhile (i2c.status == I2C_IN_PROGRESS)      ; // Reset moduleMCF5282_I2C_I2CR = MCF5282_I2C_I2CR_IEN | MCF5282_I2C_I2CR_IIEN;

 
I have an ISR that deals with I2C module interrupts and it will transmit/receive until an error or completion.
 
Cheers,
 
Paul.
0 Kudos

501 Views
EricT
Contributor I
This method helped a ton - I'm no longer running into infinite loops by using a signal like that.  However, I am running into another issue due to the way that the interrupts work.  Since getting a stop signal indicating the end of a message does not cause an I2C interrupt, I'm having truoble dealing with detecting the end of a message.  The amount of time that it could take to get the stop signal is much too long to sit around and wait for it.  The way that Freescale's sample code deals with it is to have the main loop of the program doing nothing but constantly checking for the end of message condition.

Unfortunately, this is not a practical way to handle the issue in the real world, so I had to find another way to do it.  My current solution is to use a PIT (programmable interrupt timer) to look for the condition. Basically, as soon as I start to receive a message, I start the timer up and have it check every 30 microseconds (a value I picked out of the air - to really meet the I2C spec it would need to be even more often than that).  Once the condition has been detected (or once we start to send or receive another message, meaning that the previous message must be done) the message received gets passed on and the PIT gets turned off.  This method works, but I'm not sure if this will put a major strain on the processor due to the large amount of time it will spend checking the bus.  Does anyone have a more elegant solution to this issue?
0 Kudos

501 Views
mccPaul
Contributor I
Hi
 
Is your CPU the slave then? I've only used I2C where the CPU is a master so it is in charge of generating stop signals. If you are a slave and you have no idea how long a data transfer is going to be then I suspect that polling for bus busy is the only way to tell if the master has finished sending data. As you will be interrupted each time a character is received it seems reasonable to use a timer.
 
Your I2C ISR should reset the timer every time a new character is received, so there will only be a very small overhead. The timer ISR should only run when no characters have been seen for a while. If the bus is still busy, then you can reset the timer. If the bus has been released (STOP) you know that the transfer is over.
 
Paul.
0 Kudos

501 Views
EricT
Contributor I
Yes, in this application, both sides always send as masters and receive as slaves.  Currently, I have the timer start up as soon as we start receiving a byte and it gets turned off when we decide a message has been completed either because we have spotted the idle bus or because we have started to receive a new message (which obviously means that the previous message must be complete).

I will experiment with different times for the timer to find the best balance.  Thanks for your help!
0 Kudos