1、试验目标

  1.输出2路PWM脉冲信号

  2.捕获1路PWM脉冲信号

  本次试验会使用到2个定时器,一个高级定时器用于脉冲捕获,一个普通定时器用于PWM脉冲输出。

2、准备材料

  1. STM32F103C8

  2. STM32CubeMX

2、STM32CubeMX配置

  2.1时钟树

    系统时钟为72M,APB1 和APB2 的定时器时钟都为72MHZ。

基于STM32 CubeMX 配置 PWM输出和输入捕获

 

 

 

  2.2 PWM输出配置

  PWM的输出配置比较简单,这里我们使用到了TIM2普通定时器控制输出,具体参数如下图。

 基于STM32 CubeMX 配置 PWM输出和输入捕获

   在 Parameter Settings 页配置预分频系数为 72-1,计数周期(自动加载值)为 10000-1,定时器溢出频率,即PWM的周期,就是 72MHz/(71+1)/(9999+1) = 100Hz

  2.3 PWM输入捕获配置

  PWM捕获,本次试验使用到了STM32F103C8的高级定时器TIM1。配置如下图。

基于STM32 CubeMX 配置 PWM输出和输入捕获

 

 

 

 

 中断配置勾线这里,因为我们需要使用中断回调函数来计算频率占空比。

基于STM32 CubeMX 配置 PWM输出和输入捕获

  2.4 配置中断分组和中断使能

基于STM32 CubeMX 配置 PWM输出和输入捕获

2.5串口输出

基于STM32 CubeMX 配置 PWM输出和输入捕获

 

 

 

2.6生成工程

  这里选择分离C.h文件,IDE 根据自己的环境选择,这里我使用的GUN编译方式的IDE所以选择了SW4SEM32。

基于STM32 CubeMX 配置 PWM输出和输入捕获基于STM32 CubeMX 配置 PWM输出和输入捕获

 

 

 以上CubeMX的PWM配置就完成了。

配置完毕后,生成工程打开。下面我们来分析代码和如何使用。

 

3、代码实现

  3.1 tim.c

    该代码主要配置了Tim1 和Tim2 的相关配置,为什么要这么配置,在接下来的第4大点会详细说明。这里主要了解 HAL_TIM_IC_CaptureCallback  捕获中断回调函数

这里函数主要处理计算占空比和频率。

/**
  ******************************************************************************
  * @file    tim.c
  * @brief   This file provides code for the configuration
  *          of the TIM instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>© Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

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

/* USER CODE BEGIN 0 */
/// 计算占空比时使用
__IO uint16_t IC2Value = 0;
__IO uint16_t IC1Value = 0;
__IO float DutyCycle = 0;
__IO float Frequency = 0;
/* USER CODE END 0 */

TIM_HandleTypeDef htim1; // 高级定时器捕获PWM
TIM_HandleTypeDef htim2; // 普通定时器输出PWM

/* TIM1 init function */
void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};

  htim1.Instance = TIM1;

  htim1.Init.Prescaler = 72-1;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;                  /* 计数方式 上计数 */
  htim1.Init.Period = 65535;                                    /* 计数器更新上限值         */
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;            /* 采样时钟分频 */
  htim1.Init.RepetitionCounter = 0;                             /* 重装值=0 */
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /* 自动装载值软件使能 */
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)                      /* 初始定时器 */
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;     /* 内部时钟源 */
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  ///选择从模式: 复位模式
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;                           /* 选择定时器输入触发: TI1FP1 */
  sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sSlaveConfig.TriggerFilter = 0;
  if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
    ///IC1捕获 上升沿触发 TI1FP1
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  ///IC2捕获 下降沿捕获 TI1FP2
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
  if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }

}
/* TIM2 init function */
void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim2.Instance = TIM2;
    /**   htim2.Init.Prescaler 分频计算
 * 定时器时钟源TIMxCLK = 2 * PCLK1
 * 				PCLK1 = HCLK / 2
 * 				=> TIMxCLK = HCLK/2 = SystemCoreClock / 2 *2=72MHz     (APB1)
 * 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=10KHz
 * */
  htim2.Init.Prescaler = 72-1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;                  /* 计数方式上升沿有效       */
  htim2.Init.Period = 10000-1;                                  /* 累计 TIM_Period个后产生一个更新或者中断 当定时器从0计数到10000,即为10000次,为一个定时周期*/
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  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();
  }
  ///PWM模式配置
  sConfigOC.OCMode = TIM_OCMODE_PWM1;                       /* 配置为PWM模式1*/
  sConfigOC.Pulse = 5000;                                   /* 默认占空比为50%*/
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;               /* 当定时器计数值小于CCR1_Val时为高电平*/
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)   /* 配置PWM通道*/
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim2);                             /* 外置GPIO初始化 */

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

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

  /* USER CODE END TIM1_MspInit 0 */
    /* TIM1 clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();         /*定时器时钟使能*/

    __HAL_RCC_GPIOA_CLK_ENABLE();       /*GPIO时钟使能*/
    /**TIM1 GPIO Configuration
    PA8     ------> TIM1_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8;               /* 36脚的F103 不能改变引脚编号*/
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;         /* 输入模式*/
    GPIO_InitStruct.Pull = GPIO_NOPULL;             /* 无上下拉*/
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* TIM1 interrupt Init */
    HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0);   /* 配置中断分组*/
    HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);                                     /* 使能中断*/
  /* USER CODE BEGIN TIM1_MspInit 1 */

  /* USER CODE END TIM1_MspInit 1 */
  }
  else 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();
  /* USER CODE BEGIN TIM2_MspInit 1 */

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

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

  /* USER CODE END TIM2_MspPostInit 0 */

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM2 GPIO Configuration
    PA0-WKUP     ------> TIM2_CH1
    PA1     ------> TIM2_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;        /* 这里定义了2路PMW输出 用PA0 和PA1*/
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM2_MspPostInit 1 */

  /* USER CODE END TIM2_MspPostInit 1 */
  }

}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspDeInit 0 */

  /* USER CODE END TIM1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM1_CLK_DISABLE();

    /**TIM1 GPIO Configuration
    PA8     ------> TIM1_CH1
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8);

    /* TIM1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(TIM1_CC_IRQn);
  /* USER CODE BEGIN TIM1_MspDeInit 1 */

  /* USER CODE END TIM1_MspDeInit 1 */
  }
  else if(tim_baseHandle->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 */
  }
}

/* USER CODE BEGIN 1 */
/**
  * @brief  Conversion complete callback in non blocking mode  捕获回调函数
  * @param  htim : hadc handle
  * @retval None
  */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
        /* 获取输入捕获值 */
        IC1Value = HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
        IC2Value = HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_2);
        if (IC1Value != 0)
        {
            /* 占空比计算 */
            DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);
            /* 频率计算 */
            Frequency = 72000000/72/(float)(IC1Value+1);
        }
        else
        {
            DutyCycle = 0;
            Frequency = 0;
        }

    }
}
/* USER CODE END 1 */

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

3.2 main.c

 用于计算的变量

//计算占空比时的全局表变量
extern __IO uint16_t IC2Value;
extern __IO uint16_t IC1Value;
extern __IO float DutyCycle;
extern __IO float Frequency;

 使能和输出PWM

    /// 使能捕获/比较2中断请求
    HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1);
    HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_2);

    /// 开始输出PWM
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

  打印调试输出

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    printSwo("IC1Value =",IC1Value,LINE_FEED_EN);
    printSwo("IC2Value =",IC2Value,LINE_FEED_EN);
    printSwo("占空比:",DutyCycle,LINE_FEED_EN);
    printSwo("频率:",Frequency,LINE_FEED_EN);
    HAL_Delay(500);
  }

  

3.3 输出结果

短接任意一路输出  PA0与PA8  PWM输出与捕获,或 PA1与PA8  PWM输出与捕获。

基于STM32 CubeMX 配置 PWM输出和输入捕获

 

4、分析总结

4.1输入捕获相关硬件信号知识

  脉冲信号从外部进来需要经过6个步骤。功能图如下图所示。

 

基于STM32 CubeMX 配置 PWM输出和输入捕获

    第1;确认通道;

      脉冲进来时需要确定是从哪个定时器的通道进来的,通常这个通道的叫法为TI1/2/3/4。这里我选择了高级定时器1的TI1 和TI2,

      对应的引脚是默认引脚是PA8 输入捕获,F103C8支持重映像,即不能使用其它引脚代替。

      基于STM32 CubeMX 配置 PWM输出和输入捕获

    第2;滤波器与边沿检测器;

       信号进来后为了避免干扰,就需要进行滤波。当前一个高电平信号过来,假设采样速度为1M的速度进行计算,如果滤波系数设置为4,

       那么高电平信号需要维持连续维持4us的高电平,才计数为一个高电平。边沿检测器用来设置信号在捕获的时候是什么边沿有效,可以

       是上升沿,下降沿,或者是双边沿。

    第3;捕获通道;

        是指图中的IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器CCR1/2/3/4,当发生捕获的时候,计数器CNT 的值就会被锁存到捕

        获寄存器中。

    第4;预分频;

        捕获的信号会经过一个预分频器,比如2次上升沿算为一次计数。本次试验设定为0,即不分频。

    第5;捕获/比较寄存器;

        经过预分频器的信号ICxPS 是最终被捕获的信号,当发生捕获时(第一次),计数器CNT 的值会被锁存到捕获寄存器CCR 中,还会产生CCxI 中断,

      相应的中断位CCxIF(在SR 寄存器中)会被置位,通过软件或者读取CCR 中的值可以将CCxIF 清0。如果发生第二次捕获(即重复捕获:CCR 寄存

      器中已捕获到计数器值且 CCxIF 标志已置 1),则捕获溢出标志位CCxOF(在SR 寄存器中)会被置位,CCxOF 只能通过软件清零。

        输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通道X(x=1,2,3,4)设置为匹配时输出有效电平、将通道X 设置为匹配时输出无

      效电平、翻转、强制变为无效电平、强制变为有效电平、PWM1 和PWM2 这八种模式。电机控制很常用,这里不展开讨论。

  4.2 PWM输入模式

    大概了解上述小点后,这里说明我们本次测试使用到的PWM输入模式,它是最便捷的测量脉宽和频率的方法。当使用PWM 输入模式的时候,因为一个输入通道

    (TIx)会占用两个捕获通道(ICx),所以一个定时器在使用PWM输入的时候最多只能使用两个输入通道(TIx)。本次试验就是使用TIM1的CH1 和CH2。

    工作原理是这样的:

      PWM 信号由输入通道TI1 进入,因为是PWM 输入模式的缘故,信号会被分为两路,一路是TI1FP1,另外一路是TI2FP2。其中一路是周期,另一路是占空比,

    具体哪一路信号对应周期还是占空比,得从程序上设置哪一路信号作为触发输入,作为触发输入的哪一路信号对应的就是周期,另一路就是对应占空比。作为触发

    输入的那一路信号还需要设置极性,是上升沿还是下降沿捕获,一旦设置好触发输入的极性,另外一路硬件就会自动配置为相反的极性捕获,无需软件配置。一句

    话概括就是:选定输入通道,确定触发信号,然后设置触发信号的极性即可,因为是PWM 输入的缘故,另一路信号则由硬件配置,无需软件配置。

      当使用PWM 输入模式的时候必须将从模式控制器配置为复位模式(配置寄存器SMCR 的位SMS[2:0]来实现),即当我们启动触发信号开始进行捕获的时候,

    同时把计数器CNT 复位清零。所以我们在STM32CubeMX中要勾选为复位模式。下图参考手册的时序图

                        基于STM32 CubeMX 配置 PWM输出和输入捕获

 

  4.3 PWM输出模式

    PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器ARR 的值决定,占空比由比较寄存器CCR 的值决定。

  PWM 模式分为两种,PWM1 和PWM2。下图的表格展示区别。

     基于STM32 CubeMX 配置 PWM输出和输入捕获

 

    本次试验使用的 PWM1 模式递增计数模式,计数器从0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从0 开始计数并生成计数器上溢事件。

基于STM32 CubeMX 配置 PWM输出和输入捕获

  回到2.2小点的PWM配置图

     配置预分频系数为 72-1,计数周期(自动加载值)为 10000-1,定时器溢出频率,即PWM的周期,就是

                    72MHz/(71+1)/(9999+1) = 100Hz

    输出频率:

                arr = 计数器值    psc = 预分频值

                Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)
    输出占空比:
                    duty circle = TIMx->CCRx / arr(单位:%)
                    TIMx->CCRx 用户设定值
    比如: 定时器频率Tclk = 72Mhz arr=10000   psc=71    那么PWM频率就是720000/10000/72= 100Hz
                  arr=10000,    TIMx->CCRx=5000     则pwm的占空比为50%
                      CCRx的值影响占空比,arr的值影响频率

 

参考资料:STM32中文参考手册V10

     【野火】《STM32 HAL 库开发实战指南—基于F103指南者》

 

 

 

 

相关文章: