High resolution encoder missed interrupts caused by _micros()

Not sure if this has been covered already but was using a “high” resolution 10k cpr encoder with a b-g431b-esc1.
It was missing encoder interrupts and not able to get upto full speed (~30hz).
I tracked back the issue to the micros() function inside stm32duino taking too long. It seems to have a fair bit of floating point maths and division so I guess that makes sense.
Replacing _micros() with a dedicated microsecond timer resolved the issue.
This is however a very hardware specific solution and involves modifying simpleFOC.
I’m interested in making a more flexible solution but not sure how to go about it.
Could the function be made weak and then overwritten if needed?
Is there a more flexible way of making timers in STM32duino?
Is this extra complexity for an edge case not worth it for the simpleFOC project?

1 Like

Are you using interrupt-based encoder code or the STM32HWEncoder version? Arduino-FOC-drivers/src/encoders/stm32hwencoder at master · simplefoc/Arduino-FOC-drivers · GitHub

The former is pretty much guaranteed not to work, there is simply too much overhead in the HAL interrupt servicing code. The latter doesn’t use interrupts: it uses the native encoder hardware timer functionality, itself. Some of the code still is poorly optimized (due to the STM32 HAL implementation of the Arduino framework, not SimpleFOC), but should not have problems because all the timing-critical stuff happens in hardware. Especially if you are using a recent version that fixed the velocity calculation code

2 Likes

Thanks Robca, this looks perfect.

I was using the interrupts but it the limiting factor was the micros function but performance in the main loop was being lost at higher speeds.
This looks like an even better solution and I can’t believe I didn’t find it earlier.
Very excited to try it out.

2 Likes

@scouttman Were you able to implement it? I tried but didn’t have a lot of luck. I had to change u_int_ to unit_ in the source code to make it run but it just printed out “0.00 0.00”. My code is as below. Thank you in advance for any of your input.

Blockquote
#include “Arduino.h”
#include “Wire.h”
#include “SPI.h”
#include “SimpleFOC.h”
#include “SimpleFOCDrivers.h”
#include “encoders/stm32hwencoder/STM32HWEncoder.h”
#define ENCODER_PPR 100
#define ENCODER_PIN_A PB6
#define ENCODER_PIN_B PB7
#define ENCODER_PIN_I PB8
STM32HWEncoder encoder = STM32HWEncoder(ENCODER_PPR, ENCODER_PIN_A, ENCODER_PIN_B);
void setup() {
Serial.begin(115200);
encoder.init();
}
void loop() {
Serial.print(encoder.getSensorAngle());
Serial.print(‘\t’);
Serial.println(encoder.getVelocity());
}

Hi,

Which STM32 MCU are you using?

Sometimes it is needed to set the pins in a different way, if you tell me which MCU you’re using we can check it.

If needed, you can set things up also like this:

STM32HWEncoder encoder = STM32HWEncoder(ENCODER_PPR, ENCODER_PIN_A, ENCODER_PIN_B);
void setup() {
  Serial.begin(115200);
  encoder._pinA = PB_6_ALT2; // just an example
  encoder.init();
}

I’m using the B-G431B-ESC1

Hi,

Ok then I think on PB6,PB7 you’re trying to use TIM4.

The correct setup should be:

STM32HWEncoder encoder = STM32HWEncoder(ENCODER_PPR, ENCODER_PIN_A, ENCODER_PIN_B);
void setup() {
  Serial.begin(115200);
  encoder._pinB = PB_7_ALT1; // TIM4 on PB7
  encoder.init();
}