2024-09-26 10:09 PM - edited 2024-09-26 10:21 PM
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.
Solved! Go to Solution.
2024-09-27 06:51 AM
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.
2024-09-27 06:51 AM
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.
2024-10-07 12:42 PM
Thank you for yor reply, you are right.