cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F401CCU6 I2C3 + DMA TX works electrically, DMA TC fires, but AW9523 never receives data

pmge
Associate II

Hi everyone,

I'm working with an STM32F401CCU6 and trying to transmit 2 bytes over I2C3 using DMA1 Stream 5 Channel 6, which is the correct mapping according to RM0368 Table 28:
DMA1 Channel 6 Stream 5 -> I2C3_TX

The DMA configuration appears correct, and I do receive the DMA Transfer Complete (TC) interrupt. So the DMA request is generated, the stream runs, and the transfer completes.

However, it seems like the I2C slave device (AW9523) never receives the register and data bytes. Unfortunately my osci is currently broken.
The same write sequence works perfectly when I use a blocking I2C write (polling SB, ADDR, TXE, etc.). Only the DMA‑driven version fails.
What I observe:

  • START condition is generated
  • Address is sent and ACKed
  • ADDR flag is cleared correctly
  • DMA TC interrupt fires
  • STOP is generated
  • But the slave never sees the register or data byte

DMA is configured as:

  • DMA1, Stream 5, Channel 6
  • Direction: Memory -> Peripheral
  • Memory increment enabled
  • Peripheral increment disabled
  • Byte transfers
  • Normal mode
  • TC/TE interrupts enabled

I2C sequence:

  • Configure DMA (addresses, length)
  • Enable DMA request in I2C
  • Enable DMA stream
  • Generate START
  • In SB interrupt: send address
  • In ADDR interrupt: clear ADDR and Enable DMA request in I2C
  • DMA should send the 2 bytes
  • DMA TC → STOP

Blocking version works, DMA version does not. I tried different things like switch enabling dma and so on but nothing works. My shared code is derived from the example in https://gith....Projects/STM32F411RE-Nucleo/Examples_LL/I2C/I2C_OneBoard_Communication_DMAAndIT/ 

So DMA is definitely running, but the I2C peripheral never actually transmits the bytes that DMA writes into DR.

 

Here is my not working code with dma:

static void MX_I2C3_Init(void)
{
	/* USER CODE BEGIN I2C3_Init 0 */
	/* USER CODE END I2C3_Init 0 */LL_I2C_InitTypeDef I2C_InitStruct = {0};
	LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
	/*I2C3 GPIO Configuration
	PA8   ------> I2C3_SCL
	PB4   ------> I2C3_SDA
	*/
	GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
	LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_9;
	LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	/* Peripheral clock enable */
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
	/* I2C3 DMA Init */
	/* I2C3_TX Init */
	LL_DMA_SetChannelSelection(DMA1, LL_DMA_STREAM_5, LL_DMA_CHANNEL_6);
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_5, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
	LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_5, LL_DMA_PRIORITY_VERYHIGH);
	LL_DMA_SetMode(DMA1, LL_DMA_STREAM_5, LL_DMA_MODE_NORMAL);
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_5, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_5, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_5, LL_DMA_PDATAALIGN_BYTE);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_5, LL_DMA_MDATAALIGN_BYTE);
	LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_5);
	/* I2C3 interrupt Init */
	NVIC_SetPriority(I2C3_EV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
	NVIC_EnableIRQ(I2C3_EV_IRQn);
	NVIC_SetPriority(I2C3_ER_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
	NVIC_EnableIRQ(I2C3_ER_IRQn);
	/* USER CODE BEGIN I2C3_Init 1 */
	/* USER CODE END I2C3_Init 1 */
	/* I2C Initialization
	*/
	LL_I2C_DisableOwnAddress2(I2C3);
	LL_I2C_DisableGeneralCall(I2C3);
	LL_I2C_EnableClockStretching(I2C3);
	I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
	I2C_InitStruct.ClockSpeed = 100000;
	I2C_InitStruct.DutyCycle = LL_I2C_DUTYCYCLE_2;
	I2C_InitStruct.OwnAddress1 = 0;
	I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
	I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
	LL_I2C_Init(I2C3, &I2C_InitStruct);
	LL_I2C_SetOwnAddress2(I2C3, 0);
	/* USER CODE BEGIN I2C3_Init 2 */LL_I2C_Enable(I2C3);
	LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5);
	LL_DMA_EnableIT_TC(DMA1,LL_DMA_STREAM_5);
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_5);
	LL_I2C_EnableIT_EVT(I2C3);
	LL_I2C_EnableIT_ERR(I2C3);
	/* USER CODE END I2C3_Init 2 */
}

void I2C_Master_Write_DMA(uint8_t *buf, uint8_t len)
{
    // DMA TX config
    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5);
    LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, len);
    //LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)buf);
    //LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_5, LL_I2C_DMA_GetRegAddr(I2C3));
    LL_DMA_ConfigAddresses(DMA1, LL_DMA_STREAM_5, (uint32_t)(buf), LL_I2C_DMA_GetRegAddr(I2C3), LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

    LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5);

    LL_I2C_GenerateStartCondition(I2C3);
}

// ISRs
void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */

  /* USER CODE END DMA1_Stream5_IRQn 0 */
  /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */

	  if(LL_DMA_IsActiveFlag_TC5(DMA1))
	  {
	    LL_DMA_ClearFlag_TC5(DMA1);
	    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5);
	    LL_I2C_GenerateStopCondition(I2C3);
	    i2c_tc++;
	  }
	  else if(LL_DMA_IsActiveFlag_TE5(DMA1))
	  {
		  LL_DMA_ClearFlag_TE5(DMA1);
		  i2c_error++;
	  }

  /* USER CODE END DMA1_Stream5_IRQn 1 */
}

void I2C3_EV_IRQHandler(void)
{
  /* USER CODE BEGIN I2C3_EV_IRQn 0 */

  /* USER CODE END I2C3_EV_IRQn 0 */
  /* USER CODE BEGIN I2C3_EV_IRQn 1 */
	if (LL_I2C_IsActiveFlag_SB(I2C3))
	{
		// Send slave address + WRITE
		LL_I2C_TransmitData8(I2C3, AW9253_ADDRESS << 1);
	}
	else if (LL_I2C_IsActiveFlag_ADDR(I2C3))
	{
	    LL_I2C_EnableDMAReq_TX(I2C3); // Enable DMA stream BEFORE START
	    LL_I2C_ClearFlag_ADDR(I2C3); //
	}
  /* USER CODE END I2C3_EV_IRQn 1 */
}

/**
  * @brief This function handles I2C3 error interrupt.
  */
void I2C3_ER_IRQHandler(void)
{
  /* USER CODE BEGIN I2C3_ER_IRQn 0 */

  /* USER CODE END I2C3_ER_IRQn 0 */
  /* USER CODE BEGIN I2C3_ER_IRQn 1 */
    // NACK 
    if (LL_I2C_IsActiveFlag_AF(I2C3))
    {
        LL_I2C_ClearFlag_AF(I2C3);
        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5); //LL_I2C_GenerateStopCondition(I2C3);
        LL_I2C_GenerateStopCondition(I2C3);
        i2c_error_ack = 1;
    }

    // Bus error
    if (LL_I2C_IsActiveFlag_BERR(I2C3))
    {
        LL_I2C_ClearFlag_BERR(I2C3);
    }

    // Arbitration lost
    if (LL_I2C_IsActiveFlag_ARLO(I2C3))
    {
        LL_I2C_ClearFlag_ARLO(I2C3);
    }

    // Overrun/Underrun
    if (LL_I2C_IsActiveFlag_OVR(I2C3))
    {
        LL_I2C_ClearFlag_OVR(I2C3);
    }

  /* USER CODE END I2C3_ER_IRQn 1 */
}

 

And without DMA the write method is really simple:

void I2C_Master_Write_noDMA(void)
{
	LL_I2C_GenerateStartCondition(I2C3);
	while (!LL_I2C_IsActiveFlag_SB(I2C3));
	LL_I2C_TransmitData8(I2C3, AW9253_ADDRESS << 1);

	while (!LL_I2C_IsActiveFlag_ADDR(I2C3));
	LL_I2C_ClearFlag_ADDR(I2C3);

	LL_I2C_TransmitData8(I2C3, buf1[0]);
	while (!LL_I2C_IsActiveFlag_TXE(I2C3));

	LL_I2C_TransmitData8(I2C3, buf1[1]);
	while (!LL_I2C_IsActiveFlag_TXE(I2C3));

	LL_I2C_GenerateStopCondition(I2C3);

}

Has anyone seen this behavior on the F401 I2C3 peripheral?
Is there a known timing requirement or erratum where the first TXE event is lost when using DMA on I2C3?

Any insight would be appreciated.

1 REPLY 1
pmge
Associate II

Sorry and it gots really weird. I accidentally told dma my array length is 3 and then it works. My buf still counts 2 values {reg, value}:

uint8_t buf_i2c [2] = {0};

 

and if i do this, then it works:

buf_i2c[0] = reg;
buf_i2c[1] = val;

I2C_Master_Write_DMA(buf_i2c, 3);

 

i dont know why