??? 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. } //***************************************************************** |
Topic | Author | Date |
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 |