I am using I2C Module 0 (UCB0) to communicate with two slaves - EEPROM(24LC64) and a temperature sensor(Si7060). I2C Write and Reads from the temperature sensor are being done continuously. Occasional I2C reads are failing as they do not respond in time, but it is okay by me as the next reads work.
But After 10-15 min(one in 10000 or more reads), the UCBBUSY bit gets set. The SDA line is continuously held low. My code is waiting in a while loop for the bit to get cleared which is not happening and thus the code gets stuck. Writes are working fine. I have used the same code which was being used for I2C Module 1(UCB1) which is having no problem.
(1) Why is this happening? I have observed on a CRO that the CLK signal is looking like a sine wave. Is the insufficient hold time leading to the slave pulling the data line low to signal an ACK and never releasing it? In this case changing I2C clock frequency solve the issue, right? Current clock frequency is about 133KHz(16Mhz/120)
(2)When the UCBBUSY is getting set I tried resetting the I2C Module by doing
UCB0CTL1 = UCSWRST;
__delay_cycles(1000);
UCB0CTL1 &= ~UCSWRST;
Though this reset the module and the SDA line back to the high voltage level, this is leading to unsuccessful I2C Reads continuously in certain cases. Is what I have attempted to correct? If not, how else can I solve this problem?
We are in a critical stage during production. Any help would be dearly appreciated. Please find the code attached.
Thanks
Abhishek
uint08 i2cUcb0ByteRead(uint08 device_addr, uint08* write_data, uint08 num_bytes_write, uint08* read_data, uint08* bytes_written, uint08* bytes_read, uint08 timeout) { /* * function to do random reads from EEPROM, Temperature Sensor, Digital Audio Amp * It involves first sending a write request followed by a read * */ uint08 status; uint08* dataPtr = write_data; uint08 i; uint16 currentSr; uint08 intDisabled = 0; // check for argument validity if ( timeout > 500 ) //65, increased for testing return I2C_INVALID_TIMEOUT; // since this version is polled, we don't want an interrupt firing // and the ISR executing currentSr = __get_SR_register(); if ( currentSr & GIE ) { intDisabled = 1; __disable_interrupt(); } // turn on the timer //TA1CTL |= MC_2; i2c0BaselineTimer = TA0R; // write the slave address and configure i2c hardware UCB0CTL1 = UCSWRST; UCB0CTL1 = UCSSEL_2 + UCSWRST; UCB0I2CSA = device_addr>>1; UCB0CTL1 &= ~UCSWRST; // enable module interrupts and start the transfer UCB0IE |= UCNACKIE; UCB0IE |= UCTXIE; //IE2 |= UCB1TXIE; UCB0CTL1 |= UCTR + UCTXSTT; //ideally, it should be done before clearing UCSWRST if(1) //device_addr == SI7060_SLAV_ADDR { __delay_cycles(2000); //delay increased for testing } //sendUserMessage("Read function, ST sent\n\r"); *bytes_written = 0; //check for NAK? status = i2cUcb0CheckNakTimeout(0,1); if(status != 0) { i2cUcb0Cleanup(); //sendUserMessage("NAK after sending ST\n\r"); if (intDisabled) __enable_interrupt(); return status; } //Write Address High and Address Low Bytes for (i = 0; i < num_bytes_write; i++) { //can count down // spin until transmit buffer needs to be reloaded while ((UCB0IFG & UCTXIFG) == 0) //is this checking for ACK? { status = i2cUcb0CheckNakTimeout(timeout, 1); if (status != 0) { i2cUcb0Cleanup(); if (intDisabled) __enable_interrupt(); return status; } } //UCB1IV &=~USCI_I2C_UCTXIFG; // load another data byte UCB0TXBUF = *dataPtr; // increment actual byte counter *bytes_written = *bytes_written + 1; // increment the pointer address dataPtr++; // take a new baseline timer value i2cBaselineTimer = TA0R; //sendUserMessage("wrote to txbuf\n\r"); } //Check for ACK ? while ((UCB0IFG & UCTXIFG) == 0) { status = i2cUcb0CheckNakTimeout(timeout, 1); if ( status != 0 ) { //sendUserMessage("NAK after writing to txbuf\n\r"); i2cUcb0Cleanup(); if ( intDisabled ) __enable_interrupt(); return status; } } //Send START followed by ControlByte for Receiver Mode UCB0CTL1 &= ~(UCTR); //Clear the last bit -- is this required? UCB0I2CSA = device_addr>>1; UCB0CTL1 |= UCTXSTT; //Check if indeed receiver Mode //No. of bytes to be read is 1 //check NACK? status = i2cUcb0CheckNakTimeout(0,1); if (status != 0) { //sendUserMessage("NAK after sending repeated start\n\r"); i2cUcb0Cleanup(); if (intDisabled) __enable_interrupt(); return status; } // spin until start condition cleared while (UCB0CTL1 & UCTXSTT) { status = i2cUcb0CheckNakTimeout(timeout, 1); if (status != 0) { i2cUcb0Cleanup(); if (intDisabled) __enable_interrupt(); return status; } } //wait for data to be received while ((UCB0IFG & UCRXIFG) == 0) { status = i2cUcb0CheckNakTimeout(timeout, 1); if (status != 0) { i2cUcb0Cleanup(); if (intDisabled) __enable_interrupt(); return status; } } UCB0CTL1 |= UCTXNACK; //send NACK after receiving a byte UCB0CTL1 |= UCTXSTP; //finally send STOP *read_data = UCB0RXBUF; *bytes_read = 1; // turn off the timer to save power while (UCB0STAT & UCBBUSY){}; //TA1CTL &= ~MC_2; UCB0IFG &= ~UCTXIFG; //to cleasr TXIFG UCB0IE &= ~UCNACKIE; UCB0IE &= ~UCRXIE; //UCB0I2CIE &= ~UCNACKIE; //IE2 &= ~UCB0RXIE; //sendUserMessage("Read successful"); // no errors, all bytes received if (intDisabled) __enable_interrupt(); return 0; } uint08 i2cUcb0CheckNakTimeout(uint08 timeout, uint08 check_nak) / * return 0 - Completed successfully * 1 - Slave NAck'ed * 2 - Slave did not respond before timeout period expired * */ { uint16 currentTimerVal; uint16 timerDiff; // check for NAK if ( check_nak ) { if ( UCB0IFG & UCNACKIFG ) { // send a stop and clear the interrupt flag UCB0CTL1 |= UCTXSTP; while (UCB0STAT & UCBBUSY){}; //UCB1IV &= ~USCI_I2C_UCNACKIFG; UCB0IFG &= ~UCNACKIFG; //TA1CTL &= ~MC_2; UCB0CTL1 = UCSWRST; return I2C_NO_ACK; } } // do we need to check for timeout? if ( timeout != 0 ) { currentTimerVal = TA0R; // check for TA1R rollover since we baselined if ( currentTimerVal > i2c0BaselineTimer ) timerDiff = currentTimerVal - i2c0BaselineTimer; else timerDiff = (65535 - i2c0BaselineTimer) + currentTimerVal; // Current TimerA0 frequency is 32768Hz (ACLK with ID_0) // A timer count of 33 corresponds to 1ms if((timerDiff >>10) >= (timeout)) { // generate a stop and return the error flag //TA1CTL &= ~MC_2; UCB0CTL1 |= UCTXSTP; //while(UCB0STAT & UCBBUSY); OLD CODE //My Change to solve the issue if(UCB0STAT & UCBBUSY) { testBusy++; UCB0CTL1 = UCSWRST; __delay_cycles(1000); UCB0CTL1 &= ~UCSWRST; } //while (UCB0STAT & UCBBUSY){}; return I2C_TIMEOUT; } } // nak or timeout did not occur return 0; } void i2cUcb0Cleanup(void) /** * Cleanup after a failed transaction. Turn off the timer, disable interrupts, * and put the I2C hardware into reset. * */ { // turn off the timer to save power //TA1CTL &= ~MC_2; // clear interrupt flags UCB0IFG &= ~UCTXIFG; //IFG2 &= ~UCB0TXIFG; // no errors, all bytes transmitted UCB0IE &= ~UCNACKIE; UCB0IE &= ~UCTXIE; //UCB0I2CIE &= ~UCNACKIE; //IE2 &= ~UCB0TXIE; // put i2c hardware into reset UCB0CTL1 = UCSWRST; }