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.
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?
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.
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.
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 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?
Just looking to clarify this a bit. As I understand, there are 3 ways in which STM32 can get the ADC data.
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)
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).
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.
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).
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.