I'm educating myself about I2C/TWI protocol and I find ATmega328P datasheet to be an outstanding "textbook" for this topic. However, I ran into a difficulty trying to understand example C code for handling a simple I2C transmission.
while (!(TWCR & (1<<TWINT)))
// Wait for TWINT flag set. This indicates that the START condition
// has been transmitted.
According to the datasheet, TWINT flag is set when it evaluates to 0 and cleared when it evaluates to 1 (which contradicts generic understanding of "set" and "clear", but I've already run into this sort of things, so no problem).
(1<<TWINT) returns 0b10000000
TWCR & 0b10000000 returns either 0 (if TWINT bit in TWCR is set, i.e. 0) or 0b10000000 (if TWINT bit in TWCR is cleared, i. e. 1). In boolean logic this corresponds to false and true (since any non-zero integer value evaluates to true, though only 1 equals it).
Finally, !false equals to true and !true equals to false.
While() loop breaks if condition is false and continues if condition is true.
Therefore, if TWINT is set, while() condition evaluates to true, and if TWINT is cleared, while() condition evaluates to false.
But the provided comment states that we wait for TWINT flag to be set, that is, while() loop should go on when TWINT flag is cleared and break as soon as it's set. Thus the contradiction and my problem.
I guess I'm overlooking something and I ask for a clue.
In fact, it is. Reading the datasheet lead me to a (false) assumption that, regarding to interrupt flags, bit value 0 is "set" and 1 is "clear". Now I understand that "writing 1 to TWINT to clear it" doesn't mean "make its value 1", but rather "send 1 to make its value 0". Therefore "set" is "holds value 1", as it should be. It removes the contradiction I had run into.
Anyway, how can TWCR & 0b10000000 return 1? If TWINT bit is 1, bitwise AND will return 0b10000000 (128), not 1. We haven't reached boolean stage by this moment yet.
My mistake. If the TWINT bit is set, the bitwise AND returns a non zero or logical true result, which the ! operator converts to zero, or logical false.
The method of resetting the interrupt flag bit by writing a "1" to that bit location is a problematic quirk of AVR processor, that can lead to hard to find programming errors.
For example, it is not correct or safe in the general case of a register with multiple interrupt flags to do something like this, but many people do it without thinking.
The following short discussion (referring to Fig-1) on TWINT-bit could be relevant:
Figure-1: Logic activities in the TWI Bus
Master brings START condition on the TWI Bus:
START condition has been defined as the process of bringing down the SDA line to LL state while the SCL line is still at LH state. This condition has been shown as busevent (S) in Fig-1. The completion of this process requires some finite amount of time; the TWINT-bit of TWCR Register remains at LOW state during this busy time; the TWINT-bit assumes HIGH state at the end of the process. At the same time, the upper 5-bit of the TWSR Register hold 00001 as Status Word, which becomes 00001000 (0x08) when mapped over the whole 8-bit of the TWSR Register by masking the lower 3-bit. This manipulation does not change the values of lower 2-bit of TWSR Register, which determine the speed of the TWI Bus. The status word is a known value of the TWI Bus Protocol, and it indicates the attempted process has been successfully executed and then the next bus action could be undertaken. The following C Codes bring the TWI Bus into START condition.
TWCR = 0b10100100; //TWINT-bit is cleared; START condition is asserted
//TWCR = TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE
//Execution order: TWEN, TWINT, TWSTA, ...
while(bitRead(TWCR, TWINT) != HIGH)
{
; //wait until process is being completed
}
//checking if process is done by looking for LH at TWINT-bit; wait until TWINT-bit is HIGH
Serial.print((TWSR & 0b11111000), HEX); //Serial Monitor should show: 08 (8)