3
$\begingroup$

I have a F450 Quadcopter frame with BLDC motors of 1000Kv and ESC (simonk) 30A each. I am trying to verify if the bldc motor rpm output at a commanded rpm is same as commanded rpm. I am using STM32F412zg Nucleo-144 microcontroller for the same. Code to generate PWM is written to feed to the ESCs at 4-10% DutyCycle. I made sure the RPMS fed to ESCs are same.

To check if the commanded rpm and motor output rpm's are same, I use a contact tachometer (Digital and stroboscope) which gives me the readings of the motor rpm.

So far I have tested for three different commanded RPMs and checked the RPMs at the Motor,

sl Input-RPM M1 M2 M3 M4

1 1500 2206 1950 1650 1890
2 2000 2450 2100 1810 2050
3 2500 2700 2590 2450 2600

Now, We could clearly see the motors generating speeds more than the commanded RPM and equally important not all the motors generate the same RPM.

My Question is why would the motors not spin at the same RPMs. What are the possible reasons for this. What could be done to resolve the issue of spinning the motors at same RPMs.

Thanks

Edit-1: Adding pwm code as per comment from @ifconfig.

pwm.c

#include "main.h"
#include "tim.h"
#include "gpio.h"

#define MinDutyCycle 321 // 4% - Min dutycycle based on prescaler value
#define MaxDutyCycle 766 // 10% - Max dutycycle based on prescaler value 
#define MinDutyCycleRange 380 //to be tested for 1000 RPM
#define DisArmingDcycle 321
#define MinRpmSpeed 1100.0
#define MaxRpmRange 5000.0
#define MaxRpmSpeed 11600.0 //depends on battery voltage


void EscCalibrationMax(void)
{
    
    HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
    
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);

}
void EscCalibrationMin(void)
{
        htim4.Instance->CCR1 = 321;
        htim4.Instance->CCR2 = 321;
        htim3.Instance->CCR1 = 321;
        htim3.Instance->CCR2 = 321;
        HAL_Delay(5000);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
        //SetEscCalibrationFlag();
        EscCalibrationStatus = true;
}

uint8_t MotorsArming()
{
    uint8_t ArmingStatus = '1';
    if(EscCalibrationStatus == true)
    {   
            htim4.Instance->CCR1 = MinDutyCycleRange;
            htim4.Instance->CCR2 = MinDutyCycleRange;
            htim3.Instance->CCR1 = MinDutyCycleRange;
            htim3.Instance->CCR2 = MinDutyCycleRange;
            ArmingStatus = '6';
            EscCalibrationStatus = false;
    }
    return ArmingStatus;
}

uint8_t MotorsDisArming(uint8_t DisArmCommand)
{
    uint8_t DisArmingStatus = '1';
    if(DisArmCommand == '7')
    {
        htim4.Instance->CCR1 = DisArmingDcycle;
        htim4.Instance->CCR2 = DisArmingDcycle;
        htim3.Instance->CCR1 = DisArmingDcycle;
        htim3.Instance->CCR2 = DisArmingDcycle;
        DisArmingStatus = '7';
    }
    return DisArmingStatus;
}


void DesiredRpmToDcycleM1(float DesiredRpm, uint16_t *DesiredDcycle)
{
    if(DesiredRpm > MaxRpmRange)
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((MaxRpmRange/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
    else
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((DesiredRpm/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
    CheckDcycle = *DesiredDcycle;
    DesRPM = (MaxRpmRange/MaxRpmSpeed);
}

void DesiredRpmToDcycleM2(float DesiredRpm,uint16_t *DesiredDcycle)
{
    if(DesiredRpm > MaxRpmRange)
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((MaxRpmRange/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
    else
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((DesiredRpm/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
}

void DesiredRpmToDcycleM3(float DesiredRpm,uint16_t *DesiredDcycle)
{
    if(DesiredRpm > MaxRpmRange)
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((MaxRpmRange/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
    else
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((DesiredRpm/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
}

void DesiredRpmToDcycleM4(float DesiredRpm,uint16_t *DesiredDcycle)
{
    if(DesiredRpm > MaxRpmRange)
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((MaxRpmRange/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
    else
    {
        *DesiredDcycle = MinDutyCycle + (uint16_t)((DesiredRpm/MaxRpmSpeed)*(MaxDutyCycle - MinDutyCycle));
    }
}

/*Funtion to set Desired Duty Cycle for 4 channels of TIM4*/
void PWMSetRPM(uint16_t DesiredDCycle1,uint16_t DesiredDCycle2,uint16_t DesiredDCycle3,uint16_t DesiredDCycle4)
{
        if(DesiredDCycle1 > MaxDutyCycle){
            DesiredDCycle1 = MaxDutyCycle;
        }else if(DesiredDCycle1 <= MinDutyCycle){
            DesiredDCycle1 = MinDutyCycle;
        }else{
            DesiredDCycle1 = DesiredDCycle1;
        }

        if(DesiredDCycle2 > MaxDutyCycle){
            DesiredDCycle2 = MaxDutyCycle;
        }else if(DesiredDCycle2 <= MinDutyCycle){
            DesiredDCycle2 = MinDutyCycle;
        }else{
            DesiredDCycle2 = DesiredDCycle2;
        }


        if(DesiredDCycle3 > MaxDutyCycle){
            DesiredDCycle3 = MaxDutyCycle;
        }else if(DesiredDCycle3 <= MinDutyCycle){
            DesiredDCycle3 = MinDutyCycle;
        }else{
            DesiredDCycle3 = DesiredDCycle3;
        }

        if(DesiredDCycle4 > MaxDutyCycle){
            DesiredDCycle4 = MaxDutyCycle;
        }else if(DesiredDCycle4 <= MinDutyCycle){
            DesiredDCycle4 = MinDutyCycle;
        }else{
            DesiredDCycle4 = DesiredDCycle4;
        }
    if(GetDisArmStatus() != true)
    {
        /*Writing Duty Cycle to be applied to each motor through CCR registers of 4 channels*/
        htim4.Instance->CCR1 = DesiredDCycle1;
        htim4.Instance->CCR2 = DesiredDCycle2;
        htim3.Instance->CCR1 = DesiredDCycle3;
        htim3.Instance->CCR2 = DesiredDCycle4;
    }
}

void RpmToMotors(uint16_t RPMToApply1,uint16_t RPMToApply2,uint16_t RPMToApply3,uint16_t RPMToApply4)
{
    uint16_t DesiredDCycle1;
    uint16_t DesiredDCycle2;
    uint16_t DesiredDCycle3;
    uint16_t DesiredDCycle4;

    DesiredRpmToDcycleM1((float)RPMToApply1,&DesiredDCycle1);
    DesiredRpmToDcycleM2((float)RPMToApply2,&DesiredDCycle2);
    DesiredRpmToDcycleM3((float)RPMToApply3,&DesiredDCycle3);
    DesiredRpmToDcycleM4((float)RPMToApply4,&DesiredDCycle4);
    
    PWMSetRPM(DesiredDCycle1,DesiredDCycle2,DesiredDCycle3,DesiredDCycle4);
}

tim.h

/**
  ******************************************************************************
  * File Name          : TIM.c
  * Description        : This file provides code for the configuration
  *                      of the TIM instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "tim.h"

/* USER CODE BEGIN 0 */



/* USER CODE END 0 */

TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;

/* TIM3 init function */
void MX_TIM3_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 255;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 7811;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 766;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim3);

}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 255;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 7811;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 766;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim4);

}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
{

  if(tim_pwmHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */



  /* USER CODE END TIM3_MspInit 0 */
    /* TIM3 clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();
  /* USER CODE BEGIN TIM3_MspInit 1 */



  /* USER CODE END TIM3_MspInit 1 */
  }
  else if(tim_pwmHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspInit 0 */



  /* USER CODE END TIM4_MspInit 0 */
    /* TIM4 clock enable */
    __HAL_RCC_TIM4_CLK_ENABLE();
  /* USER CODE BEGIN TIM4_MspInit 1 */



  /* USER CODE END TIM4_MspInit 1 */
  }
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspPostInit 0 */



  /* USER CODE END TIM3_MspPostInit 0 */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM3 GPIO Configuration
    PA6     ------> TIM3_CH1
    PA7     ------> TIM3_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM3_MspPostInit 1 */



  /* USER CODE END TIM3_MspPostInit 1 */
  }
  else if(timHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspPostInit 0 */



  /* USER CODE END TIM4_MspPostInit 0 */

    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**TIM4 GPIO Configuration
    PD12     ------> TIM4_CH1
    PD13     ------> TIM4_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM4_MspPostInit 1 */



  /* USER CODE END TIM4_MspPostInit 1 */
  }

}

void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle)
{

  if(tim_pwmHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspDeInit 0 */



  /* USER CODE END TIM3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM3_CLK_DISABLE();
  /* USER CODE BEGIN TIM3_MspDeInit 1 */



  /* USER CODE END TIM3_MspDeInit 1 */
  }
  else if(tim_pwmHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspDeInit 0 */



  /* USER CODE END TIM4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM4_CLK_DISABLE();
  /* USER CODE BEGIN TIM4_MspDeInit 1 */



  /* USER CODE END TIM4_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */



/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

main.c

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */

  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();

  /* USER CODE BEGIN 2 */

  time_analysis[0] = HAL_RCC_GetHCLKFreq();
  time_analysis[1] = HAL_RCC_GetPCLK1Freq();
  time_analysis[2] = HAL_RCC_GetPCLK2Freq();

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0,GPIO_PIN_SET);

  EscCalibrationMax();
  for(uint8_t i = 0; i< 4; i++){
         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);
         HAL_Delay(100);//testing purpose
  }

  //connect battery and push the user button
  while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) != GPIO_PIN_RESET)){
  EscCalibrationMin();
  }

RpmToMotors(1500,1500,1500,1500);
RpmToMotors(0,0,0,0);

 osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();
  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
$\endgroup$
5
  • $\begingroup$ Could you share some more details/code about how you've implemented the PWM output from the STM32 MCU? This reads like a PWM generation sync issue to me. $\endgroup$
    – ifconfig
    Apr 12 at 6:53
  • $\begingroup$ Did you calibrate the ESCs? $\endgroup$ Apr 12 at 7:02
  • $\begingroup$ @KritikAgrawal Yes. I have calibrated the Escs. $\endgroup$
    – TheLazy
    Apr 12 at 7:04
  • $\begingroup$ @ifconfig added the pwm generation code. I am actually confused if it is PWM generation issue or hardware issue. $\endgroup$
    – TheLazy
    Apr 15 at 7:03
  • 1
    $\begingroup$ Welp. I looked through the code and I couldn't find any issues with it at first glance. Granted, I only have a few hours of experience writing STM32Cube HAL code, so my analysis is frankly not worth much. Also, you can probably strip out the section for tim.h because it's an ST-provided header and not something you wrote yourself. $\endgroup$
    – ifconfig
    Apr 16 at 0:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.