使用STM32 HAL定时器和调整PWM信号的占空比

Using STM32 HAL Timer and Adjusting the Duty Cycle of a PWM signal

我使用STM32Cube初始化代码生成器生成了一个初始化的Timer函数。为了生成固定占空比 PWM 信号,我将 HAL_TIM_Base_Start(&htim1); //Starts the TIM Base generationHAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1)//Starts the PWM signal generation 添加到定时器初始化函数中,如下所示。

/* Private variables ---------------------------------------------------------*/
int pulse_width=0;

/* TIM1 init function */
static void MX_TIM1_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;//we want a max frequency for timer, so we set prescaller to 0         
  //And our timer will have tick frequency
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 1066;//max value for timer is 16bit = 65535, TIM_Period = timer_tick_frequency / PWM_frequency - 1  
  //In our case, for 15Khz PWM_frequency, set Period to TIM_Period = 16MHz / 15KHz - 1 = 1066
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)/* to use the Timer to generate a simple time base for TIM1 */
  {
    Error_Handler();
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//the default clock is the internal clock from the APBx, using this function
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)//Initializes the TIM PWM Time Base according to the specified
//parameters in the TIM_HandleTypeDef and create the associated handle.
  {
    Error_Handler();
  }

  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  //sConfig: TIM PWM configuration structure
  //set duty cycle: pulse_length = ((1066 + 1) * duty_cycle) / (100 - 1)
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = pulse_width;/* 50% duty cycle is 538, set to 0 initially*///
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }

  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_TIM_MspPostInit(&htim1);//output pin assignment
    HAL_TIM_Base_Start(&htim1); //Starts the TIM Base generation
  if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)//Starts the PWM signal generation
  {
    /* PWM Generation Error */
    Error_Handler();
  }

  /* Start channel 2 */
  if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
  {
    /* PWM Generation Error */
    Error_Handler();
  }

}

当我硬编码正确的值以替换 sConfigOC.Pulse = pulse_width 中的 pulse_width 值时,这足以 运行 上面评论中指定的固定占空比的 PWM。 在另一个函数中,我有一个算法可以更新 pulse_width 全局变量。该函数称为:adjust_PWM();。该算法计算从 ADC 测量的值并存储为全局变量。该函数称为:Data_Update();。在main()中,所有函数初始化完成后。我没完没了地调用这三个函数

Data_Update();
adjust_PWM();   
MX_TIM1_Init(); 

我试过了并在示波器上获得了奇怪的波形,但这可能是因为 ADC 引脚浮动,导致浮动测量干扰算法的占空比。此外,连续调用定时器的初始化会中断 PWM 信号。有没有更好的方法来更改占空比 运行 在不使用全局变量的情况下更改代码,或者每次我想更新占空比时都不需要初始化计时器。任何 link 将不胜感激。

编写您自己的函数来更新控制占空比的寄存器。您将必须手动更新相应的 CCRx 寄存器(x 是您正在使用的 PWM 通道,在您的情况下是 CCR1)。

ARR 寄存器是您在根据所需的占空比计算 CCR 寄存器的新值时将参考的寄存器。

void adjust_PWM_DC(TIM_HandleTypeDef* const pwmHandle, const float DC)
{
    assert(pwmHandle != NULL);
    assert((DC >= 0.0F) && (DC <= 100.0F));

    /* The duty cycle value is a percentage of the reload register value (ARR). Rounding is used.*/
    uint32_t newRegVal = (uint32_t)roundf((float32_t)(pwmHandle->Instance->ARR) * (DC * 0.01F));

    /*In case of the DC being calculated as higher than the reload register, cap it to the reload register*/
    if(newRegVal > pwmHandle->Instance->ARR){
        newRegVal = pwmHandle->Instance->ARR);
    }

    /*Assign the new DC count to the capture compare register.*/
    pwmHandle->Instance->CCR1 = (uint32_t)(roundf(newRegVal));  /*Change CCR1 to appropriate channel, or pass it in with function.*/ 
}

当您想要更改设置时不要重新初始化计时器,HAL 有一个专门用于此目的的宏,称为:

/**
  * @brief  Sets the TIM Capture Compare Register value on runtime without
  *         calling another time ConfigChannel function.
  * @param  __HANDLE__: TIM handle.
  * @param  __CHANNEL__ : TIM Channels to be configured.
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @param  __COMPARE__: specifies the Capture Compare register new value.
  * @retval None
  */
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
(*(__IO uint32_t *)(&((__HANDLE__)->Instance->CCR1) + ((__CHANNEL__) >> 2)) = (__COMPARE__))

对于定时器 1 - 通道 1 和定时器 1 - 通道 2,它应该如下所示:

Data_Update();
adjust_PWM();

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse_width);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pulse_width);