Skip to main content
dryet
Associate II
August 28, 2024
Solved

I2C DMA synchronization not triggering

  • August 28, 2024
  • 2 replies
  • 1178 views

Hi guys,

I am trying to run a demo on STM32U083C-DK board. 

This demo consists of LPTIMER generating PWM that is used for synchronization of I2C read from on-board temperature sensor. Rising edge writes the addres of read and falling edge reads data. The main issue I have encountered is that my synchronization setup works perfectly with UART transmit but with I2C no matter what I try I don't ever get transmit on the I2C bus.

What I checked:

- LPTIMER NVIC is on and is able to start UART synchronization

- I2C DMA works when software triggered. (with synchronization disabled)

DMA setup for I2C DMA:

dryet_0-1724843526621.png

dryet_1-1724843567189.png

dryet_2-1724843593231.png

LPTIMER config:

dryet_3-1724843626795.png

dryet_4-1724843641187.png

Logic analyzer output:

dryet_5-1724843706033.png

Where D0 is SDA, D1 is SCL and D2 is LPTIMER output

This is the main and initialisation of I2C:

I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_tx;
DMA_HandleTypeDef hdma_i2c1_rx;
LPTIM_HandleTypeDef hlptim1;

union {
	uint8_t temp_raw_8[2];
	int16_t temp_raw_16;
}temp_raw;
uint8_t stts22h_temp_l_out = STTS22H_TEMP_L_OUT;
STTS22H_IO_t stts22h_io;
STTS22H_Object_t stts22h_obj;

int main(void)
{
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_USART2_UART_Init();
 MX_I2C1_Init();
 MX_LPTIM1_Init();

 /* USER CODE BEGIN 2 */
 HAL_I2C_Master_Transmit_DMA(&hi2c1, STTS22H_I2C_ADD_L, &stts22h_temp_l_out, 1);
 HAL_I2C_Master_Receive_DMA(&hi2c1, STTS22H_I2C_ADD_L, &temp_raw.temp_raw_8[0], 2);
 HAL_LPTIM_PWM_Start_IT(&hlptim1, LPTIM_CHANNEL_1);
 /* USER CODE END 2 */
}

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 HAL_DMA_MuxSyncConfigTypeDef pSyncConfig;
 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 if(hi2c->Instance==I2C1)
 {
 /* USER CODE BEGIN I2C1_MspInit 0 */
 /* USER CODE END I2C1_MspInit 0 */
 /** Initializes the peripherals clocks
 */
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
 PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_RCC_GPIOB_CLK_ENABLE();
 /**I2C1 GPIO Configuration
 PB7 ------> I2C1_SDA
 PB8 ------> I2C1_SCL
 */
 GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 /* Peripheral clock enable */
 __HAL_RCC_I2C1_CLK_ENABLE();

 /* I2C1 DMA Init */
 /* I2C1_TX Init */
 hdma_i2c1_tx.Instance = DMA1_Channel1;
 hdma_i2c1_tx.Init.Request = DMA_REQUEST_I2C1_TX;
 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_CIRCULAR;
 hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
 if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
 {
 Error_Handler();
 }

 pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_LPTIM1_OUT;
 pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_RISING;
 pSyncConfig.SyncEnable = ENABLE;
 pSyncConfig.EventEnable = ENABLE;
 pSyncConfig.RequestNumber = 1;
 if (HAL_DMAEx_ConfigMuxSync(&hdma_i2c1_tx, &pSyncConfig) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);

 /* I2C1_RX Init */
 hdma_i2c1_rx.Instance = DMA1_Channel2;
 hdma_i2c1_rx.Init.Request = DMA_REQUEST_I2C1_RX;
 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_CIRCULAR;
 hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
 if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK)
 {
 Error_Handler();
 }
 pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_LPTIM1_OUT;
 pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_FALLING;
 pSyncConfig.SyncEnable = ENABLE;
 pSyncConfig.EventEnable = ENABLE;
 pSyncConfig.RequestNumber = 2;
 if (HAL_DMAEx_ConfigMuxSync(&hdma_i2c1_rx, &pSyncConfig) != HAL_OK)
 {
 Error_Handler();
 }
 __HAL_LINKDMA(hi2c,hdmarx,hdma_i2c1_rx);
 /* I2C1 interrupt Init */
 HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(I2C1_IRQn);
 /* USER CODE BEGIN I2C1_MspInit 1 */
 /* USER CODE END I2C1_MspInit 1 */
 }
}

 

If I am missing something please let me know but I feel like I am either using wrong function to call I2C receive and transmit (which I doubt) or there is a bug.

Thank you in advance for answers.

Best answer by TDK

The example you linked uses UART. You are using I2C which is more complicated in its usage. You will need to initiate each I2C transaction with a call to HAL_I2C_Master_Receive_DMA. Perhaps put this in a timer interrupt callback which is called periodically.

2 replies

TDK
Super User
August 28, 2024

The basic premise of the program isn't valid. You can't use the HAL DMA routines like this. Look at the code within HAL_I2C_Master_Receive_DMA to see how it starts the transfer. Only after the transfer is started can DMA be used.

"If you feel a post has answered your question, please click ""Accept as Solution""."
dryet
dryetAuthor
Associate II
August 28, 2024

Could you please elaborate on your answer? How could I otherwise you the synchronization of I2C? 

I am basically trying the same thing as in: https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/STM32H743I-EVAL/Examples/DMA/DMAMUX_SYNC

 

TDK
TDKBest answer
Super User
August 28, 2024

The example you linked uses UART. You are using I2C which is more complicated in its usage. You will need to initiate each I2C transaction with a call to HAL_I2C_Master_Receive_DMA. Perhaps put this in a timer interrupt callback which is called periodically.

"If you feel a post has answered your question, please click ""Accept as Solution""."
dryet
dryetAuthor
Associate II
August 28, 2024

Okay, thank you. 

In my excercise I wanted to make I2C read to my memory completely automatic in the backround using DMA so it updates temperature values automatically without any MCU work and I can get the latest value in my code. 

I will continue to work on my example and I will try to come up with a way to make it work