Thursday 13 February 2020

Custom Flight Controller Part 2.6: PWM driver

This post documents how I implemented PWM drivers for STM32. For the complete code, refer to my Github.

A lot of resources are available online about how to enable PWM output by setting up timers in STM32. So this post will not cover these basics. Instead, it mainly documents additional non-trivial steps to make it work in my specific case.

My STM32CubeMX setup for PWM output from TIM1 is as follows:
My system clock frequency is set as 64Mhz. By setting the prescaler to 64, we want our timer counter to increase every 1 micro-second. By setting the counter period to 5000. We are telling the counter to reset every 5 millisecond. This will produce an output PWM frequency of 200Hz which is acceptable by my ESC.

Note that the PWM input to ESC is different from normal PWM for other purposes. Input PWM to ESC must have a pulse width between 1ms to 2ms. This correspond to duty cycle from 20% to 40% (as my PWM period is 5ms) in contrast to normal PWM where duty cycle ranges from 0% to 100%.

In STM32, the way to control PWM pulse width is to set the value of CCR register of TIM1. If the current timer counter reaches the value stored in CCR register, the output voltage will go to LOW until the next cycle starts when the output voltage goes to HIGH. Therefore, in my case, my minimum CCR value is 1000 and maximum is 2000. In the code, I will always add the minimum value to the input so the input to the function should range from 0~1000 which to user, can be understood as duty cycle times 10. 

void PWM_SetDutyCycle(PWMChannelType channel, int dutyCycle)
{
    LOG("PWM channel %d, dutycycle %d\r\n", channel, dutyCycle);
    if (dutyCycle > UAV_MOTOR_MAX_DUTYCYCLE) dutyCycle = UAV_MOTOR_MAX_DUTYCYCLE;
    if (dutyCycle < UAV_MOTOR_MIN_DUTYCYCLE) dutyCycle = UAV_MOTOR_MIN_DUTYCYCLE;

    switch (channel) {
    case PWM_CHANNEL_1:
        htim1.Instance->CCR1 = dutyCycle + DUTYCYCLE_MIN;
        break;
    case PWM_CHANNEL_2:
        htim1.Instance->CCR2 = dutyCycle + DUTYCYCLE_MIN;
        break;
    case PWM_CHANNEL_3:
        htim1.Instance->CCR3 = dutyCycle + DUTYCYCLE_MIN;
        break;
    case PWM_CHANNEL_4:
        htim1.Instance->CCR4 = dutyCycle + DUTYCYCLE_MIN;
        break;
    }
}

No comments:

Post a Comment