cancel
Showing results for 
Search instead for 
Did you mean: 

Fast I2C with DMA transfer issue

LaoMa
Associate III

Hello,

I have successfully implemented the standard I2C protocol with OLED. To speed up the display operations, I increased the speed of the I2C and it works fine up to 290 kHz.

The protocol relies on DMA transfer and the CPU is in SLEEP until the DMA ends and the HAL_I2C_MasterTxCpltCallback() run the HAL_PWR_DisableSleepOnExit() instruction.

Now, like I've told, up to 290 kHz everything's fine, but going 300 kHz and above, the MCU is stuck after to have executed the HAL_PWR_DisableSleepOnExit(): it doesn't exit from sleep (in debug, sending any interaction will do it runs, you see, so I cannot really understand what's happening).

I have tried to change the interrupt priority, but it doesn't matter and I suppose the I2C event handler shall be at higher priority of the DMA handler... I2C has priority 0,0 while DMA 0,1.

Any suggestion? It's ok also to run at 290 kHz, but I wanna know what's going on.

Thanks for any reply.

Maurizio

19 REPLIES 19
Bubbles
ST Employee

Hello @LaoMa​ ,

I2C normally works well up to 1MHz on most STM32 series. What is the exact product you use?

Have you used CubeMX to make the speed configuration? What's the RCC set up like?

Rgds,

Jarda

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.

LaoMa
Associate III

Hi @JHOUD​ ,

yes, I am using the STM32CubeIDE. The MCU is STM32F103C8 and yes, I've used the MX to configure the chip. The only things I have changed are in the NVIC priorities, because the MX sets all peripheral at 0,0 by default (or that's what I see!)

The clock is from HSI/2 with PLL x7 = 28 MHz

Like I have told, the transfer EVER arrives at the end, at 290 kHz as well as at 300 kHz: I've checked with the scope to be sure. And in debug, the bookmark sets at the  HAL_I2C_MasterTxCpltCallback() is correctly checked. but after that, the CPU doesn't exit from Sleep. I give you the 2 main codes:

in the main():

void OLED_Wr_IIC(uint8_t *IIC_Data, uint8_t len) {

HAL_I2C_Master_Transmit_DMA(&hi2c1, OLED_ADD, &IIC_Data[0], len);

// this sequence put the cpu in stop while all peripheral still working

// the CPU is stopped, processing interrupts

HAL_SuspendTick();

HAL_PWR_EnableSleepOnExit(); // conversion ended, resume CPU

HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); //cpu stops until INT

// CPU will go ahead after the HAL_PWR_DiableSleepOnExit() will be executed

HAL_ResumeTick();

}

and the callback does this:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {

// TX Done .. exit from sleep

HAL_PWR_DisableSleepOnExit();

}

Then, I do not understand what's the difference setting in MX the I2C speed at 290 kHz or 300 kHz.

Thanks

Maurizio

LaoMa
Associate III

Hi @JHOUD​ ,

yes, I am using the STM32CubeIDE. The MCU is STM32F103C8 and yes, I've used the MX to configure the chip. The only things I have changed are in the NVIC priorities, because the MX sets all peripheral at 0,0 by default (or that's what I see!)

The clock is from HSI/2 with PLL x7 = 28 MHz

Like I have told, the transfer EVER arrives at the end, at 290 kHz as well as at 300 kHz: I've checked with the scope to be sure. And in debug, the bookmark sets at the  HAL_I2C_MasterTxCpltCallback() is correctly checked. but after that, the CPU doesn't exit from Sleep. I give you the 2 main codes:

in the main():

void OLED_Wr_IIC(uint8_t *IIC_Data, uint8_t len) {

HAL_I2C_Master_Transmit_DMA(&hi2c1, OLED_ADD, &IIC_Data[0], len);

// this sequence put the cpu in stop while all peripheral still working

// the CPU is stopped, processing interrupts

HAL_SuspendTick();

HAL_PWR_EnableSleepOnExit(); // conversion ended, resume CPU

HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); //cpu stops until INT

// CPU will go ahead after the HAL_PWR_DiableSleepOnExit() will be executed

HAL_ResumeTick();

}

and the callback does this:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {

// TX Done .. exit from sleep

HAL_PWR_DisableSleepOnExit();

}

Then, I do not understand what's the difference setting in MX the I2C speed at 290 kHz or 300 kHz.

Thanks

Maurizio

LaoMa
Associate III

Hi @JHOUD​ ,

yes, I am using the STM32CubeIDE. The MCU is STM32F103C8 and yes, I've used the MX to configure the chip. The only things I have changed are in the NVIC priorities, because the MX sets all peripheral at 0,0 by default (or that's what I see!)

The clock is from HSI/2 with PLL x7 = 28 MHz

Like I have told, the transfer EVER arrives at the end, at 290 kHz as well as at 300 kHz: I've checked with the scope to be sure. And in debug, the bookmark sets at the  HAL_I2C_MasterTxCpltCallback() is correctly checked. but after that, the CPU doesn't exit from Sleep. I give you the 2 main codes:

in the main():

void OLED_Wr_IIC(uint8_t *IIC_Data, uint8_t len) {

HAL_I2C_Master_Transmit_DMA(&hi2c1, OLED_ADD, &IIC_Data[0], len);

// this sequence put the cpu in stop while all peripheral still working

// the CPU is stopped, processing interrupts

HAL_SuspendTick();

HAL_PWR_EnableSleepOnExit(); // conversion ended, resume CPU

HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); //cpu stops until INT

// CPU will go ahead after the HAL_PWR_DiableSleepOnExit() will be executed

HAL_ResumeTick();

}

and the callback does this:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {

// TX Done .. exit from sleep

HAL_PWR_DisableSleepOnExit();

}

Then, I do not understand what's the difference setting in MX the I2C speed at 290 kHz or 300 kHz.

Thanks

Maurizio

You can probably try power analysis to see what's actually happening. In this particular case, is it possible that the transmission is finished before the MCU enters sleep mode and then there is no event to wake it? Power analysis would probably show you the timing relation of communication and sleep mode.

J.

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.

LaoMa
Associate III

Hi, @JHOUD​ 

I tried to reply by email but something went wrong.....

I will run the power analysis, but in any case, the minimum length is 3 bytes, address, cmd &data, total 24 bits plus 3 ACK plus start stop sequence, it needs more than 100 microseconds @ 300 kHz; the cpu clock is 28 MHz: are you telling me that the CPU needs more than 100 microseconds to go in sleep after set the DMA for transfer?

and it does the same even if the bytes to transfer are 130, which need nearly 4 milliseconds.....

maybe you're right and the issue is in somewhere which wakes up the CPU, but I cannot buy that it's set to go sleep only after the end of transfer.

look, I'm beginner with STM32, but I'm not in coding..... I do not ask for help at the 1st issue, I investigate before to rise the white flag....

in this case, there is something happening which I have really no guessing.

thanks to support me. 

Maurizio

LaoMa
Associate III

Hello to everybody,

I made code analysis, put breakpoints at every interrupts and nothing appears like it shouldn't be....

does anyone have any idea on what I can do to understand from where the issue come from?

Thanks

Maurizio

LaoMa
Associate III

Hello,

I wanna be back at this issue, that really odd ...

Refreshing the status ....

I use the I2C with DMA transfer and once the transmission has started I put the CPU on sleep and wait the end of transfer; the CPU runs only interrupts (by HAL_PWR_EnableSleepOnExit() before goes sleep). The HAL_I2C_MasterTxCpltCallback() simply executes the HAL_PWR_DisableSleepOnExit() to resume CPU threads.

All these works fine up I2C speed of 290 kHz. When I set higher speeds, the CPU doesn't exit from sleep (it seems the HAL_PWR_DisableSleepOnExit() doesn't have any effect!).

To free the field from doubts, I made several extensive checks but until now nothing and no clues :confounded_face:

What I can affirm is:

  • the polling transfer works fine at any speed, included 400 kHz, then there's no HW errors
  • no other interrupts are triggered except the I2C1_EV_IRQHandler(), the DMA1_Channel6_IRQHandler() and, at the end of transmission, the HAL_I2C_MasterTxCpltCallback(). All those point has been verified by scope and breakpoints
  • the CPU goes in sleep, verified by the current consumptions during run and sleep.
  • the debugger by the ST-Link is not so useful to understand the issue: executing step by step it works (interactions bring the CPU out of sleep!)
  • while sleeping, in DEBUG, pressing the pause (II button), the code is stuck at __WFI instruction, EVEN IF the HAL_PWR_DisableSleepOnExit() in the HAL_I2C_MasterTxCpltCallback() has been executed already, verified by breakpoints. for the routine code, see below.
  • The I2C1_EV_IRQHandler() and the DMA1_Channel6_IRQHandler() have no user code!
  • The NVIC sets DMA1 channel 6 priority at 0,0 and the I2C1 EV priority at 0,1

This the simple callback function:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {

// TX Done .. exit from sleep

HAL_PWR_DisableSleepOnExit();

}

What the hell is the difference between 290 and 300 kHz in the I2C speed? Really no reasons and unfortunately no clues.....

Thanks for any help. It might be it'd need someone from ST tech staff .....

Maurizio

Hello @LaoMa​ ,

I took another look at your case.

The STM32F1 is ancient and relatively primitive device compared to the newer ones I usually work with. I personally never worked with F1 so this did not occur to me. The old I2C of F1 has very simple way of working with clock. Not all I2C clocks are available with any fPCLK1. See description of I2C_CCR register in the RM0008 - for example for 400k the system clock must be a multiple of 10M.

Usual and nominal speeds of I2C are 100k and 400k. The 300k is unusual so it may need some clock fine tuning. Can you try increasing the PLLM to 8? Other idea is to try a different duty cycle. The F1 only supports 2 duty cycles and with speed the I2C duty cycle changes.

I know you wrote that polling works at any speed, but you probably did not try ALL the speeds.

If this does not work, I'll try to get something working with Nucleo boards based on the "I2C_TwoBoards_ComDMA" example.

Best regards,

J.

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.