一、PWM 输入模式测量脉宽
1.1 测量脉宽简介
在测量占空比之前,我们先一步一步来,先让 STM32 可以测量脉宽。
TIM3_CH1(tim3 定时器通道 1)捕获模式测量脉宽步骤如下:
1.输入捕获到 PWM 上升沿触发
2.发送中断,通知用户此时被触发,用户获得当前计数器值
3.计数器清零,然后继续计数...
让 STM32 芯片一直重复这三步即可不断地测量出当前的 PWM 脉宽。
1.2 测量脉宽原理
首先我们可以使用 PWM 的复位 Reset 模式,它复合我们之前所要求的功能。具有捕捉上升沿和计数器复位的能力。
如果我们使用 TIM_CH1,可以产生分别是TI1FP1 与TI1FP2 这一对信号。他们的功能是分别是捕捉上升沿和下降沿。
其原理是:
二者都是来自同一 TI1 输入通道,经过输入滤波和边沿检测器后所产生的具有相同特征的信号,然后映射到不同的输入捕捉通道,本质上还是同一路信号。
TI1FP1,是来自于通道TI1,经过滤波器后将接到捕捉比较通道 IC1;
TI1FP2,是来自于通道TI1,经过滤波器后将接到捕捉比较通道 IC2;
由这张图得知,所谓 IC1 就是上升沿信号,TI1FP1会捕获到。IC2 是下降沿信号,TI1FP2 会捕捉到。
1.3 cubeMX 配置
①和②在上文已经详细介绍,不再赘述
③我们使用内部时钟
④重点来了!我们捕捉的通道是 TIM_CH1 如果我们走红色这条线,即可用后面的 TI1FP1 捕捉到上升沿。使用直接捕获模式(input capture direct mode)即可。如果需要捕获 TI1FP2 的下降沿则需要使用 重映射输入捕获(input capture direct from remap)。
⑤配置分频系数,我的系统时钟是 80mhz,分频 80 则为 1mhz,所以计数速度是 每秒1000000次。
⑥计数器最大值为 65535,所以测量最大的脉宽则为 65536/1000000 = 0.065536 秒。取倒数大约为 15.25hz 所以请不要测量超过这个值的脉宽,否则就会溢出清零,导致测量不准。
⑦默认值,配置为上升沿计数。
1.3 cubeMX 生成的代码解析
/*STM32cubeMX 生成的 TIM3 初始化代码:*/
/* TIM3 init function */
void PWM_TIM3_CHANNEL_1_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3; //指定需要配置的定时器
htim3.Init.Prescaler = 79; //定时器的预分频系数
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535; //定时器计数周期值
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; //使用STM32单片机内部时钟
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim3) != 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(&htim3, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}
输入GPIO口的配置:
大致是设置复用端口配置、时钟相关配置。比较简单就不全都注释了。
让我困惑的是为什么这个捕获 PWM 的引脚被配置成 GPIO_MODE_AF_PP 复用推挽输出,难道不应该是输入吗?
/*STM32cubeMX 生成的 TIM3 初始化代码:*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
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
PB4 ------> TIM3_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* TIM3 interrupt Init */
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
}
1.4 需要我们写的获取值的代码
经过下降沿后会自动触发中断,在回调函数中判断下是 TIM3通道1发出的,就可以调用HAL_TIM_ReadCapturedValue() 取值了。
HAL_TIM_Base_Start(&htim3);
/* 启动定时器通道输入捕获并开启中断 */
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
PWM1_T_Count = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
}
}
}
二、测量占空比
2.1 测量占空比分析
基于之前测量脉宽的逻辑,如果我们把测量上升沿配置成主模式,测量下降沿配置成从模式。STM32将会是这样的逻辑:
1.在上升沿复位两个计数器
2.下降沿暂停下降沿计数器
3.再次上升沿复位两个计数器
不断重复....
在之前测量脉宽我们使用了 TI1FP1 线路测量上升沿,不过测量下降沿则需要 TI1FP2 辅助,将其信号类似“转发”到 TIM_CH2 线路中 (下图红色线路)。也就是说,当我们在 TIM_CH1 同时捕捉上升沿和下降沿,就会占用 TIM_CH2 线路。
2.2 cubeMX 配置
基于之前的配置,我们需要将①通道2配置为从模式;在②中,将这个配置为下降沿触发。
2.3 需要我们写的代码
节约篇幅不再解释 cubeMX 生成的代码,直接贴出我们需要写的代码:
//输入捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
PWM1_T_Count = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1; //捕获脉宽
PWM1_Duty = (float)PWM1_D_Count/PWM1_T_Count; //捕获占空比
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
PWM1_D_Count = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;//捕获下降沿
}
}
}