This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

I2C/EEPROM Back-to-Back Write Issue (28027F)

Other Parts Discussed in Thread: CONTROLSUITE

Hello all,

I'm writing an I2C implementation to read and write values directly between a local memory pointer on my 28027 and an EEPROM. The problem I'm running into is when I do writes in serial. 

Symptoms:

  1. First write completes successfully (as far as I can tell)
  2. Second write gets NACKed for a period of time (corresponds with the write delay indicated in the EEPROM datasheet: http://www.mouser.com/ds/2/268/21710c-76077.pdf)
  3. Eventually NACK subsides, and the CPU hangs when attempting to write the data bytes for the second write (

I'm using adapted code from several examples seen in ControlSuite and on the forums. A couple of important notes:

  • I am currently using the FIFOs. Given what I'm reading on other I2C/EEPROM related posts, I'm not sure if this is the right move.
  • I am using the interrupts for Stop condition and RegReady
  • I've constructed a simple I2C message queue (a basic LinkedList implementation) to manage the list of values I want to read/write. This code has not been included below, but happy to if necessary.

I've included as much relevant code as I can think of below. Any help would be greatly appreciated.

I2CMessage Type

// I2C Message Structure
typedef struct _I2CMessage_ {
	uint16_t type;					  // message type (TX/RX)
	uint16_t status;                // Word stating what state msg is in:
																	//   I2C_MSGCMD_INACTIVE = do not send msg
																	//   I2C_MSGCMD_BUSY = msg start has been sent,
																	//                     awaiting stop
																	//   I2C_MSGCMD_SEND_WITHSTOP = command to send
																	//       master trans msg complete with a stop bit
																	//   I2C_MSGCMD_SEND_NOSTOP = command to send
																	//       master trans msg without the stop bit
																	//   I2C_MSGCMD_RESTART = command to send a restart
																	//       as a master receiver with a stop bit
	uint16_t slaveAddress;            // I2C address of slave msg is intended for
	uint16_t byteCount;            // Num of valid bytes in
	uint16_t bufferPosition;
	uint16_t memAddress;        // EEPROM address of data associated with msg (high byte)

	struct _I2CMessage_ *next;			  // linked list's next node
	void *localPointer;
} I2CMessage;

Setup Routine in hal.c

void HAL_setupI2CA(HAL_Handle handle)
{
	HAL_Obj *obj = (HAL_Obj *)handle;

	// Slave address - EEPROM control code
	I2C_setMasterSlaveAddr(obj->i2cAHandle, 0x0050);

	// setting below for CPU Frequency of 60Mhz
	I2C_setupClock(obj->i2cAHandle, 6, 10, 5);

	// Enable SCD & ARDY interrupts
	I2C_enableInt(obj->i2cAHandle, I2C_IntEn_Stop);
	I2C_enableInt(obj->i2cAHandle, I2C_IntEn_Reg_Rdy);

	// Take I2C out of reset
	I2C_enable(obj->i2cAHandle);

	// Enable FIFO mode and TXFIFO
	I2C_enableFifo(obj->i2cAHandle);
	I2C_resetTxFifo(obj->i2cAHandle);

	// Enable RXFIFO, clear RXFFINT
	I2C_resetRxFifo(obj->i2cAHandle);
	I2C_clearRxFifoInt(obj->i2cAHandle);

	return;
}

Write Method in i2c.c

uint16_t I2C_writeData(I2C_Handle i2cHandle, I2CMessage *msg)
{
	 // Wait until the STP bit is cleared from any previous master communication.
	 // Clearing of this bit by the module is delayed until after the SCD bit is
	 // set. If this bit is not checked prior to initiating a new message, the
	 // I2C could get confused.
	 if(I2C_isMasterStopBitSet(i2cHandle))
	 {
			return I2C_MsgStat_STP_Not_Ready;
	 }

	 // Setup slave address and message block
	 I2C_setMasterSlaveAddr(i2cHandle, (msg->slaveAddress + (msg->memAddress >> 8)));

	 // Check if bus busy
	 if(I2C_getStatus(i2cHandle) == I2C_Status_Busy)
	 {
			return I2C_MsgStat_Bus_Busy;
	 }

	 // send the address low byte
	 I2C_putData(i2cHandle, (msg->memAddress & 0x00FF));

	 // if we're sending <= what the FIFO can hold, send it all
	 uint16_t endPoint;
	 if(msg->byteCount < (I2C_FIFO_LENGTH - 1))
	 {
		 endPoint = msg->byteCount;
	 }
	 // otherwise, send the next n bytes (e.g. if the FIFO is 4, 1 is reserved
	 // for address low, send the first 3 bytes)
	 else
	 {
		 endPoint = I2C_FIFO_LENGTH - 1;

		 // enable TXFIFO interrupt for any addl data bytes
		 i2cHandle->I2CFFTX |= I2C_I2CFFTX_TXFFIENA_BIT;
		 I2C_clearTxFifoInt(i2cHandle);
	 }

	 for (msg->bufferPosition = 0; msg->bufferPosition < endPoint; msg->bufferPosition++)
	 {
		 I2C_putData(i2cHandle, (uint16_t)(*(long*)(msg->localPointer) >> (msg->bufferPosition * 8) & 0x00FF));
	 }

	 // Send start as master transmitter
	 I2C_setRunFree(i2cHandle);
	 I2C_controlAsMaster(i2cHandle, I2C_Control_Single_TX, I2C_BitCount_8_Bits, (msg->byteCount + 1));

	 return I2C_MsgStat_Success;
}

MainISR method section for controlling the message queue

// process the i2c queue
  I2CMessage *currentMessage = I2C_peekQueue();
  if(currentMessage != NULL) {
	 // Are we sending or receiving?
	 if(currentMessage->type == I2C_Control_Single_TX)
	 {
		 // Check the outgoing message to see if it should be sent.
	     // In this example it is initialized to send with a stop bit.
	     if(currentMessage->status == I2C_MsgStat_Send_WithStop)
	     {
		    uint16_t writeResponse = I2C_writeData(halHandle->i2cAHandle, currentMessage);
		    // If the message is successful, update status for the ISR.
		    // If an error occurs, do nothing and try again next loop. Once message is
		    // initiated, the I2C interrupts will handle the rest.
		    if (writeResponse == I2C_MsgStat_Success)
		    {
			   currentMessage->status = I2C_MsgStat_Write_Busy;
		    }
	     }  // end of write section
	 }
	 else if(currentMessage->type == I2C_Control_Single_RX)
	 {
		 if(currentMessage->status == I2C_MsgStat_Send_NoStop)
		 {
			// EEPROM address setup portion
			while(I2C_readData(halHandle->i2cAHandle, currentMessage) != I2C_MsgStat_Success)
			{
			   // Maybe setup an attempt counter to break an infinite while
			   // loop. The EEPROM will send back a NACK while it is performing
			   // a write operation. Even though the write communique is
			   // complete at this point, the EEPROM could still be busy
			   // programming the data. Therefore, multiple attempts are
			   // necessary.
			}
			// Update current message pointer and message status
			currentMessage->status = I2C_MsgStat_Send_NoStop_Busy;
		 }
		 // Once message has progressed past setting up the internal address
		 // of the EEPROM, send a restart to read the data bytes from the
		 // EEPROM. Complete the communique with a stop bit. MsgStatus is
		 // updated in the interrupt service routine.
		 else if(currentMessage->status == I2C_MsgStat_Restart)
		 {
			 // Read data portion
			 while(I2C_readData(halHandle->i2cAHandle, currentMessage) != I2C_MsgStat_Success)
			 {
				// Maybe setup an attempt counter to break an infinite while
				// loop.
			 }
			 // Update current message pointer and message status
			 currentMessage->status = I2C_MsgStat_Read_Busy;
		 }
	 }
  }

I2CA ISR1

__interrupt void i2cAISR1(void)     // I2C-A
{
	 uint16_t interruptSource, i;

	 // Read interrupt source
	 interruptSource = I2C_getIntSource(halHandle->i2cAHandle);

	 // grab the message being processed from the queue
	 I2CMessage *currentMessage = I2C_peekQueue();

	 // Interrupt source = stop condition detected
	 if(interruptSource == I2C_IntSrc_Stop)
	 {
			// If completed message was writing data, reset msg to inactive state
			if (currentMessage->status == I2C_MsgStat_Write_Busy)
			{
						currentMessage->status = I2C_MsgStat_Inactive;
						// dequeue the message so we can process the next
						free(I2C_dequeueMessage());
						I2C_resetAll(halHandle->i2cAHandle);
						I2C_enable(halHandle->i2cAHandle);
			}
			else
			{
				 // If a message receives a NACK during the address setup portion of the
				 // EEPROM read, the code further below included in the register access ready
				 // interrupt source code will generate a stop condition. After the stop
				 // condition is received (here), set the message status to try again.
				 // User may want to limit the number of retries before generating an error.
				 if(currentMessage->status == I2C_MsgStat_Send_NoStop_Busy)
				 {
							currentMessage->status = I2C_MsgStat_Send_NoStop;
				 }
				 // If completed message was reading EEPROM data, reset msg to inactive state
				 // and read data from FIFO.
				 else if (currentMessage->status == I2C_MsgStat_Read_Busy)
				 {
						 currentMessage->status = I2C_MsgStat_Inactive;
						for(i=0; i < currentMessage->byteCount; i++)
						{
								// capture the byte from the RX FIFO
								char byte = I2C_getData(halHandle->i2cAHandle);

								// converting from 8 to 16 bits, the destination pointer's increment is half of `i`
								int localPtrIncr = i / 2;

								// messages in the buffer need to be reversed to their proper destinations
								if((i % 2) == 0) {
									*((uint16_t *)currentMessage->localPointer + localPtrIncr) = byte;
								} else {
									*((uint16_t *)currentMessage->localPointer + localPtrIncr) |= byte << 8;
								}
						}

						// dequeue the message so we can process the next
						free(I2C_dequeueMessage());
						I2C_resetAll(halHandle->i2cAHandle);
						I2C_enable(halHandle->i2cAHandle);
				 }
			}
	 }  // end of stop condition detected

	 // Interrupt source = Register Access Ready
	 // This interrupt is used to determine when the EEPROM address setup portion of the
	 // read data communication is complete. Since no stop bit is commanded, this flag
	 // tells us when the message has been sent instead of the SCD flag. If a NACK is
	 // received, clear the NACK bit and command a stop. Otherwise, move on to the read
	 // data portion of the communication.
	 else if(interruptSource == I2C_IntSrc_Reg_Rdy)
	 {
		if(I2C_isNoAck(halHandle->i2cAHandle))
			{
			I2C_clearNoAckBit(halHandle->i2cAHandle);
			I2C_controlAsMaster(halHandle->i2cAHandle, I2C_Control_Stop, I2C_BitCount_8_Bits, 0);

			// retry on write NACK
			if(currentMessage->status == I2C_MsgStat_Write_Busy)
			{
				currentMessage->status = I2C_MsgStat_Send_WithStop;
			}
			}
			else if(currentMessage->status == I2C_MsgStat_Send_NoStop_Busy)
			{
						currentMessage->status = I2C_MsgStat_Restart;
			}
	 }  // end of register access ready

	 else
	 {
			// Generate some error due to invalid interrupt source
			//__asm("   ESTOP0");
	 }

	 // Enable future I2C (PIE Group 8) interrupts
	 PIE_clearInt(halHandle->pieHandle, PIE_GroupNumber_8);
}

I2CA ISR2

__interrupt void i2cAISR2(void)
{
	uint16_t bytesLeft, endPosition;

	// grab the message being processed from the queue
	I2CMessage *currentMessage = I2C_peekQueue();

	bytesLeft = currentMessage->byteCount - currentMessage->bufferPosition;
	if(bytesLeft <= I2C_FIFO_LENGTH)
	{
		endPosition = currentMessage->byteCount;

		// disable TXFIFO interrupt
		halHandle->i2cAHandle->I2CFFTX &= ~(I2C_I2CFFTX_TXFFIENA_BIT);
	}
	else
	{
		endPosition = currentMessage->bufferPosition + I2C_FIFO_LENGTH;
	}

	for(; currentMessage->bufferPosition < endPosition; currentMessage->bufferPosition++)
	{
		I2C_putData(halHandle->i2cAHandle, (uint16_t)(*(long*)(currentMessage->localPointer) >> (currentMessage->bufferPosition * 8) & 0x00FF));
	}

	I2C_clearTxFifoInt(halHandle->i2cAHandle);

	// Enable future I2C (PIE Group 8) interrupts
	PIE_clearInt(halHandle->pieHandle, PIE_GroupNumber_8);
}

 

 

Thank you!

- Brian

 

  • Brian,

    Can you clarify what you mean by "the CPU hangs when attempting to write the data bytes for the second write"? An oscilloscope capture of the transfer would also be nice.

    Thanks,
  • Hey Adam,

    Thanks for the follow-up.

    Specifically, I never break out of the while loop in i2c.c line 57 (transferring the data bytes for the message). Both the XRDY and ARDY registers resolve to zero indefinitely.

    Will work on grabbing a scope capture for you.

    Thanks again,
    - Brian
  • Hey Adam,

    Finally able to get a clean capture of the scope. I've annotated it to the best of my abilities for clarity, but please let me know if there's more info I can provide.

    Important: in my original post, I had a few errors in my code. The EEPROM I'm using actually requires the block address to be sent via the control byte, and I've enabled the second ISR to eliminate the "hanging" effect mentioned in my initial reply. I've updated the code in the original post to reflect all the latest code.

    Order of operations in the capture below is as follows:

    1. Write a 4 byte float (-5.625) to address 0x0100.
    2. Attempt to write a second message several times, get NACKed while first write cycle completes.
    3. Write a 2 byte uint (1234) to address 0x0140.

    What I'm reading from this capture is that the lower portion of the address (0x40) is being written between the two data bytes for the uint.

    First Message (Successful)


    Second Message (Failed)


    Full Scope Capture

    Thanks again for taking the time,

    - Brian

  • Brian,

    Can you verify that the XA bit in I2CMDR is set before the writes begin? If it's not, that might explain why the address is being mangled.

  • Hey Adam,

    Switching to 10-bit addressing (XA) did not work. The SDA/SCL lines showed some activity, but nothing legible as a legitimate read or write. 10-bit addressing also does not appear to comply with the format for the EEPROM we're using (datasheet: www.mouser.com/.../21710c-76077.pdf).

    Strangely, if I change the order of the write operations (UInt followed by Float), it works just fine. I really can't think of what difference that would make, but do feel like the answer lies within there, somewhere.
  • Brian,

    This is sounding like a race condition. What are you using for the FIFO interrupt level? Have you checked the interrupt timings? (You can toggle IO pins to do that.) You might be getting an unexpected interrupt somewhere.
  • Hey Adam,

    Finally able to report back that I've at least found a workaround to my issue. I'm simply cycling (disabling/enabling) the fifos before writing.

    I'm sure I'll revisit this someday to try and find the proper fix, but this will work for now, as hacky as it is.

    Thanks again for all your help! I'll be sure and follow up here if I ever determine the root cause.