Skip to main content
dryet
Associate II
Aug 28, 2024 11:24 AM UTC
Solved

I2C DMA synchronization not triggering

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.

This topic has been closed for replies.
TDK
TDKBest answer
Aug 28, 2024 1:44 PM UTC

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""."

4 replies

TDK
Aug 28, 2024 1:13 PM UTC

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
Aug 28, 2024 1:33 PM UTC

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
Aug 28, 2024 1:44 PM UTC

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
Aug 28, 2024 1:54 PM UTC

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