Skip to main content
Visitor II
September 27, 2024
Solved

STM32H723 I2C MEM DMA not working

  • September 27, 2024
  • 1 reply
  • 1853 views

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 */

 

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;
}

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.

    This topic has been closed for replies.
    Best answer by TDK

    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.

    1 reply

    TDKAnswer
    Super User
    September 27, 2024

    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.

    Visitor II
    October 7, 2024

    Thank you for yor reply, you are right.