Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器
阅读原文时间:2022年03月18日阅读:2

Keil MDK STM32系列

配置 PWM 输出

  • 选择芯片
  • System Core -> SYS-> Debug: Serial Wire 防止下次无法烧录
  • System Core -> RCC-> High Speed Clock (HSE): Crystal/Ceramic Resonator 启用外接高速晶振
  • Clock Configuration: (配置为最高84MHz)选择外部晶振, 把HSE和PLLCLK连上, 在HCLK上输入84回车, 软件会自动调节各节点倍数
  • Timers -> TIM2
  • Clock Source: Internel Clock, 使用系统的时钟源
    • Channelx: PWM Generation CHx PWM输出
    • Counter Settings PWM频率 = 84MHz / (Perscaler + 1) / (Counter Period + 1)
      • Perscaler: 0
      • Counter Mode: Up
      • Counter Period: 256
      • Internal Clock Division(CKD): No Division
      • auto-reload preload: Enable - 如果启用, 在修改占空比时会等当前周期执行完再变化
    • Trigger Output
      • Master/Slave Mode (MSM bit): Disable
      • Trigger Event Selection: Reset (UG bit from TIMx_EGR)
  • PWM Generation Channel 1
    • Mode: PWM mode1
    • Pulse: 0
    • Output compare perload: Enable
    • Fast Mode: Disable
    • CH Polarity: High
  • PWM Generation Channel 2
  1. stm32f4xx_hal_conf.h 去掉了TIM的注释

    #define HAL_TIM_MODULE_ENABLED

  2. stm32f4xx_hal_msp.c 增加了初始化方法HAL_TIM_Base_MspInit(), HAL_TIM_MspPostInit(), HAL_TIM_Base_MspDeInit()

    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
    {
    if(htim_base->Instance==TIM2)
    {
    /* USER CODE BEGIN TIM2_MspInit 0 */

    /* USER CODE END TIM2_MspInit 0 / / Peripheral clock enable / __HAL_RCC_TIM2_CLK_ENABLE(); / USER CODE BEGIN TIM2_MspInit 1 */

    /* USER CODE END TIM2_MspInit 1 */
    }

    }

    void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(htim->Instance==TIM2)
    {
    /* USER CODE BEGIN TIM2_MspPostInit 0 */

    /* USER CODE END TIM2_MspPostInit 0 */

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM2 GPIO Configuration
    PA15     ------> TIM2_CH1
    PB3     ------> TIM2_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* USER CODE BEGIN TIM2_MspPostInit 1 */

    /* USER CODE END TIM2_MspPostInit 1 */
    }

    }

    /**

    • @brief TIM_Base MSP De-Initialization

    • This function freeze the hardware resources used in this example

    • @param htim_base: TIM_Base handle pointer

    • @retval None
      / void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef htim_base)
      {
      if(htim_base->Instance==TIM2)
      {
      /* USER CODE BEGIN TIM2_MspDeInit 0 */

      /* USER CODE END TIM2_MspDeInit 0 / / Peripheral clock disable / __HAL_RCC_TIM2_CLK_DISABLE(); / USER CODE BEGIN TIM2_MspDeInit 1 */

      /* USER CODE END TIM2_MspDeInit 1 */
      }

    }

  3. main.c 对TIM2增加初始化方法, TIM的初始化过程与其它外设是不一样的

    这里Prescaler=0, Period=255, 对应84MHz的SYSCLK, 输出的PWM频率为84MHz/256 = 32.8125KHz, 用于输出音频

    TIM_HandleTypeDef htim2;

    /**

    • @brief TIM2 Initialization Function

    • @param None

    • @retval None
      */
      static void MX_TIM2_Init(void)
      {
      TIM_ClockConfigTypeDef sClockSourceConfig = {0};
      TIM_MasterConfigTypeDef sMasterConfig = {0};
      TIM_OC_InitTypeDef sConfigOC = {0};

      htim2.Instance = TIM2;
      htim2.Init.Prescaler = 0;
      htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
      htim2.Init.Period = 255;
      htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
      htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
      if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
      {
      Error_Handler();
      }
      sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
      if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
      {
      Error_Handler();
      }
      if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
      {
      Error_Handler();
      }
      sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
      sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
      if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
      {
      Error_Handler();
      }
      sConfigOC.OCMode = TIM_OCMODE_PWM1;
      sConfigOC.Pulse = 0;
      sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
      sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
      if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
      {
      Error_Handler();
      }
      if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
      {
      Error_Handler();
      }
      /* USER CODE BEGIN TIM2_Init 2 */

      /* USER CODE END TIM2_Init 2 */
      HAL_TIM_MspPostInit(&htim2);

    }

通过TIMx配置

  • 勾选 Internal Clock
  • 配置下方参数
  • Counter Settings
    • Prescaler: 0
    • Counter Mode: Up
    • Counter Period: 1999 这里和Perscaler组合, 实现(0+1)(1999+1)个时钟的周期
    • Internal Clock Division (CKD): No division
    • auto-reload preload: Disable
  • Trigger Output (TRGO) Parameters
    • Master/Slave Mode (MSM bit): Disable
    • Trigger Envent Selection: Reset
  • NVIC Settings
    • TIM3 global interrupt: Enable

对应代码变化

stm32f4xx_hal_conf.h 启用TIM模块

#define HAL_TIM_MODULE_ENABLED

main.c 除了初始化方法, 还需要添加中断处理方法

static void MX_TIM3_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 1999;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  //...
}

stm32f4xx_hal_msp.c 这里会一起处理其他的TIM实例

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    // ...
  }
  else if(htim_base->Instance==TIM3)
  {
    __HAL_RCC_TIM3_CLK_ENABLE();
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  }

}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    //...
  }
  else if(htim_base->Instance==TIM3)
  {
    __HAL_RCC_TIM3_CLK_DISABLE();
    HAL_NVIC_DisableIRQ(TIM3_IRQn);
  }

}

stm32f4xx_it.h 增加对应的定时中断处理

void TIM3_IRQHandler(void);

stm32f4xx_it.c

/**
  * @brief This function handles TIM3 global interrupt.
  */
void TIM3_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim3);
}