Skip to main content
Visitor II
October 20, 2024
Question

I2C In Master Receive Mode, DMA transfer in circular mode not working

  • October 20, 2024
  • 1 reply
  • 2554 views

Hi I'm using the STM32F446RE with the latest FW Package - SMT32Cube_FW_F4 V1.28.1, and i have a MCP3221 connected to an I2C port.
Its a strange ADC that returns a sample every 2 bytes when its setup to send data to the I2C master. There are no addresses like most I2C devices. 
Since it works in this way i can take advantage of this and I'd like to read it continuously in DMA mode, with no interruptions and so i tried DMA circular mode. The problem is that it runs 1 time trough the recv process and stops after the callback. I'm using CubeMX to setup the peripherals.

I2C init code. 

 /* USER CODE END I2C3_Init 1 */
 hi2c3.Instance = I2C3;
 hi2c3.Init.ClockSpeed = 400000;
 hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
 hi2c3.Init.OwnAddress1 = 0;
 hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
 hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
 hi2c3.Init.OwnAddress2 = 0;
 hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
 hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
 if (HAL_I2C_Init(&hi2c3) != HAL_OK)
 {
 Error_Handler();
 }

DMA Init code. 

 /* I2C3 DMA Init */
 /* I2C3_RX Init */
 hdma_i2c3_rx.Instance = DMA1_Stream1;
 hdma_i2c3_rx.Init.Channel = DMA_CHANNEL_1;
 hdma_i2c3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_i2c3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_i2c3_rx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_i2c3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 hdma_i2c3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
 hdma_i2c3_rx.Init.Mode = DMA_CIRCULAR;
 hdma_i2c3_rx.Init.Priority = DMA_PRIORITY_LOW;
 hdma_i2c3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 if (HAL_DMA_Init(&hdma_i2c3_rx) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(hi2c,hdmarx,hdma_i2c3_rx);

 /* I2C3 interrupt Init */
 HAL_NVIC_SetPriority(I2C3_EV_IRQn, 5, 0);
 HAL_NVIC_EnableIRQ(I2C3_EV_IRQn);
 HAL_NVIC_SetPriority(I2C3_ER_IRQn, 5, 0);
 HAL_NVIC_EnableIRQ(I2C3_ER_IRQn);
 /* USER CODE BEGIN I2C3_MspInit 1 */

 Call code.

static volatile uint8_t data_buff[200*2]; //Global variable.

HAL_I2C_Master_Receive_DMA(&hi2c3, I2C_ADDRESS_ADC, (uint8_t *)data_buff, 200/2);

 

currently my void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) is just blank. I just use it with a breakpoint to check, if it got there. it gets there once and then it stops. 

NMan11_0-1729455206683.png

Currently i have it bodged where i start a new transfer as soon as the previous one ends, but I'd prefer to have it with proper circular mode.
Any help would be highly appreciated.

 

    This topic has been closed for replies.

    1 reply

    Technical Moderator
    October 21, 2024

    Hello @NMan.11 

    First let me thank you for posting.

    The MCP3221 ADC sends data in a two-byte format without a traditional I2C address. Instead, it uses a specific I2C address for the master to initiate communication. The device operates in a way that requires reading two bytes at a time, which is essential for extracting the 12-bit ADC value.

    DMA Configuration:

    • The MemDataAlignment should match the size of the data being received (in this case, DMA_MDATAALIGN_BYTE might be more appropriate since you're reading two bytes at a time)

    I2C Configuration:

    • Verify that your I2C settings are compatible with the MCP3221. The clock speed (400 kHz) is typically fine, but ensure that pull-up resistors are correctly placed on the SDA and SCL lines

    DMA Initialization

    hdma_i2c3_rx.Instance = DMA1_Stream1;
    hdma_i2c3_rx.Init.Channel = DMA_CHANNEL_1;
    hdma_i2c3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_i2c3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // Change to BYTE
    hdma_i2c3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // Change to BYTE
    hdma_i2c3_rx.Init.Mode = DMA_CIRCULAR;
    hdma_i2c3_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_i2c3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    
    if (HAL_DMA_Init(&hdma_i2c3_rx) != HAL_OK) {
     Error_Handler();
    }
    
    __HAL_LINKDMA(hi2c, hdmarx, hdma_i2c3_rx);

    Continuous Reading Logic

    In your main application logic:

    static volatile uint8_t data_buff[200]; // Adjust size based on actual needs
    
    // Start receiving data in circular mode
    HAL_I2C_Master_Receive_DMA(&hi2c3, I2C_ADDRESS_ADC << 1, (uint8_t *)data_buff, sizeof(data_buff));
    
    // Callback function
    void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
     // Process received data from data_buff here if needed
     // No need to restart transfer in circular mode
    }

     Also you can check this post , it could help you .

    THX

    Ghofrane

    NMan.11Author
    Visitor II
    October 21, 2024

    Hi @Ghofrane GSOURI 

    Thank you for the quick response. 

    Now as far as hardware and clock configuration go, they are all fine. I've checked the I2C fronts with a scope and they are steep enough to be read properly. I'm sorry i don't have a capture of the signal.
    I've also using this hardware (pull-up resistors and I2C settings), I'm just retriggering the DMA transfer every time its done, so I'm imitating the circular DMA logic as well as it is possible. 

    I2C_ADDRESS_ADC is actually already shifter.
    Yes i agree about the buffer size, this was just a simple sanity check code and i was using to quickly test everything and i was noodling.

    Unfortunately changing the data alignment, didn't fix the original issue. It just triggers the transfer once and stops. It's acting like normal mode basically.


    Technical Moderator
    October 21, 2024

    Hello again @NMan.11 

    If your DMA transfer for the MCP3221 ADC is only triggering once and then stopping, it suggests that might be an issue with the I2C communication itself. 

    Make sure to follow the correct initialization sequence as per the MCP3221’s requirements:
    Start communication with a Start condition.
    Send the device address with the read bit set.
    Read two bytes with appropriate ACK/NACK handling.
    End communication with a Stop condition