2024-12-22 9:38 AM
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
}
}
Solved! Go to Solution.
2024-12-22 1:35 PM
The reference manual has a description of the registers. You can find the RM on the Documentation page, or in CubeMX:
STM32F0 Series - PDF Documentation
From the RM, you can see the register is write-only:
> After changing this in both instances of setting DMA1->IFCR it still did the same.
I figured.
The reference manual also has instructions on how to do things with direct register access. Doesn't look like you enable RXDMAEN (or TXDMAEN) in the CR1 register anywhere.
2024-12-22 9:49 AM - edited 2024-12-22 10:05 AM
> DMA1->IFCR |= DMA_IFCR_CTCIF2; // Clear transfer complete flag
This clears all flags, not just TCIF2. You want:
DMA1->IFCR = DMA_IFCR_CTCIF2;
Presumably the other flag got cleared by one of these statements causing the mcu to wait forever.
Edit: not so sure of this anymore, but in any case, IFCR should not be read, only written.
2024-12-22 10:16 AM - edited 2024-12-22 10:25 AM
Do you perhaps mean "IFCR should not be 'Written", only "Read"?
I am not sure I understand the issue otherwise?
I am actually having a hard time finding this datasheet that actually shows the registers and not the short version of it.
So I can not see which flag in the register I actually have to reset.
Also the DMA_IFCR_CTCIF2 translates into "(0x1UL << (5U))"
Also the DMA_IFCR_CTCIF3 translates into "(0x1UL << (9U))"
If I = it then it will overwrite all of the register... Is that what I want?
After changing this in both instances of setting DMA1->IFCR it still did the same.
DMA1->IFCR = DMA_IFCR_CTCIF2; // Clear transfer complete flag
DMA1->IFCR = DMA_IFCR_CTCIF3; // Clear transfer complete flag
Thanks
2024-12-22 1:35 PM
The reference manual has a description of the registers. You can find the RM on the Documentation page, or in CubeMX:
STM32F0 Series - PDF Documentation
From the RM, you can see the register is write-only:
> After changing this in both instances of setting DMA1->IFCR it still did the same.
I figured.
The reference manual also has instructions on how to do things with direct register access. Doesn't look like you enable RXDMAEN (or TXDMAEN) in the CR1 register anywhere.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.