Know when an I2C transaction is complete

I'm using an uno as an I2C slave. It replies to a master's request with a large number of bytes of data. I would like to be able to check, in the flow of the main program and separately from interrupts, whether the full transaction has finished, and have the main program take certain actions only when it has.I know that by monitoring the TWINT (seventh) bit of TWCR you can see when a byte of an i2c transaction is complete, but there is nothing there to tell you the difference between whether it is reporting that the first byte has finished being sent and whether it is reporting that all bytes of the message are sent.

I've monitored my arduino with an oscilloscope and found that the I2C actions run while the program continues, giving occasional interruptions to the main program each time a further byte of the message is sent over i2c.

I can't be sure but as far as I can tell calling Wire.write(bytes_array,number_of_bytes_inarray); during the Wire.onRequest() callback doesn't actually send all your bytes there and then, it just sets something up to cause another interrupt to happen each time a single byte is sent and this keeps happening until all bytes are sent.

I want the main program to be able to use an if loop, encompassing certain actions, which will only run if the i2c has finished sending ALL of my bytes. I can't hope for the main loop of code to run so fast as to detect every instance where TWINT goes high and try to count them to record the bytes going out, TWINT only goes high for perhaps a microsecond and the main loop isn't fast enough to catch that, and if I missed one such event I'd think that the bytes had never all gone out.So I want something where some sort of value starts at say 0 normnally,gets set to 1 when the i2c request comes in, then stays at 1 while the arduino sends each byte over i2c to the master one at a time, then returns to 0 once the last byte has fully finished being sent out.

How can this be done? What is the interrupt called which handles the sending of each byte out over i2c to the master, how can I modify it to include some sort of volatile counter which the main program can check the value of so as to know when the whole lot of i2c bytes have all finished being sent.

Thanks

It replies to a master's request with a large number of bytes of data.

What is a "large number of bytes"? There are 32 byte buffers in the Wire library for block writes, but I believe can be changed in the Wire library and twi.

Wire.requestFrom() does not return until the requested data is fully available, or an error occurred. This is assuming that the write takes place within the 32 byte limit.

See Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

@Infraviolet, are you trying to solve a problem that is not even there ?
I think this is a XY-problem.

Could you be more specific ?
Are you using Arduino boards of the AVR family for both the Slave and the Master ? For example Arduino Uno boards ?
Do you have a Master sketch and a Slave sketch that shows the problem ?
What is the problem ?

You know that the Wire library works with buffers and the Wire.write() writes to a buffer and as soon onRequest() handler has finished, you can write new data to the global volatile data, right ?
Or maybe you want to know when the I2C bus is idle, so the Slave can become a Master ? You better avoid that, unless you are looking for trouble :smiling_imp:

cattledog: I'm sending 32 bytes from slave to master, at 100KHz this takes a few ms overall. I don't think requestfrom is of any applicability here, my arduino is s slave not a master. I am trying to get the SLAVE to knw once it has finished sending out its 32 bytes so it can then go off and execute bits of code in the main loop which involve long atomic blocks of ultra precision timing which are incompatible with having I2C run at the same time. I am working to manage thing such that by knowing when the I2C has actually finished only then will I call these i2c-unfriendly sections of coe, and I can guaranteee that these i2c-unfriendly bits of code will then finish well before the master sends another I2C request.

P.S. I had already looked over Gammon's article on the subject, what really surprises me is how much less he has to say about i2c than he does about other topics like interrupts and things. Most of his articles really dig into AVR code details but this one sticks mostly to arduino library functions. I am surprised that for this there isn't something there about how to read out mroe information about what the i2c hardware is up to and hence know when all the bytes of the buffer have been sent out, rather than the way that TWINT only tells you when the current byte has ben sent out but not how many mroe bytes of buffer are still to go.

Koepel: My slave is an uno, but my master is not an arduino. I'm just considering code on the slave here. I do not wish the slave to become master, but I do in effect wish to know when the bus is idle, when the slave has finished sending the 32 bytes up to the master. When the i2c has finished then the slave can do some VERY time sensitive stuff. My master des not call for specific bytes from specific registers of the slave arduino, instead it asks for 32 bytes all one after the other.

As far as I've been able to work out the i2c wire library works as I decribe below:
1.when an arduino has been initialised as an i2c slave you set up a function which is going to run when an i2c request for data is received.
2.when the maser actually sends a reuest the i2c hardware detects this and raises an interrupt of the main program
3.this interrupt runs just once per i2c transaction, so the function you set up to be the callback executes just once (I've proved this by oscilloscope tests). Wire.write(bytes_array,number_of_bytes_inarray); is included within this function.
4.then once the first byte of your multibyte i2c message has been sent out by the hardware the TWINT flag gets set
5.some sort of hidden away interrupt within the wire library is being triggered by TWINT going to the "completed the sending of the byte" state???
6.this hidden interrupt chanegs the TWINT flag back to the opposite state, and puts the next byte of the buffer into the i2c hardware???
7.the hardware again feeds out this byte, and again TWINT goes to "byte done" once it has done so???
8.yet again the hidden interrupt happens, next byte goes in, and so on???
9.this continues until the number of bytes specified in "Wire.write(bytes_array,number_of_bytes_inarray);" have all been sent out to the master, after which once TWINT has been set back to the normal "nothing to report" value this interrupt won't get caleld again until a new i2c request coems from the master???

What I want to do is modify this so that when that 9th step of, what I beleive is roughly, what the i2c wire library is up to runs I want it to also set a volatile flag so the main loop on the uno can see that all the twiddling up and down of both clock and data lines is now all finished.

Thanks for the explanation.

How long does the precise timing take ?
Does the unknown Master support clock pulse stretching ? and for how long ?
Perhaps you can afford to turn the interrupts off for some time, even when the I2C bus is still busy.

Can you tell more about the precise timing ?
When a hardware timer could be used, then there is no worry what the code or interrupts are doing.

Could it be possible to determine the maximum time that it takes for the Master to read those bytes ?
Then you could start a software millis() timer in the onRequest() handler and wait in the loop() until 10 or 20 milliseconds have passed (4 milliseconds for the I2C data, some extra by the Master and some extra by the Slave).
I think this is the best solution, and it does not require to mess with the standard Arduino library.

It seems that the interrupt for STOP is only used in Slave-mode when receiving data.
github.com/arduino/ArduinoCore-avr/blob/master/libraries/Wire/src/utility/twi.c#L498

As far that I understand the code, when the Slave should send data, the first is send after a fall-through of TW_ST_SLA_ACK. After that the rest of the bytes is send in TW_ST_DATA_ACK and the Slave does not seem to do anything else. It might just stop sending data when the Master does no longer request data with an acknowledge. The Slave only needs to send data, it does not need to acknowledge the data or do a STOP on the I2C bus.
Therefor I don't see how to capture the end.

I have not checked what the hidden interrupt is doing.

Could this library be useful ? GitHub - thexeno/HardWire-Arduino-Library: A spinoff of the Arduino Wire library, implements a fully controllable I2C slave.

I understand better your issue. I did not previously realize that the master was not an Arduino, and that you wanted the slave(not the master) to know when a transfer was complete.

You could try reading the TWI Status Register. There is a status code for Slave Transmitter which indicates last data byte transmitted, ACK received

TW_ST_LAST_DATA 0xC8

https://www.nongnu.org/avr-libc/user-manual/group__util__twi.html#gaf92d03d0051d47f5b9375f0ef9293d64

Infraviolet:
I do not wish the slave to become master, but I do in effect wish to know when the bus is idle, when the slave has finished sending the 32 bytes up to the master.

In I2C communication, it is the Master that can send/receive data to/from a Slave by virtue of its ability to generate SCL pulses. The Slave can not send/transmit data to the Master as it (the Slave) can not generate SCL pulses. Therefore, it is the Master that can say if the data transfer is complete or not and not the Slave.

The Slave creates the ACK/NACK signal by modulating the SDA line to inform the Master that it (the Slave) has well received the data byte and the Master my proceed to send the next data byte. Here, 'proceed' refers to writing next data byte into TWDR Register (for example: TWDR = 0x35;) during which the SCL pulses are automatically generated. At the same time, the TWINT Register records the 'process status/bus status' based on which the Master decides if the next data byte would be sent or not.

To study I2C Protocol, one needs Master-Slave pair for which UNO-DS3231 and UNO-NANO are the good options. High level codes for I2C communication could be easily transformed into ATmega's register level instructions for onward testing on the DS3231/NANO slaves.

The DS3231 has built-in 2C logic to increment the addresses of the internal registers; whereas, for NANO we don't have that kind of arrangement. The Wire.h Library has provided the following event handlers so that we can perform read/write operations on NANO's registers:

Wire.onReceive(receiveEvent);
Wire.onRequest(sendEvent);

void receiveEvent(int howMany)
{

}

void sendEvent(int howMany)
{

}