Examples of I2C DMA
Hi, first of all: thank you for this fantastic repository. The build system setup is super pragmatic, easy to work with - and everything I have tried so far works nicely. And there is an impressive amount of examples.
I have one project with an accelerometer, where I would like to read at high rates (from the built-in FIFO). And it is also doing computationally intensive processing (some DSP and machine learning). So I would be very interested in using the DMA support to enable doing work on the CPU while the transfers are ongoing. I see that there is a DMA_CHANNEL_MAP_I2C* in the code. So there does seem to be support for this. There are DMA examples for the ADC for example, but I have not seen anything for I2C+DMA. Does anyone have such examples and/or tips&tricks? It is basically just receive that would benefit from DMA. SPI+DMA would also be relevant
I haven't tried I2C DMA, in my work I use timer to trigger I2C reading. Have you tried the I2C DMA examples in Puya's SDK? https://github.com/OpenPuya/PY32F0xx_Firmware/tree/master/Projects/PY32F030-STK/Example_LL/I2C
An example sending a 128 Byte buffer to an I2C device using DMA:
example.c
#define I2C_ADDRESS 0x3C
#define SCL_Port GPIOF
#define SCL_Pin LL_GPIO_PIN_1
#define SCL_AF LL_GPIO_AF12_I2C
#define SDA_Port GPIOA
#define SDA_Pin LL_GPIO_PIN_7
#define SDA_AF LL_GPIO_AF12_I2C
volatile uint8_t dma_busy;
__attribute__((aligned(4))) uint8_t buffer[128];
const LL_DMA_InitTypeDef DMAInit = {
.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH,
.Mode = LL_DMA_MODE_NORMAL,
.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT,
.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT,
.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE,
.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE,
.Priority = LL_DMA_PRIORITY_HIGH,
};
void i2c_dma_init(void){
LL_GPIO_Init(SCL_Port, &(LL_GPIO_InitTypeDef){ SCL_Pin, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_VERY_HIGH, LL_GPIO_OUTPUT_OPENDRAIN, LL_GPIO_PULL_NO, SCL_AF });
LL_GPIO_Init(SDA_Port, &(LL_GPIO_InitTypeDef){ SDA_Pin, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_VERY_HIGH, LL_GPIO_OUTPUT_OPENDRAIN, LL_GPIO_PULL_NO, SDA_AF });
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
LL_SYSCFG_SetDMARemap_CH1(LL_SYSCFG_DMA_MAP_I2C_TX);
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, (LL_DMA_InitTypeDef*)&DMAInit);
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, LL_I2C_DMA_GetRegAddr(I2C1));
LL_I2C_DeInit(I2C1);
LL_I2C_Init(I2C1, &(LL_I2C_InitTypeDef){ 400000, LL_I2C_DUTYCYCLE_2, 0, I2C_CR1_ACK }); //LL_I2C_DUTYCYCLE_16_9
LL_I2C_DisableBitPOS(I2C1);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
NVIC_SetPriority(DMA1_Channel1_IRQn, 1);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
void send_i2c_dma(void){
while(dma_busy){ ; }
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, sizeof(buffer));
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_I2C_GenerateStartCondition(I2C1); // Start condition
while(!LL_I2C_IsActiveFlag_SB(I2C1));
LL_I2C_TransmitData8(I2C1, I2C_ADDRESS); // Device address
while(!LL_I2C_IsActiveFlag_ADDR(I2C1));
LL_I2C_ClearFlag_ADDR(I2C1);
LL_I2C_TransmitData8(I2C1, modeData); // Device memory register address (If applicable)
while(!LL_I2C_IsActiveFlag_TXE(I2C1));
dma_busy = 1; // Set busy flag
LL_I2C_EnableDMAReq_TX(I2C1); // Enable DMA request, transfer starts inmediately
}
py32f0xx_it.c
void DMA1_Channel1_IRQHandler(void){
extern volatile uint8_t dma_busy;
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_ClearFlag_GI1(DMA1);
dma_busy = 0;
}