I2C DMA is Busy

Firstly I've tried the following on STM32F303 and STM32F072 and it's the same result on both. The MCU is configured as a master and I'm trying to send I2C commands to an SSD1306 based OLED display.

I've tried using DMA to do this but the controller becomes HAL_BUSY after the first packet transfer. I can initialize the OLED via polling mode operation, and then use DMA to send one display buffer (192 bytes + 1 control byte). This locks the DMA in the HAL_BUSY state and it becomes unusable until the MCU is reset. I've also tried initializing with DMA, and the same thing happens, after just one command (sent as a single DMA packet) i.e. 1+1 bytes of data.

I've checked this on the scope, and as I mentioned: the first DMA packet transfers fine, be it 193 bytes or 2 bytes, but following that nothing happens and the I2C lines just very slowly exponentially decrease down to 0. I've tried these operations both at 100kHz and 400kHz without any difference.

My code is generated from CubeMX and is shown below:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)

{

GPIO_InitTypeDef GPIO_InitStruct;

if(hi2c->Instance==I2C1)

{

/* USER CODE BEGIN I2C1_MspInit 0 */

/* USER CODE END I2C1_MspInit 0 */

/* Peripheral clock enable */

__I2C1_CLK_ENABLE();

/**I2C1 GPIO Configuration

PB6 ------> I2C1_SCL

PB7 ------> I2C1_SDA

*/

GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;

GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_MEDIUM;

GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;

HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

__HAL_SYSCFG_FASTMODEPLUS_ENABLE(HAL_SYSCFG_FASTMODEPLUS_I2C_PB6);

__HAL_SYSCFG_FASTMODEPLUS_ENABLE(HAL_SYSCFG_FASTMODEPLUS_I2C_PB7);

/* Peripheral DMA init*/

hdma_i2c1_tx.Instance = DMA1_Channel2;

hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;

hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

hdma_i2c1_tx.Init.Mode = DMA_NORMAL;

hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;

HAL_DMA_Init(&hdma_i2c1_tx);

__HAL_REMAPDMA_CHANNEL_ENABLE(HAL_REMAPDMA_I2C1_TX_DMA1_CH2);

__HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);

hdma_i2c1_rx.Instance = DMA1_Channel5;

hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;

hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

hdma_i2c1_rx.Init.Mode = DMA_NORMAL;

hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;

HAL_DMA_Init(&hdma_i2c1_rx);

__HAL_REMAPDMA_CHANNEL_ENABLE(HAL_REMAPDMA_I2C1_RX_DMA1_CH5);

__HAL_LINKDMA(hi2c,hdmarx,hdma_i2c1_rx);

/* USER CODE BEGIN I2C1_MspInit 1 */

/* USER CODE END I2C1_MspInit 1 */

}

}

/**

* Enable DMA controller clock

*/

void MX_DMA_Init(void)

{

/* DMA controller clock enable */

__DMA1_CLK_ENABLE();

/* DMA interrupt init */

}

/* I2C1 init function */

void MX_I2C1_Init(void)

{

hi2c1.Instance = I2C1;

hi2c1.Init.Timing = 0x2000090E;

hi2c1.Init.OwnAddress1 = 0;

hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;

hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;

hi2c1.Init.OwnAddress2 = 0;

hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;

hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;

hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;

HAL_I2C_Init(&hi2c1);

/**Configure Analogue filter

*/

HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);

}

A display buffer is sent as such:

/* Use DMA to send the buffer, in order to save processor cycles;

NOTE: We embed the control byte (0x40) in the DMA command by having it write to a memory location */

Commands are sent in a similar fashion, so I won't show those as well. If anyone has any insight on why the DMA controller always becomes busy after the first packet, I'd really appreciate it. I could seriously use the extra speed.

Thanks for your response. It appears that the entire issue was due to me not having initialised the DMA NVIC. I thought that since I didn't need the interrupt in my program I could get away without initialising it. I clearly need to read up more on how DMA works. :)