1、试验目标
1) 输出2路PWM脉冲信号
2) 捕获1路PWM脉冲信号
本次试验会使用到2个定时器,一个高级定时器用于脉冲捕获,一个普通定时器用于PWM脉冲输出。
2、准备材料
1) STM32F103C8
2) STM32CubeMX
3、STM32CubeMX配置
3.1时钟树
系统时钟为72M,APB1 和APB2 的定时器时钟都为72MHZ。
3.2 PWM输出配置
PWM的输出配置比较简单,这里我们使用到了TIM2普通定时器控制输出,具体参数如下图。
在 Parameter Settings 页配置预分频系数为 72-1,计数周期(自动加载值)为 10000-1,定时器溢出频率,即PWM的周期,就是 72MHz/(71+1)/(9999+1) = 100Hz
3.3 PWM输入捕获配置
PWM捕获,本次试验使用到了STM32F103C8的高级定时器TIM1。配置如下图。
中断配置勾线这里,因为我们需要使用中断回调函数来计算频率占空比。
3.4 配置中断分组和中断使能
3.5串口输出
3.6生成工程
这里选择分离C.h文件,IDE 根据自己的环境选择,这里我使用的GUN编译方式的IDE所以选择了SW4SEM32。
以上CubeMX的PWM配置就完成了。
配置完毕后,生成工程打开。下面我们来分析代码和如何使用。
4、代码实现
4.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****/
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的值影响频率。