I2C DMA endless while loop.
Hi
I am having some issues where my MCU ( stm32f030C8T6 ) seems to get stuck in a while loop when using I2C1 and a AS5600 IC.
I will paste the code below but give some more detail on the issue here.
In the function named "void I2C1_Write_DMA(uint8_t slave_address, uint8_t *data, uint16_t size)" AND "void I2C1_Read_DMA(uint8_t slave_address, uint8_t *data, uint16_t size)" the line "while (!dma_transfer_complete);" makes the MCU get stuck in an Endless loop.
When I use the BusPirate to sniff on the transaction I see that it gives the following (" [0x6C+ ")
(Which ended on ACK) before getting stuck on in the loop.
You can compile and run this code if you happen to have this board laying around.
#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
// Function Prototypes
void Clock_Setup(void);
void DMA_Init(void);
void I2C1_Init(void);
void I2C1_Write_DMA(uint8_t slave_address, uint8_t *data, uint16_t size);
void I2C1_Read_DMA(uint8_t slave_address, uint8_t *data, uint16_t size);
void DMA1_Channel2_3_IRQHandler(void);
// Global Variables
volatile uint8_t dma_transfer_complete = 0;
void GPIO_Setup(void) {
// Enable GPIOC clock
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
// 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)
// Optional: Configure output type and speed
GPIOC->OTYPER &= ~(1U << 13); // Set output type to push-pull
GPIOC->OSPEEDR |= (3U << (13 * 2)); // Set to high speed
}
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 DMA_Init(void) {
RCC->AHBENR |= RCC_AHBENR_DMAEN; // Enable DMA clock
// Enable DMA interrupts in NVIC
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); // Enable DMA1 Channel 2 and 3 interrupt
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 1); // Set interrupt priority
}
void I2C1_Init(void) {
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Enable I2C1 clock
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Enable GPIOB clock
// Configure PB6 (SCL) and PB7 (SDA) as Alternate Function
GPIOB->MODER &= ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7); // Clear mode bits
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 for I2C
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0; // Pull-up resistors
// Configure I2C1
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1 for configuration
I2C1->TIMINGR = 0x2000090E; // Configure timing (100kHz @ 48MHz PCLK)
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C1
}
void I2C1_Write_DMA(uint8_t slave_address, uint8_t *data, uint16_t size) {
// Configure DMA for transmission
DMA1_Channel2->CMAR = (uint32_t)data; // Memory address
DMA1_Channel2->CNDTR = size; // Number of bytes
DMA1_Channel2->CPAR = (uint32_t)&I2C1->TXDR; // Peripheral address
DMA1_Channel2->CCR = 0; // Reset DMA channel configuration
DMA1_Channel2->CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel2->CCR |= DMA_CCR_DIR; // Memory-to-peripheral
DMA1_Channel2->CCR |= DMA_CCR_TCIE; // Transfer complete interrupt
DMA1_Channel2->CCR |= DMA_CCR_EN; // Enable DMA channel
// Configure I2C for write
I2C1->CR2 = 0;
I2C1->CR2 |= (slave_address << 1); // Slave address, write mode
I2C1->CR2 |= (size << I2C_CR2_NBYTES_Pos); // Number of bytes
I2C1->CR2 |= I2C_CR2_START; // Generate START condition
while (!dma_transfer_complete); // Wait for DMA transfer to complete
dma_transfer_complete = 0;
}
void I2C1_Read_DMA(uint8_t slave_address, uint8_t *data, uint16_t size) {
// Configure DMA for reception
DMA1_Channel3->CMAR = (uint32_t)data; // Memory address
DMA1_Channel3->CNDTR = size; // Number of bytes
DMA1_Channel3->CPAR = (uint32_t)&I2C1->RXDR; // Peripheral address
DMA1_Channel3->CCR = 0; // Reset DMA channel configuration
DMA1_Channel3->CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel3->CCR |= DMA_CCR_TCIE; // Transfer complete interrupt
DMA1_Channel3->CCR |= DMA_CCR_EN; // Enable DMA channel
// Configure I2C for read
I2C1->CR2 = 0;
I2C1->CR2 |= (slave_address << 1) | I2C_CR2_RD_WRN; // Slave address, read mode
I2C1->CR2 |= (size << I2C_CR2_NBYTES_Pos); // Number of bytes
I2C1->CR2 |= I2C_CR2_START; // Generate START condition
while (!dma_transfer_complete); // Wait for DMA transfer to complete
dma_transfer_complete = 0;
}
void DMA1_Channel2_3_IRQHandler(void) {
if (DMA1->ISR & DMA_ISR_TCIF2) { // DMA transfer complete for Channel 2 (TX)
DMA1->IFCR |= DMA_IFCR_CTCIF2; // Clear transfer complete flag
DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Disable DMA channel
while (!(I2C1->ISR & I2C_ISR_TC)); // Wait for I2C transfer complete
I2C1->CR2 |= I2C_CR2_STOP; // Generate STOP condition
dma_transfer_complete = 1;
}
if (DMA1->ISR & DMA_ISR_TCIF3) { // DMA transfer complete for Channel 3 (RX)
DMA1->IFCR |= DMA_IFCR_CTCIF3; // Clear transfer complete flag
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable DMA channel
while (!(I2C1->ISR & I2C_ISR_TC)); // Wait for I2C transfer complete
I2C1->CR2 |= I2C_CR2_STOP; // Generate STOP condition
dma_transfer_complete = 1;
}
}
int main(void) {
Clock_Setup(); // Set up system clock
GPIO_Setup();
DMA_Init(); // Initialize DMA
I2C1_Init(); // Initialize I2C1
uint8_t command[1] = { 0x0C }; // Example command to send to Bus Pirate
uint8_t response[4]; // Buffer to store response from Bus Pirate
while (1) {
// Write command to Bus Pirate
I2C1_Write_DMA(0x36, command, 1); // 0x08 is the Bus Pirate's I2C slave address
// Read response from Bus Pirate
I2C1_Read_DMA(0x36, response, 4);
GPIOC->ODR ^= (1U << 13);
// Response processing (add your logic here)
for (volatile int i = 0; i < 100000; i++); // Simple delay
}
}


