Challenges going faster than certain rpm

You can use the PIO stuff in the rp2040 to do interrupt free encoder reading. It’s just as you describe, but programmable. The state machine will count the pulses and update a register, then updates a fifo, you read the fifo and you are good. the state machine can also give an interrupt to the main cpu every whatever many pulses or something. Or you can request a reading and grab the latest position or whatever, very flexible and powerful.

It’s too bad they limited the pio program lenght to 32 instructions, or you could make something that output the pwm precursor to the sine wave automatically, you just tell it the frequency and amplitude. That would be nice and free a lot of cpu resources, oh well. There might be a way to do something like that but it would take some skill.

1 Like

Have you written a hardware encoder on the PIO engine?
Sure, it’s possible, but it’s also quite a lot of extra work, when a driver for stm32 is already written…

Or we can add an FPGA to some simpleFOC boards for handling DDS PWM and encoder hardware :stuck_out_tongue:

Yeah but once it’s done anyone can use it. There are probably already pio programs to do it.

By the way, PWM peripherals already work like how you describe → you only control the frequency and amplitude, you are not bit-banging if you’re using the HW peripheral.

I only work at very low speeds, so what I am proposing may be total nonsense, but I would try it if I had to take SimpleFOC to the extremes: Use a tracking filter to compensate the electrical angle for the delay of sensor and code to maximize speed or reduce current. This dynamic compensation should only be active while the motor is at a reasonably steady state. Good filters here could be g-h filters with their tracking properties. If this gives an advantage over a linear lag compensation as proposed by @Anthony_Douglas - I don’t know, but it may help to adjust the linear fit.

you’re right that someone has done it before:

I think the PIO is clocked at system clock, so I guess this should be at 170MHz, about as fast as any STM32 can go.

1 Like

I think the RP2040, at least the Pico, is clocked at 125MHz.

But I think changing MCU is a distraction. The G431 is an excellent MCU to work with, and changing to a Pico would not be faster, in my opinion.

To go faster than the G431 would need a faster STM32, a Teensy or a ESP32.

Sticking with the G431, I think using the STM32HWEncoder to have 0-overhead sensor readings would be the way to go. STM32HWEncoder(ppr, PB6, PB7, PB8); on the G431-ESC1 should do the trick, I think? These pins are all on TIM4 so should be able to use encoder mode of this timer.
So if you can get an AS5047P based sensor board to swap out your AS5048A you could get quite a speed-boost, I think…

I’ll try to find the CORDIC code for you this evening… have to search for it.

1 Like

Thank you so much for all the replies, this is truly helpful !
I have ordered the new AS5047P sensors already, will try that when I get them. And in the meantime I will see what I can do with a linear offset, as suggested by @Anthony_Douglas.
And yes, if I could stick with the same board at least, that’d much easier. Thank you @runger, the CORDIC enhancement sounds like a very good thing to try out.

Would it be imaginable to stop using the sensor completely at higher speeds and instead rely on BEMF measurement or is this non-sense?

Oh, maybe I misunderstood. If the G431 is being used, just keep that! I saw in the top post that the Pico was the board being used here.
I definitely think the STM32HWEncoder is the way to go here.

It’s not nonsense, and the B-G431-ESC1 is set up for voltage measurements in terms of the hardware, IIRC.
But it’s not supported at all in our library at the moment… so in those terms it’s not a quick solution for you.

I believe the combination of ABZ Sensor used via HWEncoder and the CORDIC optimization may get you to your goal.

1 Like

In the PM conversation @Candas1 mentioned in post #17, we were thinking of leveraging SmoothingSensor for this. Add a new variable to apply a fixed offset to the timestamp before calculating the predicted angle. To get good results, you’d have to measure the average time between the sensor update and PWM duty update on your specific hardware.

But if using hardware current sense, you’d ideally want to make two separate angle predictions, or perhaps use the previous frame’s angle for the current transformation. Otherwise you’ll be transforming the start-of-the-frame current readings by the end-of-the-frame angle, and cancel out your improvement. But I’m not sure how to cleanly integrate that into the library code.

1 Like

I tried downsample rates of 22 on my ST32F030, but I didn’t notice any difference.
I also mentioned before, there is a typo in the default.h file (@runger )

// default monitor downsample
#define DEF_MON_DOWNSMAPLE 100 //!< default monitor downsample
#define DEF_MOTION_DOWNSMAPLE 0 //!< default motion downsample - disable

I’ve changed the names, but maybe that’s the reason why it doesn’t work in my case?
Maybe the typo goes on in other files?

Hi all,

I’ve tried to play with the motor.zero_electric_angle value to see whether this would help, as suggested above. And the difference is massive.
Not only do I reach speeds of 1700 rad/s (and possibly beyond), the currents are much lower at high speeds. Main loop is still around 13 kHz with the changes I reported here.

I tweaked the sensor offset manually at every speed so at to get the lowest multimeter-based current draw and then fitted the data as follow:

float normal_zero_angle = 0;
void setup(){
	...
    normal_zero_angle = motor.zero_electric_angle;
}
void loop(){
	static int i = 0;
	i ++;
	
	motor.loopFOC();
	motor.move();
	motor.monitor();
	command.run();
	
	if(i%10 == 0){
		motor.zero_electric_angle=-0.0000007*pow(motor.shaft_velocity_sp,2) + 0.0001*motor.shaft_velocity_sp + normal_zero_angle + 0.2;
	}

Since the new sensor value calculation was slowing down the main loop by 1 kHz, I chose to only update the value every 10 loops.

Pasting here the current measurements with this correction. Note that beforehand, with a static zero_electric_angle, at above 1000 rad/s I was maxing out my power supply at 5A.

3 Likes

As I said you can overcompensate with this

[EDIT] I think as the speed increases the interrupts slow down loopfoc more and more

wow, really impressive result!
how did you plot the offset graph? manually adjusting it or did you setup some kind of automated measurement?

1 Like

All manual, tuning the offset value at every speed point. Since the current measure (which I was trying to minimize) was taken from a multimeter, I couldn’t really automate anything.

@runger Do you guys really need the adc interrupts ? This is probably also slowing down foc_current.

In my gd32 implementation I read the injected adc registers only in getPhaseCurrents function.

[EDIT] Example with STM32F1

Before:
RAM: [= ] 9.1% (used 4456 bytes from 49152 bytes)
Flash: [=== ] 26.8% (used 70256 bytes from 262144 bytes)
loopfoc=296us

After:
RAM: [= ] 9.0% (used 4404 bytes from 49152 bytes)
Flash: [=== ] 26.7% (used 69880 bytes from 262144 bytes)
loopfoc=266us

Same would be possible with the G4 implementation as it’s also using injected adc

[EDIT2] And you will see a difference even in voltage mode, because if current sense is initialized, those interrupts are triggered even if you are not in foc_current mode.

1 Like

This seems like a good idea to me. :slight_smile:

I don’t have an F1 set up to test it at the moment…

Ran it yesterday on a STM32F103RCT6(hoverboard controller), it was working.
Just want to make sure I am not missing something else.

I made it optional so the current setup with interrupts can stay, in case someone want’s to run loopfoc from there in the future.

I have a few G431B-ESC’s also, I really need to set it up so I can also test this on G4.
There will be a lot more people that can test this I imagine.

Just one doubt @runger , there are 2 ways to use current sense with G431.
The G431B-ESC specific one with DMA’s and no interrupt, and the generic G4 implementation that uses interrupts and injected adc?