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:
- First write completes successfully (as far as I can tell)
- 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)
- 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


