I2CRead() returns "previous" byte

Discussion to talk about software related topics only.
Post Reply
joepasquariello
Posts: 85
Joined: Mon Apr 28, 2008 7:32 am

I2CRead() returns "previous" byte

Post by joepasquariello »

There have been a few threads about this problem, one in 2010, and another one in 2016. I'm using an AT24CS01 serial (I2C) EEPROM on MOD5213 with NNDK 2.7.5. It's one of those devices that requires a Write/Read sequence, which doesn't fit the model of NB's high-level I2C functions. I did some testing with the low-level functions and found the same problem of needing to read twice. From the MCF5213 manual description of I2DR:
In order to start the read of data, a dummy read to this register starts the read process from the slave. The NEXT read of the I2DR register contains the actual data.
Code from the I2CRead() function is shown below. I2C_DR is read before waiting on the semaphore, which would seem to be the "dummy" read to start the read, but there is no second read to get the actual data. I think this explains why *val doesn't contain the data we want. Does this sound right?
*val = I2C_DR;
if (OSSemPend(& Master_I2C_RX_Semaphore, ticks_to_wait)==OS_TIMEOUT)
{
I2CStop(); // Send stop or release bus
return I2C_TIMEOUT; // and return error
}
return MASTER_INT_STATUS; // Successfully sent data
Joe
mbrown
Posts: 61
Joined: Tue Jan 29, 2013 7:12 pm

Re: I2CRead() returns "previous" byte

Post by mbrown »

That's right. The SendBuf and ReadBuf commands are meant to be the default library functions. In those functions, all of the underlying read, write, start, restart, and stop commands are used without using the registers i2c explicitly. You'll notice in the ReadBuf command the calls roughly look like...

I2CRead(buf);
while(num > 1)
{
...
num--;
status = I2CRead(buf++);
...
}

Calling the first I2CRead is a dummy read with the following value being stored before incrementing our count of how many bytes we've written. The I2CRead command itself doesn't do the dummy read. Basically the way to think about it is that there's only one data register that acts as a buffer for I2C bus. If you write to it, it pushes that data out onto the bus. If you read from it, it tries to perform a read on the bus. The first read just starts the bus, but the data you read back is stale, it's whatever was already in it. However, subsequent calls to read return valid data and should start the bus again. If you were looking to read say 10 bytes off the bus subsequently, you'd only want 11 read calls, not 20 since each subsequent call starts the bus again. So if the I2CRead command called read twice each time, it would throw out valid data. You basically need to call it once to start the transaction then each subsequent call gets you the data you'd want.
joepasquariello
Posts: 85
Joined: Mon Apr 28, 2008 7:32 am

Re: I2CRead() returns "previous" byte

Post by joepasquariello »

Thanks very much. That helps a lot. I've extended your code snippet from I2CReadBuf() to show 2 more lines, including the final read of I2C_DR. To read N bytes, there are N calls to I2CRead() plus one final read of I2C_DR. That makes sense. Is the purpose of the I2C_SET_TX before the final read of I2C_DR simply to prevent the final read of I2C_DR from starting yet another read cycle?
mbrown wrote: I2CRead(buf);
while(num > 1)
{
...
num--;
status = I2CRead(buf++);
...
}
I2C_SET_TX;
*buf = I2C_DR; // Last read
mbrown
Posts: 61
Joined: Tue Jan 29, 2013 7:12 pm

Re: I2CRead() returns "previous" byte

Post by mbrown »

Every time I read through that logic, I think it's a little odd looking, but I can't really think of a better way to write it that adds clarity so I haven't changed it. The while loop is while(num > 1) not while(num >= 1), so you'll have read everything into the buffer except the last byte.

Why read directly from the register instead of calling the read function? It's a matter of state-checking. During the last read cycle we set the NO_ACK condition so that when we run through the read routine which calls the interrupt routine, instead of returning the status NEXT_READ_OK that our read function needs to operate, we return OK. If we called our read function instead of reading from the register, when we get through the first check, if(status != NEXT_READ_OK ), we fail and reset the bus when in reality, we're just trying to get our buffered data and we don't care too much what happens after this.
joepasquariello
Posts: 85
Joined: Mon Apr 28, 2008 7:32 am

Re: I2CRead() returns "previous" byte

Post by joepasquariello »

Okay, thanks for the explanation. I had just skimmed over those NO_ACK statements while trying to understand how I2CRead() was used, so that's really good info. I don't understand how I2C interrupts get triggered, so I'll try to review that along with your post. May I ask again what is the purpose of the call of I2C_SET_TX just before the final read of I2C_DR? Does taking the I2C out of RX mode prevent the read of I2C_DR from starting yet another read?
mbrown
Posts: 61
Joined: Tue Jan 29, 2013 7:12 pm

Re: I2CRead() returns "previous" byte

Post by mbrown »

The interrupts are triggered anytime the wires are toggled on the I2C lines basically is the way to think about it. Are we trying to send information out and wrote something to the data register? The interrupt is triggered. Are we trying to do a read and tried to get the byte from the data register which starts a read transaction on the wires? The interrupt is triggered. In a multi-master situation where someone else is trying to address a device or us as a slave and the lines toggle? The interrupt triggers. Basically, if you read through the functions and we're pending on a semaphore or it isn't clear why we just returned the status after doing something, look to see if we did something with the data register and we probably just returned from the interrupt routine.

The I2C_SET_TX call..., yes, I believe this sets us to be a transmitter so that we don't actually try to read from the bus when getting info from the data register. If you're curious where how the I2C interrupt routine functions and you're more of a visual person, there's a flow chart on page 42-16 of the reference manual.
joepasquariello
Posts: 85
Joined: Mon Apr 28, 2008 7:32 am

Re: I2CRead() returns "previous" byte

Post by joepasquariello »

Thanks again for your help. I see the flowchart on page 22-16 of the MCF5213 reference manual. Was this flowchart the basis for the NB driver's interrupt routine?

Joe
mbrown
Posts: 61
Joined: Tue Jan 29, 2013 7:12 pm

Re: I2CRead() returns "previous" byte

Post by mbrown »

Yup. If you read through our interrupt routine, it should be fairly obvious where you're going in the tree. Best of luck developing your protocol driver.
joepasquariello
Posts: 85
Joined: Mon Apr 28, 2008 7:32 am

Re: I2CRead() returns "previous" byte

Post by joepasquariello »

mbrown wrote:Yup. If you read through our interrupt routine, it should be fairly obvious where you're going in the tree. Best of luck developing your protocol driver.
Thanks. I'm not planning to develop an I2C driver, if that's what you mean. Since we have to use the "low-level" functions to read data from our I2C EEPROM, I'm trying to understand those functions and how to use them.
Post Reply