I2C is one of the most popular buses in use for sensors. It is not particularly fast or robust, but it's simple and supported by most of the microcontrollers. It is also reasonably easy to bit-bang. The problem is, that most of the implementation examples I found online (as well as issues) are related to polling mode communications. But I wanted to do DMA and couldn't understand, why doesn't it work out of the box.
Why would I care about I2C over DMA anyway? The thing is, that I2C is slow. Say, if I have 100kHz I2C sensor, then my MCU does nothing for 1800 cycles for each bit in polling mode, when running at 180MHz. We have better things to do during that time.
My initial attempt was to pass a buffer to I2C_Master_Receive_DMA() function, same as I would do it in blocking mode. And nothing happened. Well, something happened - both pins (SCL and SDA) were pulled down, what's called a START condition. And then MCU would get stuck there in RX_BUSY state. Dammit, machine, you are supposed to generate clocks, not wait for something.
I then proceeded to look at HAL code, which basically just sends START condition. But who's supposed to do all the work? Reading reference manual, I found out, that after START condition an EVENT interrupt is generated. And HAL is supposed to handle that. So you have to enable event interrupt handler and feed that to the HAL. Only then would actual transmit/receive proceed. Who would've guessed, but it still beats waiting in polling mode...