cancel
Showing results for 
Search instead for 
Did you mean: 

Is there a guide on how to use I2C with DMA?

Matth1
Associate III

Bonjour,

I've been able to use I2C to communicate with I/O extender and display drivers with HAL. I'm now trying to use the I2C with HAL using DMA.

The problem is that UM1785 Description of STM32F0 HAL and low-layer drivers is a bit vague on explanations. STM32 offers examples, but that's not really what I'm looking for, and videos, but I'd rather have a written article (with a written article I can read at my own speed, easily skip or re-read parts, copy text, and so on).

Are their learning tools such as UMs or Application notes dedicated to the subject?

M.R.

6 REPLIES 6
TDK
Guru

There are examples you can follow along with. Reasonably documented in code comments and the readme.

https://github.com/STMicroelectronics/STM32CubeF0/blob/4390ff6bfb693104cf97192f98c3dc9e3a7c296a/Projects/STM32F072B-Discovery/Examples/I2C/I2C_TwoBoards_ComDMA/Src/main.c

If you feel a post has answered your question, please click "Accept as Solution".
Imen.D
ST Employee

Hello @Matth​ and welcome to the Community :)

You will find a working I2C_TwoBoards_ ComDMA examples (relevant to the device that you are using) provided with STM32CubeF0 MCU package

The list of examples provided in the table 2, on the AN4735 Application note STM32Cube firmware examples for STM32F0 Series;

Have a look also at this FAQ: STM32 I2C does not work, which describes general tips related to I2C on STM32 devices.

When your question is answered, please close this topic by choosing Select as Best. This will help other users find that answer faster.

Imen

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen

Thanks for the answer.

However, I'm afraid learning directly from an example would be an exercise in cargo cult programming, where one wanting to learn will have to copy/paste the whole project and snip bit by bit what seems to be specific to the example (communication between two boards) to keep the parts relevant to the subject (I2C with DMA).

I'd rather have an article focussed on a subject, in this case I2C with DMA.

First part gives a synthetic view of the processes, such as what the https://community.st.com/s/article/stm32-i2c-does-not-work article provides.

Then the MCU configuration could be addressed, with explanations of the different setting possibilities (with pros and cons). In such a case, how to add DMA settings to I2C configuration, etc.

Then a synthetic description of the main.c code what is in main(), what is in the infinite loop. Also a part on how settings can be changed on the fly if needed (peripherals with different speeds or addressing?) and of course the proper I2C transmitting and receiving functions.

It can be helpful for programmers who are not yet familiar with MCU.

Such an article would give away to make things work, to understand how it works, and where to look for for if on wants to make the process more efficient.

Looking for the same thing just now, I came across this old thread.

I know @Matth1 didn't want videos, but these two do seem to cover how to set up a comms peripheral with DMA:

STM32CubeMX basics: 10.11 STM32Cube HAL labs UART - UART DMA: 

https://www.youtube.com/watch?v=BxFYmBYBmaU

STM32CubeMX basics: 11.3 STM32Cube HAL labs SPI - Lab SPI DMA:

https://www.youtube.com/watch?v=s9GCTLd6-AM&list=PLnMKNibPkDnGtuIl5v0CvC81Am7SKpj02&index=52

 

There doesn't seem to be one specifically for I2C, but the process should be the same?

Hi TDK,

I am also trying to integrate my IMU with stm32f103c8t6 using I2C DMA but facing a lot of issues, can you help me out or can you give me any references? 

Hi Piyoosh,

Same here, i am facing a lot of trouble trying to read the MPU6050 with STM32F103C6T6 using DMA. I think that i made it work well the transmitter, because i see the flags and seems fine, but the receiver is still wrong. Here is my code if can help you.

#include "dma_i2c.h" uint8_t g_rx_cmplt; uint8_t g_tx_cmplt; static void i2c_start(void) { I2C1->CR1 |= CR1_START; while(!(I2C1->SR1 & SR1_SB)){} } static void send_address(uint8_t address, uint8_t mode) { i2c_start(); I2C1->DR = ((address << 1)|(mode)); while(!(I2C1->SR1 & SR1_ADDR)){} } void I2C1_init(void) { RCC->APB2ENR |= APB2ENR_IOPBEN; // Enable clock access to GPIOB Port RCC->APB2ENR |= APB2ENR_AFIOEN; // Alternate Function I/O clock enabled GPIOB->CRL |= (1U << 24); // MODE6[0] = 1 Output mode, max speed 10 MHz, PB6, I2C1_SCL GPIOB->CRL |= (1U << 25); // MODE6[1] = 0 GPIOB->CRL |= (1U << 26); // CNF6[0] = 1 Alternate function output Open-drain GPIOB->CRL |= (1U << 27); // CNF6[1] = 1 GPIOB->CRL |= (1U << 28); // MODE7[0] = 1 Output mode, max speed 10 MHz, PB7, I2C1_SDA GPIOB->CRL |= (1U << 29); // MODE7[1] = 0 GPIOB->CRL |= (1U << 30); // CNF7[0] = 1 Alternate function output Open-drain GPIOB->CRL |= (1U << 31); // CNF7[1] = 1 RCC->APB1ENR |= APB1ENR_I2C1EN; // Enable clock access to I2C1 peripheral I2C1->CR1 = CR1_SWRST; // I2C Peripheral under reset state I2C1->CR1 &= ~CR1_SWRST; // I2C Peripheral not under reset I2C1->CR1 &= ~CR1_NOSTRETCH; // Clock stretching enabled I2C1->CR1 &= ~CR1_ENGC; // General call disabled. Address 00h is NACKed. I2C1->CR2 |= CR2_DMAEN; // DMA request enabled when TxE=1 or RxNE =1 I2C1->CR2 |= CR2_LAST; // Next DMA EOT is the last transfer I2C1->CR2 |= (FREQ << 0); // Peripheral Clock = APB Clock = System Clock = 8MHz I2C1->CCR |= (I2C_MODE << 0); // Set I2C1 to standard mode, 100KHz clock I2C1->TRISE = I2C_MAX_RISE_TIME; // Set I2C1 max rise time I2C1->CR1 |= CR1_PE; // Peripheral enable } void DMA1_Channel6_I2C1_TX_init(void) { RCC->AHBENR |= AHBENR_DMA1EN; // Enable clock access to the DMA1 module DMA1_Channel6->CCR = 0; // Disable DMA channel while(DMA1_Channel6->CCR & CCR_EN){} // Wait until channel is disabled DMA1_Channel6->CCR |= CCR_MINC; // Memory increment mode enabled DMA1_Channel6->CCR |= CCR_TCIE; // Transfer complete interrupt enable DMA1_Channel6->CCR |= CCR_DIR; // Read from memory NVIC_EnableIRQ(DMA1_Channel6_IRQn); // Enable channel 6 TCI in NVIC } void DMA1_Channel7_I2C1_RX_init(void) { RCC->AHBENR |= AHBENR_DMA1EN; // Enable clock access to the DMA1 module DMA1_Channel7->CCR = 0; // Disable DMA channel while(DMA1_Channel7->CCR & CCR_EN){} // Wait until channel is disabled DMA1_Channel7->CCR |= CCR_MINC; // Memory increment mode enabled DMA1_Channel7->CCR |= CCR_TCIE; // Transfer complete interrupt enable DMA1_Channel7->CCR &= ~CCR_DIR; // Read from peripheral NVIC_EnableIRQ(DMA1_Channel7_IRQn); // Enable channel 7 TCI in NVIC } void i2c_write(uint8_t slave_address, uint8_t *p_write_buff, uint16_t num_of_bytes) { DMA1->IFCR |= IFCR_CTCIF6; DMA1_Channel6->CPAR = (uint32_t)(&(I2C1->DR)); DMA1_Channel6->CMAR = (uint32_t)p_write_buff; DMA1_Channel6->CNDTR = num_of_bytes; I2C1->CR2 |= CR2_DMAEN; while(I2C1->SR2 & SR2_BUSY){} send_address(slave_address, WRITE); (void)I2C1->SR2; DMA1_Channel6->CCR |= CCR_EN; while (!g_tx_cmplt) {} I2C1->CR1 |= CR1_STOP; g_tx_cmplt = 0; } void i2c_read(uint8_t slave_address, uint8_t reg, uint8_t *buffer_rx, uint16_t num_of_bytes) //STILL DONT WORK { DMA1->IFCR |= IFCR_CTCIF7; DMA1_Channel7->CPAR = (uint32_t)(&(I2C1->DR)); DMA1_Channel7->CMAR = (uint32_t)buffer_rx; DMA1_Channel7->CNDTR = num_of_bytes; I2C1->CR2 &= ~CR2_LAST; while(I2C1->SR2 & SR2_BUSY){} send_address(slave_address, WRITE); (void)I2C1->SR2; while(!(I2C1->SR1 & SR1_TXE)){} I2C1->DR = reg; while(!(I2C1->SR1 & SR1_BTF)){} send_address(slave_address, READ); (void)I2C1->SR2; if (num_of_bytes > 1) { I2C1->CR1 |= CR1_ACK; } else { I2C1->CR1 &= ~CR1_ACK; } if (num_of_bytes == 2) { I2C1->CR2 |= CR2_LAST; } I2C1->CR2 |= CR2_DMAEN; DMA1_Channel7->CCR |= CCR_EN; while (!g_rx_cmplt) {} g_rx_cmplt = 0; } void DMA1_Channel6_IRQHandler(void) { if(DMA1->ISR & ISR_TCIF6) { DMA1->IFCR |= IFCR_CTCIF6; I2C1->CR2 &= ~CR2_DMAEN; DMA1_Channel6->CCR &= ~CCR_EN; while(DMA1_Channel6->CCR & CCR_EN){} g_tx_cmplt = 1; } } void DMA1_Channel7_IRQHandler(void) { if(DMA1->ISR & ISR_TCIF7) { I2C1->CR1 |= CR1_STOP; DMA1->IFCR |= IFCR_CTCIF7; I2C1->CR2 &= ~CR2_DMAEN; I2C1->CR2 &= ~CR2_LAST; DMA1_Channel7->CCR &= ~CCR_EN; while(DMA1_Channel7->CCR & CCR_EN){} g_rx_cmplt = 1; } }
View more

 I think that the trick is in something related to the set of the DMA enable bits at some point, exactly.