2022-10-03 07:08 AM
Hi,
I'm working on a project to generate a 3phase sinusoidal pwm.
F_fundemnetal = 0.1 - 100 Hz
F_sw = 1-100 kHz.
I would like to calculate the PWM values on the fly if possible instead of a lookup table. I am generating an ISR every time TIM1 overflows and am doing my calculations in there.
I'm using STM32F302R8.
IT works fine at 10 kHz, however I have been struggling to get it to work at 100 kHz with more than one phase calculation. I think there is not enough time for the calculations of the following code.
As far as I'm aware, the FPU should do each of these calculations in 14 clock cycles (running at 72 MHz) so that should be plenty of time to do it even at 100 kHz.
In keil I have enabled the floating point hardware to use single precision in the options for target 1 menu. Then when I am initializing the clocks, I should be enabling the FPU with the following command.
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
At this point I'm guessing the FPU is doing the calculations but I am not sure on how to verify this.
Am I missing something? Is the uC/FPU not fast enough for these calculations? Can I somehow optimize the code to make it execute more quickly?
// interrupt occurs at switching frequency
void TIM1_UP_TIM16_IRQHandler(void)
{
TIM1->SR = ~TIM_SR_UIF; // Clear interrupt
if (PWM_index >= nsamples)
{
PWM_index = 1;
}
PWM_U = MaxPWM_value*sinf((6.28315/nsamples)*PWM_index);
PWM_V = MaxPWM_value*sinf((6.28315/nsamples)*PWM_index);
PWM_W = MaxPWM_value*sinf((6.28315/nsamples)*PWM_index);
if (PWM_U <= 0)
{
PWM_U = 0;
}
if (PWM_V <= 0)
{
PWM_V = 0;
}
if (PWM_W <= 0)
{
PWM_W = 0;
}
// //Duty Cycles
TIM1->CCR1 = PWM_U;
TIM1->CCR2 = PWM_V;
TIM1->CCR3 = PWM_W;
PWM_index++;
}
2022-10-03 09:53 AM
Sinus function is definitely not computed by a single instruction, it may take at least tens, if not hundreds of instructions to compute.
2022-10-03 01:07 PM
Hi code seems to generate the same value for the 3 phases...;>))
Stm32f3 have a 32bits fpu (cortex M4).
*Be sure that all code use only float and not double.
*Use 6.28315f (add f to be sure that const is a 32bit float, not a double...)
*Have a look to LST file to track float to double conversion...
*optim level?
*code can be rewritten using precalculated tables and factorizing duplicated code (but avoid call)
*using code in ccmram is possible with stm32f3(not on stm32f4) and can give a little improvement...
*maybe a major rework can use Tim dma to rewrite values to timer from precalculated tables...
2022-10-03 01:11 PM
And try to avoid any float operation in interrupt is generally a good goal...
Not allways recheable...
2022-10-03 10:13 PM
2022-10-04 01:07 AM
I plan to have a look and determine how many clock cycles it takes.
2022-10-04 01:10 AM
Hi, yes the code generates the same values for the 3 phases for now as I just wanted to look at how long the computation took. Once it would be sorted I would add the phase shift.
I will look into ensuring the code is using floats and not doubles.
I'm not sure what an LST file is, could you elaborate on this as it might be helpful for me.
Im tying not to use look up tables as at 100 Hz and 2 kHz I would need 20 values for a sine calculation. at 100 Hz and 100 kHz that is a 1000 so to go from the 20 LUT I would need a lot of interpolation at 100 kHz
2022-10-04 01:10 AM
Thanks for this tip. I'm not a software guy so always learning about this
2022-10-04 01:12 AM
Im tying not to use look up tables as at 100 Hz and 2 kHz I would need 20 values for a sine calculation. at 100 Hz and 100 kHz that is a 1000 so to go from the 20 LUT I would need a lot of interpolation at 100 kHz
Thanks for the polynomial approximation I think that would help a lot. I'll give it a try
2022-10-04 08:09 AM
Be aware that generating ths same value for the 3 phase can be optimised by compiler...
If so, benchmark can be falsly optimistic.
As @Javier Muñoz stated polynomial approx can fasten sinf.
For Motor control i think that stm32f3 or stm32g4 are more suited than stm32f4...
But sw optim can be sufficient...