View previous topic :: View next topic |
Author |
Message |
valemike Guest
|
i2c_start(); i2c_write(ADDRESS) no ack! |
Posted: Thu May 12, 2005 2:53 pm |
|
|
99.99% of the time, when I do an i2c_start(); followed by an i2c_write();, then there is an 'ack' returned by the slave.
Once in a great while, I don't get the ACK. The ACK is generated in hardware automatically, right? Thus there is no software intervention by the i2c slave PIC to send an 'ack' on the 9th bit following an address match.
I only have two devices on the i2c bus - one master PIC and one slave PIC.
What would cause this ACK from the slave to master to fail once in a rare while? A delayed slave isr perhaps??
i2c_start();
ack = i2c_write(address); |
|
![](templates/subSilver/images/spacer.gif) |
Humberto
Joined: 08 Sep 2003 Posts: 1215 Location: Buenos Aires, La Reina del Plata
|
|
Posted: Fri May 13, 2005 12:26 pm |
|
|
Quote: |
The ACK is generated in hardware automatically, right? Thus there is no software intervention by the i2c slave PIC to send an 'ack' on the 9th bit following an address match.
|
Assuming this you are right but, your i2c_write() handler (Master side)
normally doesn´t check this condition.
Quote: |
I only have two devices on the i2c bus - one master PIC and one slave PIC.
|
In this case the Slave may be servicing an interrupt (just before sending
the ACK) that insert an unexpected delay which hold off the master in the
middle of a transaction using what's called a clock stretching (the slave
keeps SCL pulled low until it's ready to continue). Most I2C slave devices
don't use this feature but in this case the Slave is another PIC.
What I know is that every Master should support this condition.
To workaround: add a 10-15ms delay after the i2c_stop() in the
i2c_write() routine and tell us your result.
Best wishes,
Humberto |
|
![](templates/subSilver/images/spacer.gif) |
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon May 16, 2005 2:09 pm |
|
|
I, personally, hate using delay statements in my code especially in I2C routines. It's like having the processor twiddle it's thumbs while it could be doing something productive. I like using something like this:
Code: |
if(!i2c_write(address))
{
do i2c stuff here
}
|
If the i2c_write() returns an ACK then the rest of the sequence can be done. If it returns a NOACK then you could have it loop on it until it does or until a counter times out, just so you don't get stuck there forever. I've seen devices not ACK back for whatever reason and hitting it again had it ACK the second time.
Ronald |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon May 16, 2005 8:02 pm |
|
|
Quote: | Assuming this you are right but, your i2c_write() handler (Master side)
normally doesn´t check this condition.
|
Well it should if you are a good coder! And Mike is checking:
Code: | ack = i2c_write(address); |
BTW, this is the way you should check to see if devices are ready. People usually just stick delays in there to try and make it work. That is just plain bad programming if you ask me and you are just asking for trouble in the long run. Just wait until your buyers change vendors on an eeprom and your code no longer works.
Quote: | In this case the Slave may be servicing an interrupt (just before sending
the ACK) that insert an unexpected delay which hold off the master in the
middle of a transaction using what's called a clock stretching (the slave
keeps SCL pulled low until it's ready to continue). Most I2C slave devices
don't use this feature but in this case the Slave is another PIC.
| Clock stretching doesn't have anything to do with not receiving ACKS. Don't know why you think slave devices don't use clock stretching. Well they do, they are just really fast so you probably wouldn't notice it.
Quote: | What I know is that every Master should support this condition.
To workaround: add a 10-15ms delay after the i2c_stop() in the
i2c_write() routine and tell us your result.
| Not necessary and just plain wastes time. rnielsen has the right idea.
Code: |
/* *************************************************************************
Useful defines for writing to the I2C bus
*************************************************************************** */
#define I2C_IDLE() while ((SSPCON2 & 0x1F) || (SSPSTATbits.R_W))
#define I2C_START() \
SSPCON2bits.SEN = 1; \
while (SSPCON2bits.SEN) \
{ \
_asm nop _endasm \
}
#define I2C_RESTART() \
SSPCON2bits.RSEN = 1; \
while (SSPCON2bits.RSEN) \
{ \
_asm nop _endasm \
}
#define I2C_STOP() \
SSPCON2bits.PEN = 1; \
while (SSPCON2bits.PEN) \
{ \
_asm nop _endasm \
}
#define I2C_WRITE(x) \
SSPBUF = (x); \
while (SSPSTATbits.BF) \
{ \
_asm nop _endasm \
} \
I2C_IDLE()
#define I2C_READ(x) \
SSPCON2bits.RCEN = 1; \
while (SSPCON2bits.RCEN) \
{ \
_asm nop _endasm \
} \
SSPCON2bits.ACKDT = (x); \
SSPCON2bits.ACKEN = 1; \
while (SSPCON2bits.ACKEN) \
{ \
_asm nop _endasm \
}
/* *************************************************************************
DESCRIPTION: This function checks to see if an i2c device will ACK
RETURN: TRUE if device ACKed otherwise FALSE
ALGORITHM: none
NOTES: none
*************************************************************************** */
static bool I2C_Device_Ready(
uint8_t id) /* FIX ME: add comment */
{
do
{
/* Important to clear these bits before we attempt to write */
PIR2bits.BCLIF = 0;
SSPCON1bits.WCOL = 0;
I2C_IDLE(); /* ensure module is idle */
I2C_START();
} while (PIR2bits.BCLIF);
I2C_WRITE(id);
if (!SSPCON2bits.ACKSTAT) /* test for ACK condition, if received */
return (TRUE);
return (FALSE);
}
/* *************************************************************************
DESCRIPTION: This function writes the passed data to the specified
address of the serial eeprom.
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
void I2C_Write_Byte(
uint8_t device, /* I2C address of the device that we are writing to */
uint8_t data, /* byte written to seeprom */
uint16_t address) /* SEEPROM address to write data */
{
uint8_t control;
/* MSB if address > 0xFF. Should be in bit1 */
control = device | ((uint8_t) (address >> 8) << 1);
while (!I2C_Device_Ready(control));
I2C_WRITE((uint8_t) address);
I2C_WRITE(data);
I2C_STOP();
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
|