I2C Interrupt issue when using low side current sense on Nucleo 64

Hello,

We are trying to implement gyro stabilization using the sFOC shield. The setup is as follows :

  • sFOC shield version : 2.0.4
  • Driver board : Nucleo-F446RE
  • sFOC version : 2.3.2
  • Controller mode : Torque mode in FOC current mode
  • Current sensing : Low side current sensing

As seen below, the master reads gyro measurements and commands Iq setpoints to the driver using the I2C interface. We are using the Wire library for the I2C interface, which calls the command.run() after the bytes are read. The commanding of Iq setpoints on the I2C happens at 1 kHz, which triggers an I2C interrupt and then executes the handler.

We are toggling a GPIO on the Nucleo to verify that the interrupt hits. When we use inline current sensing, the I2C interrupt hits every 1ms as expected, but with the low side current sensing, the I2C interrupt hits initially a few times after which the I2C line becomes busy and hangs.

Since, loopFOC() takes around ~130us with inline current sensing, while it takes around 25us with low side current sensing, we tried to enforce a delay of 75us after loopFOC() executes, but that did not help.

Would appreciate any suggestions on this.

Thanks for the great work and all the support.

Tried a few things to resolve this :

  • Updated sfoc version to 2.3.4
  • Enforced a delay in loop() function so that loopFOC() gets called every 100us.
  • Set PWM freq to 20kHz

Still finding that the I2C interrupt hits a few times and then the I2C line stays busy. Since the low side sensing uses a DMA, could this have something to do with the DMA interrupt nesting with the I2C interrupts?

Low side current sense is not using DMA on F4.
It’s not even using an interrupt if you use one of the advanced timers (TIM1 or TIM8).

I see. What puzzles is me that the I2C interrupts hit at 1kHz when using the inline current sensing, but hit initially for a few times and then the I2C bus hangs when using low side current sensing. If this was an I2C bus issue by itself, I would expect it to not work in the inline sensing config.

But is i2c running fine without loopfoc?

Delaying loopfoc is not a good idea.
You could sample the imu every 10 loopfoc for example.

No I mean with inline current sensing, I2C interrupts hit at 1kHz on the Nucleo which is running loopFOC. With low side sensing, the interrupts hit initially a few times then the bus hangs. We are checking the timing of the interrupts by toggling a GPIO in the callback. loopFOC is always running in both cases.
The difference I’ve noticed is that loopFOC takes 130us with inline sensing, and takes 25us(!) with low side sensing. Tried enforcing a delay of 75us to see if the small compute time of 25us was doing something, but that did not work.

I mean that if you use in-line current sensing but without running loopfoc, you will know if speed is the problem.

The current loop runs fine in both inline and low side sensing, with low side sensing executing much faster. The I2C interrupts are used to send gyro information to the Nucleo at 1kHz (on which only loopFOC is running). With inline sensing, the interrupts keep occuring at 1kHz without issues. However, with low side sensing, the interrupts trigger for a few times and then the I2C bus hangs (no more interrupts occur). Not sure, but does this appear to be a nested interrupt situation? and why it does not occur with inline sensing. Hope this clarifies the situation. Thanks for the inputs.

But have you checked this

I see the following lines :

But not sure where SIMPLEFOC_STM32_ADC_INTERRUPT is being #defined though.

But using this build flag will make SIMPLEFOC use the INTERRUPT for adc which you don’t want right?
By default it’s not used, unless it has to because you are not using TIM1 or TIM8. You will be informed about that if you use the debug flag for stm.

Apology for the naive question, but do I #define the SIMPLEFOC_STM32_DEBUG in SimpleFOC.h?

Looking at the documentation here (Low-Side Current Sense | Arduino-FOC), this says STM32F4 uses DMA for ADC conversion? Or am I missing something here?

No, you have to add it as build flag in the environment.

For platformio this is easy in the platformio.ini, for ArduinoIDE you have modify the board or framework files, you can find instructions online.

Just looking to clarify this a bit. As I understand, there are 3 ways in which STM32 can get the ADC data.

  1. Direct ADC read with interrupt : In this case, the processor is interrupted after every ADC conversion. Since the ADC conversion happens very fast, the processor may get not get enough cycles to do the main tasks. This is what is referred to as the ‘interrupt based ADC conversion’ in the doc (Low-Side Current Sense | Arduino-FOC)
  2. Use DMA to handle the ADC without interrupt : In this case, the DMA is used to transfer the data from the ADC to memory. No interrupt is triggered after the DMA transfer, and the processor simply reads the memory region to obtain the ADC value. I assume this is what is referred to as ‘DMA based ADC conversion’ in the doc (Low-Side Current Sense | Arduino-FOC).
  3. Use DMA to handle the ADC with interrupt : Same as 2, except that an interrupt is triggered after every DMA transaction. My understanding is this method is not being used.

Is there something wrong in my interpretation here?

I already replied here.
We use Injected ADC in particular, it has it’s own registers for keeping the samples.
We use ADC + interrupt with basic timers. Reason is with low side current sensing we need to sample only once during the pwm period, so in this case downsampling is done in the code.
We use ADC without interrupt with advanced timers (tim1 or tim8 on f4), because here we use the repetition counter to ensure sampling is only done once. So no interrupt is needed.
Interrupt usage can still be forced with a build flag in case you need the interrupt for running other code.

Got it, thank you. So I need to check if the timers being used are the basic timers or the advanced timers. That would indicate if the ADC interrupts are being used.

Ok, we are using the simpleFOC shield v2.0.4 along with the Nucleo F446RE, and the PWM pins are set to 5 (pwmA), 9 (pwmB), 6 (pwmC).

From here : F446R(C-E)T Pinout | STM32 Pinouts , it seems that these pins would correspond to :
pwmA (pin 5) : TIM3
pwmB (pin 9) : TIM3
pwmC (pin 6) : TIM2

So that means the ADC + interrupt is being used? Since we are using the shield, probably cannot change the PWM pins to TIM1.

Finally, Low-Side Current Sense | Arduino-FOC mentions the use of DMA in low side sensing STM32F4, not sure how to interpret this.

In any case, going back to the original issue, whether the ADC interrupts are interfering with the I2C interrupts when using low side sensing is still open. As mentioned before, the I2C interrupts hit consistently when using inline sensing (for 3+ hours without issue).

Thank you for the replies.

This is not relevant for you.

You could try forcing this to = 0.

This means sampling will happen twice per PWM period with no interrupt, which shouldn’t be a problem if the hardware has inline current sensing.

But I still don’t know why having interrupts on I2C and ADC would be a problem.

Ideally in a new release we would need to implement faster inline current sensing for stm32 without analogread() and without downsampling.

Set use_adc_interrupt = 0; in stm32f4_mcu.cpp#L67, and now the I2C interrupts are hitting at 1kHz while using low side sensing. Maybe something to do with interrupt nesting or priorities?

In any case, marking above as solution. Thanks for your help.

Your main loop should be running even faster without the interrupt.

But I am still surprised you have a problem with the interrupt.
We are running very little code in it with highest preemption.

How does your i2c code look like?