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 interrupt problem

Other Parts Discussed in Thread: MOTORWARE

I have written a simple I2C slave interface for the F28027F and it seems to be working as expected, but there is one remaining problem.  When I add the I2C stuff into motorware lab1 it runs for a while then hangs (seems to be holding scl low).  This locks up the bus until there is a reset.  When I reduce the overhead of mainISR the problem goes away.  

This is is what I have found causes the problem:

I get the I2C_IntSrc_Slave_Addr (7) interrupt, if it is a read command I also immediately get the I2C_IntSrc_Tx_Rdy (5).  Normally this isn't a problem. If mainISR is executing at this time it seems that one of the interrupts is missed and the I2C module locks up.  I have verified this behavior by timing the ISRs with a logic analyzer.

I am not using FIFOs, which I imagine could mitigate the problem.  I use the I2C_IntSrc_Slave_Addr interrupt to setup to clear some array indices and set I2CDXR to the first value (if I do this on the I2C_IntSrc_Tx_Rdy interrupt the first byte sent uses the previous value of I2CDXR), I could probably do this on a stop interrupt just as well.  I could also implement some sort of timeout check as well. I mainly just want to understand what the problem is in this case.

Init Code:

void HAL_setupI2C(HAL_Handle handle)	
{
	HAL_Obj *obj = (HAL_Obj *)handle;
	//I2C_Obj *I2CARegs=(I2C_Obj *)obj->I2CaHandle;
	   // Initialize I2C
	/*   I2CARegs->I2CSAR = 0x0050;        // Slave address - EEPROM control code
	   I2CARegs->I2COAR =1;

	   // I2CCLK = SYSCLK/(I2CPSC+1)

	   I2CARegs->I2CPSC = 6;       // Prescaler - need 7-12 Mhz on module clk
	   I2CARegs->I2CCLKL = 20;           // NOTE: must be non zero
	   I2CARegs->I2CCLKH = 20;            // NOTE: must be non zero
	   I2CARegs->I2CIER = (1<<6)|(1<<4)|(1<<3);      // Enable SCD & ARDY interrupts

	   I2CARegs->I2CMDR = 0x0020;    // Take I2C out of reset*/
	                                    // Stop I2C when suspended
	   //PIE_enableInt(PIE_Handle pieHandle, const PIE_GroupNumber_e group, const PIE_InterruptSource_e intSource)*/

	I2C_setSlaveAddress(obj->I2CaHandle, 0x0001);
	I2C_setupClock(obj->I2CaHandle, 6, 20,20);
	I2C_enableInt(obj->I2CaHandle,I2C_IntEn_Rx_Rdy|I2C_IntEn_Tx_Rdy|I2C_IntEn_Slave_Addr);
	I2C_setSlave(obj->I2CaHandle);
	I2C_enable(obj->I2CaHandle);

}

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

	  PIE_enableInt(obj->pieHandle,PIE_GroupNumber_8,PIE_InterruptSource_I2CA1 );
	  CPU_enableInt(obj->cpuHandle,CPU_IntNumber_8);
}

Isr code:

interrupt void i2c_int1a_isr(void)     // I2C-A
{

   // Read interrupt source
	halHandle->gpioHandle->AIOTOGGLE=(1<<2);
	int IntSource;
   IntSource = (int)I2C_getIntSource(halHandle->I2CaHandle);

   //GPIO_toggle(myGpio, GPIO_Number_1);
   // Interrupt source = stop condition detected
   /*if(IntSource == I2C_SCD_ISRC)
   {

   }  // end of stop condition detected
	*/


	if(IntSource==I2C_IntSrc_Slave_Addr)
	   {
		   RxPointer=0;
		   TxPointer=0;
		   //GPIO_toggle(myGpio, GPIO_Number_2);
		   //I2caRegs.I2CDXR=StatusPacket[0];
	   }
	if(IntSource==I2C_IntSrc_Rx_Rdy)
	   {

		   if(RxPointer<sizeof(RxPacket))
			   RxPacket[RxPointer]=I2C_getData(halHandle->I2CaHandle);
		   if(RxPointer==1)
			   RxLength=RxPacket[RxPointer];
		   if(RxPointer==(RxLength+3))
		   {
			   memcpy((void*)DecodeBuffer,(void*)RxPacket,sizeof(DecodeBuffer));
			   DecodePacket=1;
		   }
		   RxPointer++;

	   }
	if(IntSource==I2C_IntSrc_Tx_Rdy)
	   {
		   if(TxPointer<(sizeof(StatusPacket)-1))
				   TxPointer++;
		   /*if(TxPointer==0)
			   GPIO_setLow(myGpio, GPIO_Number_3);
		   else
			   GPIO_setHigh(myGpio,GPIO_Number_3);*/
		   I2C_putData(halHandle->I2CaHandle,StatusPacket[TxPointer]);
	   }


   // Enable future I2C (PIE Group 8) interrupts
   //PieCtrlRegs.PIEACK.all = PIEACK_GROUP8;
	PIE_clearInt(halHandle->pieHandle,PIE_GroupNumber_8);
	//halHandle->gpioHandle->AIOCLEAR=(1<<2);
}

  • Here's what I think is happening:


    1. The AAS interrupt comes in while the CPU is in the main ISR.

    2. A TX request is triggered. This would normally cause an interrupt, but AAS has already set the PIEIFR flag.

    3. The CPU enters the I2C ISR. It reads I2CISRC and sees the AAS code.

    4. The CPU does the work for AAS, then exits the ISR. Since the TX request is still active, no further interrupts are triggered.

    5. The I2C module holds the SCL line low while it waits for the CPU to provide data. This never happens, so the bus hangs.


    To fix this, add a do-while loop in your ISR that reads and processes I2CISRC repeatedly until it reads zero.

  • I was thinking exactly this.  I tried reading I2CISRC in a while loop, but the problem still persists.

  • You're only calling PIE_clearInt() at the end of the ISR, right? Not once per I2CISRC loop?

    Try checking the I2CSTR and I2CMDR registers after the fail. Also look at the PIEIERx, PIEIFRx, PIEACK, IER, IFR, and ST1.INTM values to see if the interrupt got stuck anywhere in the propagation path. Please post what you find and I'll take a look.

    Thanks,
  • Correct, I'm only calling PIE_clearint() once per loop

    Register contents when stuck are:

    I2CSTR=0x5230
    I2CMDR=0x0020
    PIEIER8=0x0001
    PIEIER10=0x0001
    PIEIFR10=0x0003
    PIEIFR1=0x0003
    PIEACK=0x0200
    STM1.INTM=1
  • The main thing I see is that interrupts are globally disabled (INTM = 1). Were you inside an ISR when you read the registers? If so, can you read them outside of any ISR after the fail? The ones I need are PIEIER8, PIEIFR8, the CPU IER, the CPU IFR, and ST1.INTM.

    If you were outside of all ISRs, you're probably disabling interrupts somewhere and not re-enabling them.

    Your I2C appears to be in slave transmitter mode with a busy bus, as expected. Strangely, XSMT has not gone high yet.
  • I was not in the ISR when I read those values.  I paused the processor and looked at the registers.

    I'm fairly certain the ISRs continued to work properly when I resumed.

    I will hopefully get to test more tonight.