@trullock
Have you tested my sketches? Are they working? If they are working, then you have working examples which you can analyse to get the answers to your all queries.
The sketches have been designed based on the sound principles of the I2C Bus which has been developed by the "Really" smart guys of the Arduino Development Team.
The Arduino Team had to perform a lot of register level instructions before developing the high level structure of the I2C Bus. You need to design and execute some register level programs in order to grasp the working principles of the following high level commands of Arduino Platform on I2C/TWI Bus.
1. Wire.beginTransmission()
2. Wire.endTransmission()
3. Wire.write()
4. Wire.read()
5. Wire.requestFrom()
6. Wire.onReceive()
7. Wire. onRequest()
The reading of the following short note may help you to understand the I2C Bus operation before you proceed to write register level instructions:
6.2 Understanding I2C Bus Operation by Register Level Instructions
Figure-6.4: 7-bit address format of an I2C Bus Slave
(1) IDLE State: The I2C Bus is said to be at IDLE State when both SDA and SCL lines are at LH-states (U-state in Fig-6.4) and there is no activities on the bus.
(2) START State: The START (S-state in Fig-6.4) condition is brought on the I2C Bus by bringing down the SDA line into low-state (LL-state) while the SCL line is still at high-state and then pulling down the SCL line into low-state. The event happens when the user/Master executes the following codes; where, logic-high is placed at the TWSTA-bit (TWI START Condition Bit) of TWCR Register. If the bus responses to this action, the TWINT-bit will assume logic-high state and upper 5-bit of TWSR Register will hold 00001 which is known as bus status. In order to allow the TWINT-bit assuming new value, we have to clear its content at the beginning by writing logic-high into it.
TWCR = 0b10100100; //TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE //no SCL is generated
while(bitRead(TWCR,7)!= HIGH)
; //wait until the process completes and then TWINT will assume LH-state
lcd.print((TWSR & 0b11111000),HEX); //shows: 0x08(00001000) indicates successful process;
(3) Process State: The Master detects the presence (V-state in Fig-6.4) of a Slave (for example: BME280 of Fig-6.1) sensor by asserting its ‘7-bit address (1110110) + data direction bit (R-W/ = 0)’ on the bus. The resultant 8-bit (11101100 = 0xEE) is known as Control Byte (Fig-6.2). To clock-in the 8-bit data of the Control Byte into the Slave, the Master automatically generates 8 SCL pulses. The 8 SCL pulses are generated when the user loads data into TWDR Register. If the Slave is present, it accepts the address bits and then generates ACK (acknowledgement) signal on the I2C bus by bringing down the SDA line. The Master generates the 9th SCL pulse (Fig-6.4) to sample/grasp the ACK-bit. The event happens when the user executes the following codes:
TWDR = 0b11101100; //Slave address (SLA) + write-bit (0) = 11101100 = 0xEE
TWCR = 0b10000100; //TWINT-bit is cleared; it will assume LH-state when process will end
while(bitRead(TWCR,7)!= HIGH)
; //wait until TWINT-bit LH-state indicating end-of-process
lcd.print((TWSR & 0b11111000),HEX); //shows: 0x18(00011000) indicates successful process;
(4) STOP State: The Master brings STOP (P-state in Fig-6.4) on the I2C Bus by bringing SDA line to LH-state and then SCL line to LH-state. The event happens when the user executes the following code where logic-high is stored into TWSTO-bit (TWI Bus Stop) of TWCR Register. As no ‘bus status’ signal will be generated during this process, there is no need to poll the TWINT-bit to see if it has assumed LH-state.
TWCR = 0b10010100; //TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE;