cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F030C8T6 DMA I2C stuck in endless while loop without interrupt

table
Associate

Hi.

I tried using DMA with I2C before using interrupts and I seem to get the same problem that still remains unsolved.
This time around I decided I will try and not use any interrupts to make things a bit simple. However I am still getting the same result and I just can not seem to figure out why my I2C coms get stuck in the middle of a transmission. The Error flag is not going up either.

I am trying to read from the 0x0C (raw_angle) address from a AS5600 IC.
I do this by sending '0x0C' to '0x36'(AS5600 address). On the BusPirate on SNIFFER mode I see the following on the bus line.
[0x6C+
This did not change from the last example. When I take the BusPirate and the AS5600 IC alone and run the command Step1: [0x6C 0x0C]
Step2: [0x6D rr]
I do manage to get some legit bytes referring to what I believe is the raw_angle values.

When I put it all back onto the MCU it just gets stuck WITH OR WITHOUT the BusPirate on the bus.
In the code below the breakpoint will get stuck in the while loop on line 86 " while (!(DMA1->ISR & DMA_ISR_TCIF2)){} // Wait for DMA transfer complete "

I just do not know what I am doing wrong here.

#include <stdint.h>
#include "stm32f030x8.h"

#if !defined(__SOFT_FP__) && defined(__ARM_FP)
  #warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif

void Clock_Setup(void) {
    RCC->CR |= RCC_CR_HSION;                   // Enable High-Speed Internal Clock
    while (!(RCC->CR & RCC_CR_HSIRDY));        // Wait for HSI to stabilize

    RCC->CFGR &= ~RCC_CFGR_SW;                 // Set HSI as SYSCLK
    RCC->CFGR |= RCC_CFGR_SW_HSI;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);

    RCC->CFGR &= ~RCC_CFGR_HPRE;               // AHB Prescaler: Divide by 1
    RCC->CFGR &= ~RCC_CFGR_PPRE;               // APB Prescaler: Divide by 1
}

void Config_Pin13(){
    // Set PC13 as output
    GPIOC->MODER &= ~(3U << (13 * 2)); // Clear MODER13[1:0]
    GPIOC->MODER |= (1U << (13 * 2));  // Set MODER13[1:0] to 01 (General-purpose output mode)
}

void I2C_DMA_Init(void) {
    // Enable clocks for I2C1, DMA, GPIOB
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable DMA clock
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Enable I2C1 clock
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Enable GPIOB clock

    // Configure GPIOB pins for I2C1 (PB6: SCL, PB7: SDA)
    GPIOB->MODER &= ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7); // Clear mode
    GPIOB->MODER |= (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1); // Set to Alternate Function
    GPIOB->AFR[0] |= (1 << GPIO_AFRL_AFRL6_Pos) | (1 << GPIO_AFRL_AFRL7_Pos); // AF1 for I2C1
    GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7; // Open-drain
    GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7; // High speed
    GPIOB->PUPDR |= GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0; // Pull-up

    // Configure I2C1
    I2C1->TIMINGR = 0x20000913; // Configure timing for 8 MHz HSI and 100 kHz I2C
    I2C1->CR1 = I2C_CR1_PE; // Enable I2C1

    // Configure DMA for I2C1
    // TX DMA (I2C1_TX -> Memory to Peripheral)
    DMA1_Channel2->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // Memory increment, Read from memory
    DMA1_Channel2->CPAR = (uint32_t)&I2C1->TXDR; // Peripheral address

    // RX DMA (I2C1_RX -> Peripheral to Memory)
    DMA1_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_TCIE; // Memory increment
    DMA1_Channel3->CPAR = (uint32_t)&I2C1->RXDR; // Peripheral address
    I2C1->CR1 |= (I2C_CR1_RXDMAEN | I2C_CR1_TXDMAEN);
}

void I2C_Read_DMA(uint8_t deviceAddr, uint8_t *data, uint16_t size) {
    // Configure DMA for RX
    DMA1_Channel3->CNDTR = size; // Number of bytes to transfer
    DMA1_Channel3->CMAR = (uint32_t)data; // Memory address
    DMA1_Channel3->CCR |= DMA_CCR_EN; // Enable DMA channel

    // Start I2C transfer
    I2C1->CR2 = (deviceAddr << 1) | (size << 16) | I2C_CR2_RD_WRN | I2C_CR2_START; // Set address, size, read mode, and start condition

    // Wait for transfer to complete
    while (!(DMA1->ISR & DMA_ISR_TCIF3)); // Wait for DMA transfer complete
    DMA1->IFCR = DMA_IFCR_CTCIF3; // Clear transfer complete flag

    // Wait for STOP condition
    while (!(I2C1->ISR & I2C_ISR_STOPF));
    I2C1->ICR = I2C_ICR_STOPCF; // Clear STOP flag

    // Disable DMA channel
    DMA1_Channel3->CCR &= ~DMA_CCR_EN;
}

void I2C_Write_DMA(uint8_t deviceAddr, uint8_t *data, uint16_t size) {
    // Configure DMA for TX
    DMA1_Channel2->CNDTR = size; // Number of bytes to transfer
    DMA1_Channel2->CMAR = (uint32_t)data; // Memory address
    DMA1_Channel2->CCR |= DMA_CCR_EN; // Enable DMA channel

    // Start I2C transfer
    I2C1->CR2 = (deviceAddr << 1) | (size << 16) | I2C_CR2_START; // Set address, size, and start condition

    // Wait for transfer to complete
    while (!(DMA1->ISR & DMA_ISR_TCIF2)){} // Wait for DMA transfer complete
    DMA1->IFCR = DMA_IFCR_CTCIF2; // Clear transfer complete flag

    // Wait for STOP condition
    while (!(I2C1->ISR & I2C_ISR_STOPF));
    I2C1->ICR = I2C_ICR_STOPCF; // Clear STOP flag

    // Disable DMA channel
    DMA1_Channel2->CCR &= ~DMA_CCR_EN;
}


int main(void) {
	uint32_t counter_1 = 0;
	uint32_t counter_2 = 0;
    uint8_t txData[] = {0x0C};
    uint8_t rxData[2];
    Clock_Setup();        // Configure the clock
    Config_Pin13();
    I2C_DMA_Init();

    I2C_Write_DMA(0x36, txData, 1);

    I2C_Read_DMA(0x36, rxData, 2);

    while (1) {
    	counter_1++;
    			if (counter_1 == 10000) {
    				counter_1 = 0;
    				counter_2++;
    				if (counter_2==10){
    					counter_2=0;
    					GPIOC->ODR ^= (1U << 13);
    					I2C_Write_DMA(0x36, txData, 1);
    				}
    			}
    }
}

 

 

7 REPLIES 7
STea
ST Employee

Hello @table ,

may I ask for the reason you are using direct access to registers making it difficult to identify if it was done the right way as DMA config especially requires specific sequence described in Reference manual. using HAL will make it much easier to develop and debug and for such application the cost on memory footprint is negligible unless this is a requirement i recommend you use HAL or LL library which will help us in the debug process and return values of it functions to know for sure what is not correctly implemented.
Regards

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @STea 

 

I Generally do not use HAL because of vendor lock in.
I use various vendors of MCUs.

 

Thanks.

gbm
Lead III

GPIOC is not enabled in your code. STM32 I2C is quite complex when compared to other vendors. The sequence of events and software actions in I2C routines is not that simple. Start with HAL for I2C. Also, DMA for I2C is rarely a good solution - I2C is slow by its nature, so interrupt-based service is reasonable.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

@table wrote:

I Generally do not use HAL because of vendor lock in.


Even you are not using HAL try to inspire from it. At least it will be a good starting point.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

So I enabled GPIOC by enabling the clock for it before setting anything else for PortC?
"RCC->AHBENR |= RCC_AHBENR_GPIOCEN;"

Also the I2C is on GPIOB right? So the GPIOC has to be enabled non the less?

 

Thanks

I just want to get this version working and then I will look into HAL.

 

Thanks

The idea is to inspire from it not forcing you to use it and to speed up your development.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.