ACK Polling to detect end of Write Cycle for 24C512 EEPROM:R-W/=0 does not work!

1. According to the underneath Flow Chart (extracted from Microchip DS), the end of write cycle could be detected by polling the acknowledge bit. The Flow Chart says that the R-W/ bit should be set to LL (0). And accordingly, the status code that will be generated in the TWSR Register after the reception of the ACK signal is 0x18. Unfortunately, R-W/=0 does not work; the process falls in an infinite loop. Why? When the setting of R-W/ bit is changed to LH (R-W/ = 1), the process works well! The ACK is received from the EEPROM, and the TWSR Register produces correct status code 0x40. The working codes for the given Flow Chart with R-W/=1 are:

//--- wait until data get written into EEPROM location: 
    do
    {
      TWCR = 0b10100100; //TWINT TWEA TWSTA TWSTO TWCC TWEN X TWIE : START command
      while (bitRead(TWCR, 7) != HIGH)
        ;
      TWDR = 0b10100101;   //SLA + R     //Write Mode does not work!
      TWCR = 0b10000100;
      while (bitRead(TWCR, 7) != HIGH)
        ;
    }

    while ((TWSR & 0b11111000) != 0x40);        //status code when ACK is received


Figure-1: Acknowledge polling to sense end of write cycle (extracted from Microchip DS)

2. Atmel DS on ACK Polling:

Atmel DS has not mentioned any specific value for the R-W/ bit; whereas, Microchip has mentioned that the value should be LL (Fig-1) which does not work.

Is there any rational opinion/interpretation in this regard?

The R-W/ bit defines if bytes are sent (written) to a device (in which case that device sets the ACK bit) or if bytes are read from the device in which case the master sets the ACK bit (except for the first byte, which contains the R-W/ bit).

Other than that I don't understand your problem.

If you have some code that does not work (but you think it should work according to the datasheet) you probably should post that too and not just say that something does not work.

You're overthinking it.

bool i2cReady(uint8_t adr){
uint32_t timeout=millis();
bool ready=false;
while((millis()-timeout<100)&&(!ready)){
 Wire.beginTransmission(adr);
 int err=Wire.endTransmission();
 ready=(err==0);
 }
return ready;
}

Chuck

@chucktodd

1. As a learner, I simply wanted to translate label-by-label the tasks, contained in the Flow Chart of Fig-1 of the OP, in terms of register level instructions. While testing the codes (shown in OP) in a real situation of the following setup (Fig-1), I discovered that the program segment does not work with R-W/ bit set to LL (Write Mode) as prescribed in the Flow Chart of OP; but, it works with R-W/ bit set to LH (Read Mode). I decided to post it in the Forum to hear others' opinions to resolve this apparent contradiction!


Figure-1: Connection diagram between Arduino UNO and 24C512 EEPROM using TWI Bus

2. For one byte write operation, we could comfortably insert delay(5) to complete the 'write cycle'. When we are going to make a page (128 byte) write operation, should we insert delay(5*128) or let the system take as much time as required to complete the 'write cycle'? This is the situation where ACK Polling is a desirable option to detect the end of 'write cycle'.

3. Interestingly, the following Wire Library based instructions (agrees with Flow Chart, Fig-1 of OP) work to sense the end of 'write cycle'.

do
{
   Wire.beginTransmission(deviceAddress);   //START command and deviceAddress queued
   Wire.write(0x00);                         //dummy data and direction mode (R-W/=0) bit queued
   byte stscode = Wire.endTransmission();   //Transmission of queued data and STOP command
}
while (stscode != 0x00);                  //Transmission success status code as per Wire Library

4.

Wire.beginTransmission(adr);
int err=Wire.endTransmission();

Wire.endTransmission() method transmits queued Control Byte (deviceAddress + W/) and then asserts STOP command. In the quoted instructions, Wire.beginTransmission(adr) contains 7-bit (deviceAddress) in the argument; from where the missing bit (W/) is coming to have a place at the end of deviceAddress for the formation of an 8-bit control byte?

We have tested your program codes, and they have worked well (program file is attached as a reference). Your program segment contains a 'timeout' feature based on millis(), which has made it very attractive. Thanks a lot for the contribution.

multiByteWrite.ino (1.9 KB)

Try to print TWSR via Serial.print when it is in the infinite loop. For example you may read the register and print it each time it changes. I guess you have some typo in your code.

GolamMostafa:
@chucktodd

1. As a learner, I simply wanted to translate label-by-label the tasks, contained in the Flow Chart of Fig-1 of the OP, in terms of register level instructions. While testing the codes (shown in OP) in a real situation of the following setup (Fig-1), I discovered that the program segment does not work with R-W/ bit set to LL (Write Mode) as prescribed in the Flow Chart of OP; but, it works with R-W/ bit set to LH (Read Mode). I decided to post it in the Forum to hear others' opinions to resolve this apparent contradiction!

2. For one byte write operation, we could comfortably insert delay(5) to complete the 'write cycle'. When we are going to make a page (128 byte) write operation, should we insert delay(5*128) or let the system take as much time as required to complete the 'write cycle'? This is the situation where ACK Polling is a desirable option to detect the end of 'write cycle'.

3. Interestingly, the following Wire Library based instructions (agrees with Flow Chart, Fig-1 of OP) work to sense the end of 'write cycle'.

do

{
  Wire.beginTransmission(deviceAddress);   //START command and deviceAddress queued
  Wire.write(0x00);                         //dummy data and direction mode (R-W/=0) bit queued
  byte stscode = Wire.endTransmission();   //Transmission of queued data and STOP command
}
while (stscode != 0x00);                  //Transmission success status code as per Wire Library




**4.** 
Wire.endTransmission() method transmits queued Control Byte (deviceAddress + W/) and then asserts STOP command. In the quoted instructions, Wire.beginTransmission(adr) contains 7-bit (deviceAddress) in the argument; from where the missing bit (W/) is coming to have a place at the end of deviceAddress for the formation of an 8-bit control byte?

We have tested your program codes, and they have worked well (program file is attached as a reference). Your program segment contains a 'timeout' feature based on millis(), which has made it very attractive. Thanks a lot for the contribution.

#4, the Wire.h library takes the Address you provide, Left shifts it one bit, appends the R/W bit. Then hands that byte to the TwoWire hardware. The TwoWire hardware create a START signal on the Bus, then clocks out the 'control' byte (leftShifted address + r/w).

#3, your example would send 2 bytes the first one a 'control' byte (leftShifted address +'w' bit) and then a second byte of 8 zero bits. The 'dummy' byte is an error. If the Device was ready, it would accept that byte of zero.

#2, no, Most EEPROM devices write a 'page' of data at a time, the 24C512 has a 256 byte page size. When you send a write command:

Wire.beginTransmission(0x51);
Wire.write((uint8_t)highByte(address));
Wire.write((uint8_t)lowByte(address));
Wire.write((uint8_t)1);
Wire.write((uint8_t)2);
Wire.write((uint8_t)4);
Wire.write((uint8_t)5);
Wire.write((uint8_t)61);
Wire.write((uint8_t)1;
Wire.write((uint8_t)61);
Wire.write((uint8_t)1);
Wire.write((uint8_t)61);
Wire.write((uint8_t)1);
Wire.write((uint8_t)61);
Wire.write((uint8_t)1);
Wire.endTransmission();

The chip sees the write command, takes the first two bytes from the I2C bus, then uses that address to copy the current content of the addressed page into a temporary write buffer. Then it overwrites matching bytes in that temporary write buffer with the data it receives from the I2C bus. after the transmission is completed, it starts the program cycle:
First it erases the 'page' all 256 bytes in it's eeprom array,
Then it programs each byte to match the content of the temporary write buffer.
This sequence can take a maximum of 5ms. But is usually shorter.

Any single Write transmission will take at most 5ms.

Chuck.

Have a look at Peter Fleury I2C library based on 8-bit AVR.

Specifically, on the routine
i2c_start_wait();
where he uses ack polling.

@chucktodd

First of all, many many thanks for taking time to explain the matter in a simple way.

Secondly, I have taken the following summary from your post.
a. Wire.transmission(deviceaddress) always appends Write bit (LL) at the end of 7-bit deviceAddress.

(If so, I can say that Wire.requestFrom(deviceaddress, n) also appends Read bit (LH) at the end of the deviceaddress.)

b. A page write in 24C512 EEPROM takes in total 5 ms time.

BTW: From the following excerpt of Microchip DS, I perceived the notion of 128-byte page size.

6.2 Page Write
The write control byte, word address and the first data
byte are transmitted to the 24XX512 in the same way
as in a byte write. But instead of generating a Stop
condition, the master transmits up to 127 additional
bytes, which are temporarily stored in the on-chip page
buffer and will be written into memory after the master
has transmitted a Stop condition. After receipt of each
word, the seven lower Address Pointer bits are internally
incremented by one. If the master should transmit
more than 128 bytes prior to generating the Stop condition,
the address counter will roll over and the previously
received data will be overwritten. As with the byte
write operation, once the Stop condition is received, an
internal write cycle will begin (Figure 6-2). If an attempt
is made to write to the array with the WP pin held high,
the device will acknowledge the command, but no write
cycle will occur, no data will be written and the device
will immediately accept a new command

GolamMostafa:
@chucktodd

First of all, many many thanks for taking time to explain the matter in a simple way.

BTW: From the following excerpt of Microchip DS, I perceived the notion of 128-byte page size.

You're welcome.

a: correct
b: maximum. It can take less. I use ack polling to increase my app preformance.
My normal sequence of I2C calls is something like this:

if(i2cReady(address)){ // ready will hang until device is available or timeout occures
  // do I2C transaction
  }
else { // handle Device / Bus Failure.
  // report error
  // reset devices
  // reset I2C Hub
  // poll each hub segment to find failed device or segment
  //   if failing segment found, isolate it.
}

by checking before, not After, I can hopefully be doing something else while the I2C eeProm is executing its' program cycle. If I happen to re-access during its' program cycle, then I have to wait. Else it operates in parallel.

You are correct about the 128byte Write Page. The Write Page Size is specific to each chip and manufacturer.

I can't remember which device I used that had a 256 byte buffer, it exists. looking at my documentation file Both the Microchip 24LC512 and 24LC1026 use 128byte page buffers. The 24LC32 uses a 32byte Write Page buffer.

Chuck.

@chucktodd

bool i2cReady(uint8_t adr){
uint32_t timeout=millis();
bool ready=false;
while((millis()-timeout<100)&&(!ready)){
 Wire.beginTransmission(adr);
 int err=Wire.endTransmission();
 ready=(err==0);
 }
return ready;
}

Referring to the above codes, we are willing to go with you for a discussion on why have you written this shorthand ready=(err==0); instead of this shorthand ready=(err==0) ? 1 : 0; for the implied if-else structure? I recognize that the experienced people will not get confused with the format you have stated.

But, I am a bit worry that the beginners might get confused with this cryptic code. Because expression 2 and expression 3 are not explicitly mentioned in the statement, a novice might expand it as ready=(err==0)? 0 : 1;. We would be glad to hear your comments.

Thanks.

GolamMostafa:
@chucktodd

bool i2cReady(uint8_t adr){

uint32_t timeout=millis();
bool ready=false;
while((millis()-timeout<100)&&(!ready)){
Wire.beginTransmission(adr);
int err=Wire.endTransmission();
ready=(err==0);
}
return ready;
}




Referring to the above codes, we are willing to go with you for a discussion on why have you written this shorthand **ready=(err==0);** instead of this shorthand **ready=(err==0) ? 1 : 0;** for the implied **if-else** structure? I recognize that the experienced people will not get confused with the format you have stated. 

But, I am a bit worry that the beginners might get confused with this cryptic code. Because expression 2 and expression 3 are not explicitly mentioned in the statement, a novice might expand it as **ready=(err==0)? 0 : 1;**. We would be glad to hear your comments.

Thanks.

The reason I chose to write it that was, is idiosyncratic. Given:

int err=0;

bool ready =(err==0);

to me, I read this as: ready is true when err equals 0. The trinary statement of:

int err=0;

bool ready = (err==0)?1:0;

requires more thought:
ready is equal to 1 if err is equal to 0; or ready is equal to 0 if err is not equal to 0. when ready is 1 is it true or false? I don't care if ready is equal to 1, I want to know if ready is true.

Given the numeric equivalents of true and false as defined. false is equal to zero, true is not false.

So, what is the numeric value of true? There is not a single numeric value of 'true' the only defined value is of false. and true is relative to false. So:

bool ready = false

int i = 7;

i = i + (byte) ready;  // should evaluate to 7
i = i * (byte) ready; // should evaluate to zero

ready = true;

i = i + (byte) ready; // could evaluate to 8 or any integer value not equal to 7.
//
i=7;
if(i) {
  Serial.print(" True is equal to 7");
}
else {
  Serial.print(" True is Not equal to 7");
}

Which statement prints? If the first prints, is that proof that true is 7 or only that 7 is true.

I have never liked the fact that 'c' evaluates non zero value as true.

Chuck.

It is how I think.

int err=0;

bool ready =(err==0);

to me, I read this as: ready is true when err equals 0.

That's what I also read when I tested your given codes. Later on, I read few textbooks on Ternary Operator, and I came to know that the cryptic ternary operations may sometimes become confusing; but, the book has not discussed well how the operation could be confusing. It is out of curiosity that I wanted to know more from you, which you have provided with example and in a sincere way.

Thanks a lot.

GolamMostafa:
Thanks a lot.

You are welcome, honest question should always receive a respectful answer.

Have fun coding!

Chuck.