Email: Password: Remember Me | Create Account (Free)

Back to Subject List

Old thread has been locked -- no new posts accepted in this thread
???
01/15/06 16:19
Read: times


 
#107431 - Problem Solved.
Responding to: ???'s previous message
Dear Forum Members,

The problem with i2c bus getting stuck has been solved. The problem was with clearing SI flag. In my earlier routines i used following.

1. Enable I2C by setting I2EN bit
2. Generate start condition by setting STA bit.
3. Wait for SI to generate.
4. Clear SI.
5. Load Slave addres + read/write in I2DAT.
6. Wait for SI
7. Clear SI.
.....................................
.....................................

The problem lies in clearing SI before loading I2DAT. I2C hardware inside micro sets SI bit once it as done one I2C task and software should clear it. While SI is set I2C bus is idle no clock osicllations. Once SI is cleared the action starts. The correct way is dont clear SI untill all registers such as AA flag & I2DAT registers are loaded. clearing it before leads to problems. I have written fresh routines for Hardware I2C for PCF8591 ADC redading and AT24C64 eeprom write and read. These are working absolutely fine now. Here is the code.

Credit where its due : seeprom routines i have converted Peter Daneegers software bit bang routines to hard i2c with minor changes.

/* I2C Status Message Codes (Master mode) */
#define	START_TXD			0x08	/* Start condition for bus transmited */
#define	REPEAT_START_TXD	0x10	/* Repeated Start condition for bus transmited */
#define	ADDRESS_TXD_ACK		0x18	/* Address plus write sent, ack received */
#define	ADDRESS_TXD_NOACK	0x20	/* Address plus write sent, NO ack received! */
#define	DATA_TXD_ACK		0x28	/* Data sent, ack received */
#define	DATA_TXD_NOACK		0x30	/* Data sent, NO ack received! */
#define	ARB_LOST			0x38	/* I2C Master Arbitration Lost */
#define	ADDRESS_RXD_ACK		0x40	/* Address plus read sent, ack received */
#define	ADDRESS_RXD_NOACK	0x48	/* Address plus read sent, NO ack received! */
#define	DATA_RXD_ACK		0x50	/* Data received, ack sent */
#define	DATA_RXD_NOACK		0x58	/* Data received, NO ack sent! */

#define I2B_I2EN 	0x40
#define I2B_STA		0x20
#define I2B_STO		0x10
#define I2B_SI		0x08
#define I2B_AA		0x04

#define I2BX_I2EN	0xBF
#define I2BX_STA	0xDF
#define I2BX_STO	0xEF
#define I2BX_SI		0xF7
#define I2BX_AA		0xFB

#define WRITEPAGE 3

bit hi2c_in(bit, uchar *);
bit hi2c_out(uchar);
bit hi2c_stop(void);
bit hi2c_start(void);

bit read_pcf8591(uchar *, uchar);
bit Seepromwrite( uint adr, uchar *s, uchar n );
bit Seepromread( uint adr, uchar *s, uchar n );

// Generates start condition on i2c bus returns 1 if error else returns 0.
bit hi2c_start(void)
{
	I2CON=0x00;							// I2C speed set using I2SCL & I2SCH
	I2CON|=I2B_I2EN;					// Enable I2C Bus.
	I2CON|=I2B_STA;						// Send Start condition.
	while (!(I2CON & I2B_SI) );			// Wait for si.

	if ( (I2STA!= START_TXD) && (I2STA!=REPEAT_START_TXD))  // If Status not as expected.
	{
		I2CON&=I2BX_STA;				// Clear start bit;
		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// return error.
	}

	I2CON&=I2BX_STA;					// Clear start bit;
	return 0;
}

// Generates stop condition returns 1 if error else 0.
bit hi2c_stop(void)
{
	I2CON|=I2B_STO;						// Send stop stop condition.
	I2CON&=I2BX_SI;						// Clear SI.
	while ( I2CON & I2B_STO );			// Wait till hardware clears stop bit.
	I2CON&=I2BX_I2EN;					// Disable I2C Bus.
	return 0;
}

// Reads byte from i2c bus parameters ack to send pointer to uchar to store data.
// Returns 1 if error in operation.
bit hi2c_in(bit ack, uchar *i2rxdata)
{
	if ( ack )							// set auto acknowledge flag if ack is to be sent.
		I2CON|= I2B_AA;
	else
		I2CON&= I2BX_AA;

	I2CON&=I2BX_SI;						// Clear SI.
	while (!( I2CON & I2B_SI ) );		// Wait for SI.

	if ((I2STA!= DATA_RXD_ACK) && (I2STA!= DATA_RXD_NOACK)) // If status not as expected.
	{
		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// Return error.
	}
	*i2rxdata=I2DAT;					// Store received data.
	return 0;							// Return no error.
}

// Transmits a byte on i2c bus parameter byte to send.
// Returns 1 if error occured.
bit hi2c_out(uchar i2txdata)
{
	I2DAT=i2txdata;						// Put data to transmit in register.
	I2CON&=I2BX_SI;						// Clear SI.
	while ( ! ( I2CON & I2B_SI ) );		// Wait for SI.

	if ((I2STA!=DATA_TXD_ACK) && (I2STA!=ADDRESS_TXD_ACK) && (I2STA!=ADDRESS_RXD_ACK)) // If status not as expected.
	{

		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// Return error.
	}
	return 0;							// Return no error.
}

// Reads a value from ADC PCF8591 Parameters pointer to uchar to store result and channel no to read.
// Returns 1 if error occured in operation.
bit read_pcf8591( uchar *adrdbuff, uchar adch)
{
	if(hi2c_start())					// Send start condition.
		return 1;
	if(hi2c_out(0x90))					// Send device address + write.
		return 1;
    if(hi2c_out(adch))					// send channel number to read.
		return 1;
	hi2c_stop();						// Send stop condition.

	if(hi2c_start())					// send start condition.
		return 1;
	if(hi2c_out(0x91))					// Send device address + Read.
		return 1;
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(0, adrdbuff))			// Final Read data from ADC send nack.
		return 1;				
	hi2c_stop();						// Send stop condition.

	return 0;							// Return no error.
}

/////////////// Modified to support AT24C64 ////////////////////
bit Seepromadr( uchar adrh, uchar adrl, bit rd )
{
  	uchar devadr=0xa2;				//I2C Bus Address 0.
	devadr |= rd;					//Or the RD/WR bit.

	hi2c_start();					//Send a start codition.
	if( hi2c_out( devadr ))			//Send the device address.
		return 1;
	if(!rd)							//If Write Operation.
	{
		if( hi2c_out( adrh ))		//Send the address high byte.
    		return 1;				
  	  	return hi2c_out( adrl );	//Send the address low byte.
	}
	return 0;						// Return no error.
}

bit Seepromwrite( uint adr, uchar *s, uchar n )
{
	uchar i;
	do
	{
		// Acknowledge pooling for eeprom.
	    for( i = 255;; )
		{
      		if( 0 == Seepromadr( adr>>8, adr, 0) )
				break;				// receive ACK = write finished
      		hi2c_stop();
		    if( 0 == --i )			// try it 25.5msec
				return 1;			// Timeout
    	}

    	if( 0 == n )
      		return hi2c_stop();		// all bytes written

	    i = adr & ((1 << WRITEPAGE) - 1);   // prepare write boundary check
    	do
		{
      		if( hi2c_out( *s ))
			{
				hi2c_stop();
				return 1;			// Write error
		    }
		    s++;
		    adr++;
	    }while( --n && ++i < (1 << WRITEPAGE));
    	hi2c_stop();
	}while(1);
}

bit Seepromread( uint adr, uchar *s, uchar n )
{
  	if( Seepromadr( adr>>8, adr, 0))
    	return 1;							// EEPROM not connected
  	do
	{
    	if( Seepromadr( adr>>8, 0, 1))		// repeat start for read
      		return 1;

    	for(;;)
		{
	     	bit ack = --n && ++adr & 0xff;	// last byte or last in page
		  	if( hi2c_in(ack, s) )
				return 1;
		  

		    s++;
    		if( !ack )
				break;
	    }
	}while( n );
	hi2c_stop();							// send stop condition.
  	return 0;								// return no error.
}
//*****************************************************************




List of 15 messages in thread
TopicAuthorDate
I2C routine getting stuck.            01/01/70 00:00      
   you can get working code by using            01/01/70 00:00      
      Re: Getting code working            01/01/70 00:00      
         I took aquick look at the I2con use and            01/01/70 00:00      
            Re: Why not interrupt.            01/01/70 00:00      
   24MHz without four layer board?            01/01/70 00:00      
      Re: 24MHz            01/01/70 00:00      
      What max freq can be used with two layer            01/01/70 00:00      
         who knows            01/01/70 00:00      
   I2C routine getting stuck.            01/01/70 00:00      
      Working very well with C668 @ 94khz bus            01/01/70 00:00      
   Bit Bangin Works fine.            01/01/70 00:00      
   Problem Solved.            01/01/70 00:00      
      maybe worth putting it into code library            01/01/70 00:00      
         Uploaded into code library.            01/01/70 00:00      

Back to Subject List