[HAL库]STM32F103C8T6--使用定时器3产生硬件PWM

写在前面

这篇博客介绍下我是如何使用CubeMX生成基础环境并调用HAL库来产生硬件PWM的。

CubeMX生成过程

103C8T6的通用定时器,1个定时器可以产生4个通道的PWM波,每个通道对应了不同的Pin脚。我这次用的是PB5(TIM3_CHANNEL2)。
分两种办法:
1.自己配置相应的寄存器,配置好之后,类似51单片机那样使能输出,则相应的Pin脚就会输出对应的PWM波。
2.使用HAL库,HAL库提供了丰富的接口,调用对应的函数,并且传入正确的参数,那么也能使相应的Pin脚产生目标波形。

介绍下第二种方法:
首先CubeMx配置好基础环境:
[HAL库]STM32F103C8T6--使用定时器3产生硬件PWM_第1张图片
定时器初值这个部分:
补上上一篇博客的定时器定时时间计算公式:

(ARR+1)*(PSC+1)/Tclk=time

也就是说假如我要定时10ms作为PWM的总周期,并且一个周期分为100次计数,那么TIM3的定时时间就应该是(100-1+1)*(2400-1+1)/2400 0000 (24M时钟)=0.01s,即10ms的总周期。

设置完总周期之后,我可以得知PWM的频率为100Hz,1个周期分为了100份。这时候开始设置下面的这个PWM设置。PWM模式 1和2 区别就是定时器当前值跟占空比设置的值做对比,如果定时器当前值更大则输出PWM/或者不输出。

上面截图上的注释中:模式1应该修改为模式2

模式1: 在向上计数的模式中,定时器值<设置的占空比值 时 ,PWM输出。在向下计数模式中,定时器当前值<=设置的占空比值 时,PWM输出。

模式2跟模式1是相反的。

完成了上面的CubeMX设置后,生成代码。进入到调用步骤:

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_TIM2_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

可以在主函数里面看到MX_TIM3_Init();
这个就是CubeMX生成的定时器3的初始化函数。
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2); 这个函数功能就是让定时器3通道2的PWM开始输出。至于这个函数的内容我这里不考究了。因为用HAL库就是要学会调用库,至于库为什么能实现,那是底层层面的内容。根据前面弄51的经验,这个库函数的最终就是根据我们传入的参,判断哪些寄存器的哪些位作一些改变。根本上来说还是操作寄存器,只不过用HAL库开发就是省略了我们自己去操作这些寄存器的麻烦步骤。

再来看看MX_TIM3_Init();函数内容:

void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 2400-1;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 100-1;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  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();
  }
  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 = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);

}

重点看:

htim3.Instance = TIM3;
htim3.Init.Prescaler = 2400-1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100-1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

这里对应了我刚刚CubeMX设置的TIM3的一些配置,比如我设置的周期、计数模式、时钟分频以及是否自动重载。

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

这里对应的是默认的那两条设置。

sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

这里的意思就是 PWM模式1、占空比为0、有效电平为高电平、非快速模式。

还有个地方想说一下,当在CubeMX中设置了PB5作为TIM3_CHANNEL2 PWM输出之后,在GPIO_Init(); 函数里面找不到关于PB5的设置,我是通过搜索才搜索到配置PB5的地方。
代码如下:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_baseHandle->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */

  /* USER CODE END TIM2_MspInit 0 */
    /* TIM2 clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();

    /* TIM2 interrupt Init */
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  /* USER CODE BEGIN TIM2_MspInit 1 */

  /* USER CODE END TIM2_MspInit 1 */
  }
  else if(tim_baseHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */

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

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM3 GPIO Configuration
    PB5     ------> TIM3_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_AFIO_REMAP_TIM3_PARTIAL();

  /* USER CODE BEGIN TIM3_MspInit 1 */

  /* USER CODE END TIM3_MspInit 1 */
  }
}

在MX_TIM3_Init(void);函数中 有一句htim3.Instance = TIM3;
在上面的函数中:

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

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

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM3 GPIO Configuration
    PB5     ------> TIM3_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_AFIO_REMAP_TIM3_PARTIAL();

  /* USER CODE BEGIN TIM3_MspInit 1 */

  /* USER CODE END TIM3_MspInit 1 */
  }

正好与PB5匹配,于是在这里面找到了配置PB5的代码。
GPIO_MODE_AF_PP 这个是设置PB5的输出模式为复用推挽输出模式。
后面的就不多赘述了。

到这为止,PB5已经可以输出我想要的PWM了。为了做一个呼吸灯的效果,我又把TIM2开了一个10MS的定时器中断,在中断里面我执行了如下代码:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance == TIM2)
  {
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);

    if(flag==0)
    {
      pwm++;
      if(pwm>100)
      {
        pwm=100;
        flag=1;
      }
    }
    else
    {
      pwm--;
      if(pwm<1)
      {
        pwm=0;
        flag=0;
      }
    }

    __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,pwm);
  }
}

这样就通过硬件PWM做出了一个呼吸灯的效果。

你可能感兴趣的:(技术分享,stm32,单片机,arm)