Вот это тоже не могу понять "большой объём кода для настройки и
запуска DMA". Где большой-то? вот, например:
void
i2c_writebytes_start(int addr, const void* src, int len)
{
txdma->CNDTR = len;
txdma->CPAR = (int)&I2C1->TXDR;
txdma->CMAR = (int)src;
txdma->CCR = DMA_CCR_MINC
| DMA_CCR_DIR
| DMA_CCR_EN;
I2C1->CR2 = I2C_CR2_AUTOEND
| (len << I2C_CR2_NBYTES_Pos)
| addr;
I2C1->CR2 |= I2C_CR2_START;
}