STM32H573 - How to Determine Actual Number of I2C Bytes Sent to Controller?
Hi all,
I am implementing a protocol over I2C that requires the STM32 part to act as peripheral and respond to host data reads. I get two types of read transactions: random access, where the controller sends the peripheral address with the WRITE bit set, then a memory address to begin reading, then another start and the peripheral address with the READ bit set. And sequential reads, where the controller doesn't specify an address, it just starts reading. To handle this I need to maintain a "next read address" internally. Here's a diagram of the sequential read:

I am using HAL APIs successfully and driving this from FreeRTOS. I'm testing it with an Aardvark debugger standing in for the controller and an Analog Discovery 2 watching the bus and decoding it. My task turns on listen mode. I get the expected calls to HAL_I2C_AddrCallback(). I can successfully distinguish the two types of transactions, get the memory address with HAL_I2C_Slave_Seq_Receive_IT() with FIRST_FRAME, and send the requested bytes with HAL_I2C_Slave_Seq_Transmit_IT() with NEXT_FRAME.
The difficulty I'm having is trying to figure out how many bytes my code is actually getting onto the I2C bus. I need to be able to keep track of this in order to keep my internal "next read address" correct, so that if the controller starts another sequential read, where it doesn't supply a memory address, I know exactly which byte to send next.
I'd like to be able to just increment my internal "next read address" in the HAL_I2C_SlaveTxCpltCallback(). But this doesn't quite work. It seems that the peripheral and HAL driver lags behind in that the callbacks indicating that the transmission is complete get ahead of what has gone onto the bus. So when I get the HAL_I2C_ErrorCallback(), I have sent out two bytes more than the controller actually wants. (One is expected, as with this sort of sequential read the only way I have of knowing when to stop is by sending an extra byte and getting a NACK). So one might think I could just always just increment my internal "next read address" when I get a transmit complete callback, and back it up when I get the error callback and verify that the error was a NACK. (This ought to work fine because the 8-bit address is supposed to overflow/underflow).
But this doesn't quite seem to be the case either. It looks like I get either one extra HAL_I2C_SlaveTxCpltCallback(), or (sometimes) two. How much I do in the callback functions seems to affect this (I have code to put logging messages into a queue for another task to send to the UART; this slows things down enough to affect how many callbacks I get. Playing around with interrupt and task priority between the I2C task and the UART task also affect this. In other words, it's not quite deterministic and there seems to be a bit of a race. If I don't do any of that logging, just update some counters, it seems to reliably only get one callback ahead of the bus, but I'm not really happy with just assuming that will always be the case, especially once I have a number of additional tasks to talk to SPI devices, run control loops, etc.
I also get two successive error callbacks, which is an annoyance, but I can update a state variable when I get the first one and ignore the second one, so that seems harmless. Then I get the listen complete callback and signal my task to start the cycle over again.
What I'm looking for is a way that I can determine from inside the transmit complete callback whether the send operation I'm getting the callback for is actually completed at the hardware level, so that I'm able to keep my internal "next read address" value accurate.
I've seen some other messages on this board recommending using buffers and counts, but I'm not sure I can apply that device when I have to send bytes one at a time. I'd love to be able to use DMA calls, but it doesn't seem like I'll be able to make them work in a way that is compatible with the HAL sequential drivers that allow me to specify first, next, etc.
I have experience writing low-level drivers from scratch but I'd really like to stick with the HAL APIs if I can.
Thanks for any suggestions.
