cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H723 I2C MEM DMA not working

Grozea.Ion
Associate II

Hi,

On a small project I am trying to use HAL_I2C_Mem_Read_DMA and AL_I2C_Mem_Write_DMA to get and send data to INA260 and SSD1306 without success.

In main i have:

 

MPU_Config(); SCB_EnableICache(); SCB_EnableDCache(); HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_I2C1_Init(); if (INA260_Init() == HAL_OK) { printf("INA260 initialized successfully.\n"); }

 

The MPU configuration is:

 

void MPU_Config(void) { MPU_Region_InitTypeDef MPU_InitStruct = {0}; /* Disables the MPU */ HAL_MPU_Disable(); /** Initializes and configures the Region and the memory to be protected */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.BaseAddress = 0x38000000; MPU_InitStruct.Size = MPU_REGION_SIZE_4KB; MPU_InitStruct.SubRegionDisable = 0x0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* Enables the MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); }

 

The I2c file is :

 

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file i2c.c * @brief This file provides code for the configuration * of the I2C instances. ****************************************************************************** * @attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "i2c.h" /* USER CODE BEGIN 0 */ volatile uint8_t i2c1_transfer_complete = 0; uint8_t txBuffer[TX_LEN] __ATTR_DMA_BUFFER; uint8_t rxBuffer[RX_LEN] __ATTR_DMA_BUFFER; /* USER CODE END 0 */ I2C_HandleTypeDef hi2c1; DMA_HandleTypeDef hdma_i2c1_rx; DMA_HandleTypeDef hdma_i2c1_tx; /* I2C1 init function */ void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.Timing = 0x60404E72; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /** Configure Analogue filter */ if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { Error_Handler(); } /** Configure Digital filter */ if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; if(i2cHandle->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER CODE END I2C1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1; PeriphClkInitStruct.I2c123ClockSelection = RCC_I2C1235CLKSOURCE_D2PCLK1; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } __HAL_RCC_GPIOB_CLK_ENABLE(); /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; 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); /* I2C1 clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); /* I2C1 DMA Init */ /* I2C1_RX Init */ hdma_i2c1_rx.Instance = DMA1_Stream6; 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_NORMAL; hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_i2c1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c1_rx); /* I2C1_TX Init */ hdma_i2c1_tx.Instance = DMA1_Stream7; 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_NORMAL; hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c1_tx); /* I2C1 interrupt Init */ HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0); HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0); HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspInit 1 */ /* USER CODE END I2C1_MspInit 1 */ } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle) { if(i2cHandle->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspDeInit 0 */ /* USER CODE END I2C1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_I2C1_CLK_DISABLE(); /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9); /* I2C1 DMA DeInit */ HAL_DMA_DeInit(i2cHandle->hdmarx); HAL_DMA_DeInit(i2cHandle->hdmatx); /* I2C1 interrupt Deinit */ HAL_NVIC_DisableIRQ(I2C1_EV_IRQn); HAL_NVIC_DisableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspDeInit 1 */ /* USER CODE END I2C1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ HAL_StatusTypeDef I2C_DMA_Write_MEM(uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint16_t Size) { // Clean the cache for the transmit buffer to ensure coherency //SCB_CleanDCache_by_Addr((uint32_t*)pData, Size); // Start the I2C DMA memory write operation HAL_StatusTypeDef status = HAL_I2C_Mem_Write_DMA(&hi2c1, DevAddress, MemAddress, MemAddSize, txBuffer, Size); if (status != HAL_OK) { return status; } // // Track the start time for timeout management // uint32_t tickstart = HAL_GetTick(); // // // Wait for the transfer to complete with timeout mechanism // while (i2c1_transfer_complete == 0) { // // Check if timeout is reached // if ((HAL_GetTick() - tickstart) > TIME_OUT) { // return HAL_TIMEOUT; // } // } return HAL_OK; } HAL_StatusTypeDef I2C_DMA_Read_MEM(uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize){ // Invalidate the cache for the receive buffer to ensure we get fresh data //SCB_InvalidateDCache_by_Addr((uint32_t*)pData, Size); // Start the I2C DMA memory read operation HAL_StatusTypeDef status = HAL_I2C_Mem_Read_DMA(&hi2c1, DevAddress, MemAddress, MemAddSize, rxBuffer, RX_LEN); if (status != HAL_OK) { return status; } // // Track the start time for timeout management // uint32_t tickstart = HAL_GetTick(); // // Wait for the transfer to complete with timeout mechanism // while (i2c1_transfer_complete == 0) { // // Check if timeout is reached // if ((HAL_GetTick() - tickstart) > TIME_OUT) { // return HAL_TIMEOUT; // } // } return HAL_OK; } void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) { if (hi2c->Instance == I2C1) { i2c1_transfer_complete = 1; // Mark transfer as complete } } void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) { if (hi2c->Instance == I2C1) { i2c1_transfer_complete = 1; // Mark transfer as complete } } void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { if (hi2c->Instance == I2C1) { i2c1_transfer_complete = 1; // Mark as complete even in case of error // Additional error handling code can be added here } } /* USER CODE END 1 */
View more

 

In i2c.h i have:

 

/* USER CODE BEGIN Includes */ extern volatile uint8_t i2c1_transfer_complete; /* USER CODE END Includes */ extern I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN Private defines */ #define __ATTR_DMA_BUFFER __attribute__ ((section(".dma_buffer"))) __attribute__ ((aligned (4))) #define TIME_OUT 100 #define TX_LEN 1024 #define RX_LEN 2 extern uint8_t txBuffer[TX_LEN] __ATTR_DMA_BUFFER; extern uint8_t rxBuffer[RX_LEN] __ATTR_DMA_BUFFER; /* USER CODE END Private defines */ void MX_I2C1_Init(void); /* USER CODE BEGIN Prototypes */ HAL_StatusTypeDef I2C_DMA_Write_MEM(uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint16_t Size); HAL_StatusTypeDef I2C_DMA_Read_MEM(uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize); /* USER CODE END Prototypes */

 

In Ina260.c i have : 

// Track energy consumption static uint32_t start_time_ms; static float total_energy_Wh = 0.0f; static INA260_Config myConfig = {.Value = DEFAULT_CONFIG}; static INA260_Mask myMask = {.Value = DEFAULT_MASK}; extern uint8_t txBuffer[TX_LEN] __ATTR_DMA_BUFFER; extern uint8_t rxBuffer[RX_LEN] __ATTR_DMA_BUFFER; // Helper function to write a 16-bit register (MSB first) // Original implementation commented for reference /* static HAL_StatusTypeDef write_register(uint8_t reg, uint16_t value) { uint8_t data[BUFFER_LEN]; // Send MSB first, followed by LSB data[0] = (uint8_t)(value >> 8); // MSB data[1] = (uint8_t)(value & 0xFF); // LSB return HAL_I2C_Mem_Write(&hi2c1, INA260_I2C_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, data, BUFFER_LEN, TIMEOUT_MS); } */ // Updated write_register using DMA static HAL_StatusTypeDef write_register(uint8_t reg, uint16_t value) { // Send MSB first, followed by LSB txBuffer[0] = (uint8_t)(value >> 8); // MSB txBuffer[1] = (uint8_t)(value & 0xFF); // LSB // Use the global I2C DMA function for writing return I2C_DMA_Write_MEM((uint16_t)INA260_I2C_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, BUFFER_LEN); } // Helper function to read a 16-bit register (MSB first) // Original implementation commented for reference /* static HAL_StatusTypeDef read_register(uint8_t reg, uint16_t *value) { uint8_t data[BUFFER_LEN]; HAL_StatusTypeDef status; // First write the register address to read from status = HAL_I2C_Mem_Read(&hi2c1, INA260_I2C_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, data, BUFFER_LEN, TIMEOUT_MS); if (status == HAL_OK) { // Reconstruct the 16-bit value (MSB first) *value = (data[0] << 8) | data[1]; // MSB first, then LSB } return status; } */ // Updated read_register using DMA static HAL_StatusTypeDef read_register(uint8_t reg, uint16_t *value) { HAL_StatusTypeDef status; // Use the global I2C DMA function for reading status = I2C_DMA_Read_MEM((uint16_t)INA260_I2C_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT); if (status == HAL_OK) { // Reconstruct the 16-bit value (MSB first) *value = (rxBuffer[0] << 8) | rxBuffer[1]; // MSB first, then LSB } return status; } // Initialize INA260 HAL_StatusTypeDef INA260_Init(void) { uint16_t mfg_id, dev_id; HAL_StatusTypeDef status; // Check manufacturer and device IDs status = INA260_GetManufacturerID(&mfg_id); if (status != HAL_OK || mfg_id != DEFAULT_MFG_ID) return HAL_ERROR; status = INA260_GetDieID(&dev_id); if (status != HAL_OK || dev_id != DEFAULT_DEV_ID) return HAL_ERROR; INA260_Reset(); // Set default configuration (continuous current and voltage measurement) status = INA260_SetConfig(&myConfig); if (status != HAL_OK) return HAL_ERROR; // Set mask/enable configuration status = INA260_SetMaskEnable(&myMask); if (status != HAL_OK) return HAL_ERROR; // Read back the configuration to ensure it is set correctly status = INA260_GetConfig(&myConfig); status = INA260_GetMaskEnable(&myMask); return status; }
View more

Now, I do not get any error and also the HAL_I2C_MasterTxCpltCallback, HAL_I2C_MasterRxCpltCallback and HAL_I2C_ErrorCallback are not triggered.

The data I receive in the init of INA260 is not correct, I have to add the fact that the code is working in non DMA mode.

My scatter file is : 

LR_IROM1 0x08000000 0x00100000 { ; load region size_region ER_IROM1 0x08000000 0x00100000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } RW_IRAM2 0x24000000 0x00020000 { .ANY (+RW +ZI) } ; Added new region DMA_BUFFER 0x38000000 0x00001000 { *(.dma_buffer) } }

 

Any idea how to fix the issue? Thank you.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

DMA happens in the background. You need to wait for the operation to complete before using the values. The HAL_I2C_Mem_Read_DMA function only starts the process. Data isn't in the receiving buffer immediately after that.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

2 REPLIES 2
TDK
Super User

DMA happens in the background. You need to wait for the operation to complete before using the values. The HAL_I2C_Mem_Read_DMA function only starts the process. Data isn't in the receiving buffer immediately after that.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you for yor reply, you are right.