基於STM32 CubeMX 配置 PWM輸出和輸入捕獲


PWM輸出和輸入捕獲

1、試驗目標

  1.輸出2路PWM脈沖信號

  2.捕獲1路PWM脈沖信號

  本次試驗會使用到2個定時器,一個高級定時器用於脈沖捕獲,一個普通定時器用於PWM脈沖輸出。

2、准備材料

  1. STM32F103C8

  2. STM32CubeMX

2、STM32CubeMX配置

  2.1時鍾樹

    系統時鍾為72M,APB1 和APB2 的定時器時鍾都為72MHZ。

 

 

 

  2.2 PWM輸出配置

  PWM的輸出配置比較簡單,這里我們使用到了TIM2普通定時器控制輸出,具體參數如下圖。

 

   在 Parameter Settings 頁配置預分頻系數為 72-1,計數周期(自動加載值)為 10000-1,定時器溢出頻率,即PWM的周期,就是 72MHz/(71+1)/(9999+1) = 100Hz

  2.3 PWM輸入捕獲配置

  PWM捕獲,本次試驗使用到了STM32F103C8的高級定時器TIM1。配置如下圖。

 

 

 

 

 中斷配置勾線這里,因為我們需要使用中斷回調函數來計算頻率占空比。

  2.4 配置中斷分組和中斷使能

2.5串口輸出

 

 

 

2.6生成工程

  這里選擇分離C.h文件,IDE 根據自己的環境選擇,這里我使用的GUN編譯方式的IDE所以選擇了SW4SEM32。

 

 

 以上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輸出與捕獲。

 

4、分析總結

4.1輸入捕獲相關硬件信號知識

  脈沖信號從外部進來需要經過6個步驟。功能圖如下圖所示。

 

    第1;確認通道;

      脈沖進來時需要確定是從哪個定時器的通道進來的,通常這個通道的叫法為TI1/2/3/4。這里我選擇了高級定時器1的TI1 和TI2,

      對應的引腳是默認引腳是PA8 輸入捕獲,F103C8支持重映像,即不能使用其它引腳代替。

      

    第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中要勾選為復位模式。下圖參考手冊的時序圖

                        

 

  4.3 PWM輸出模式

    PWM 輸出就是對外輸出脈寬(即占空比)可調的方波信號,信號頻率由自動重裝寄存器ARR 的值決定,占空比由比較寄存器CCR 的值決定。

  PWM 模式分為兩種,PWM1 和PWM2。下圖的表格展示區別。

     

 

    本次試驗使用的 PWM1 模式遞增計數模式,計數器從0 計數到自動重載值(TIMx_ARR 寄存器的內容),然后重新從0 開始計數並生成計數器上溢事件。

  回到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指南者》

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM