背景
在使用STM32进行项目开发时,需要有2个通道的输入捕获采集,一开始使用不同定时器进行捕获是能够满足需求的。但是在新需求到来时,需要增加多个PWM输出及2个输入捕获,在进行资源分配时发现如果一个定时器能够同时采集4个通道,如果新需求再次到来,也能够有余量去实现不同频率的PWM输出(输入捕获可以使用同一个定时器频率,但PWM输出可能针对场景会有所不同),因此实现同一定时器多通道捕获是非常有效的功能。
实现方法
在输入捕获模式下,当检测到ICx信号上相应的边沿后,计数器的当前值被锁存到捕获/比较寄存器(TIMx_CCRx)中。如果我们在第一次边沿触发后记录下此时的TIMx_CCRx值,记为CCRx_FIRST,在下一次边沿触发后记录下TIMx_CCRx值,记为CCRx_SECOND,将这两次值进行一个差值计算能够获取到这次采集波形的频率。但是,这里需要考虑一种会频繁发生的情况:计数器CNT值溢出,如果此时只是简单的做减法是不行的。如何解决?我们引入熟悉的定时器更新中断,在每一次溢出时会触发一次更新中断,我们记录下从第一次边沿触发后到第二次边沿触发总共发生了几次更新中断,记为UPDATE_CNT,然后通过以下公式即可计算出捕获计数值:
CAPTURE_COUNT = CCRx_SECOND - CCRx_FIRST + PERIOD_COUNT*UPDATE_CNT;
其中CAPTURE_COUNT为捕获计数值,PERIOD_COUNT为捕获使用定时器设置周期值,一般作为捕获输入设置为0xFFFF。
获取到捕获计数值后就能够计算出周期,频率等参数(该方法无法计算占空比),尽管已经完成了一次捕获,我们还需要为下次捕获做准备。需要做的工作是:1.清空UPDATE_CNT值 2. 将CCRx_SECOND的值作为第一次捕获得到的值,这样就能连续捕获,不会丢波形
到此为止我们便能够支持多通道捕获功能,上面提到实现的方法没有描述定时器的输入捕获配置通道,关于这方面可以查阅其它相关的资料。
开发环境
主控
| STM32F103VET6
|
IDE
| KEIL MDK528
|
虽然这里指出本文代码使用的开发环境,但是针对本篇文章,与芯片型号没有太大关联,其它芯片开发方式思路类似。
代码实现
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @attention
*
*
© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* 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 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;
/* IC capture edge define*/
#define IC_RISE_EDGE_FIRST 0
#define IC_RISE_EDGE_SECOND 1
/* IC capture Parameters define*/
#define TIM_ICOF_MAX 0x32
#define TIM_IC_MAX (4)
typedef struct tim_ic_info_st
{
uint8_t update_cnt;
uint8_t edge_seq;
uint32_t cap_val_last;
uint32_t cap_val_cur;
uint32_t cap_sum;
float freq;
}tim_ic_info_t;
tim_ic_info_t tim_ic_info[TIM_IC_MAX] = {0};
/* TIM2 init function */
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = TIM2_PRESCALER_COUNT -1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = TIM2_PERIOD_COUNT - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
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();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
/* TIM3 init function */
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 = TIM3_PRESCALER_COUNT-1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = TIM3_PERIOD_COUNT -1;
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;
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_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = TIM4_PRESCALER_COUNT - 1;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = TIM4_PERIOD_COUNT - 1;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &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(&htim4, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* USER CODE END TIM4_Init 2 */
}
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();
/* 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();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**TIM4 GPIO Configuration
PD12 ------> TIM4_CH1
PD13 ------> TIM4_CH2
PD14 ------> TIM4_CH3
PD15 ------> TIM4_CH4
*/
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
__HAL_AFIO_REMAP_TIM4_ENABLE();
/* TIM4 interrupt Init */
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_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;
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 */
}
else if(timHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspPostInit 0 */
/* USER CODE END TIM3_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspPostInit 1 */
/* USER CODE END TIM3_MspPostInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
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 */
}
else if(tim_baseHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspDeInit 0 */
/* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM3_CLK_DISABLE();
/* USER CODE BEGIN TIM3_MspDeInit 1 */
/* USER CODE END TIM3_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/**TIM4 GPIO Configuration
PD12 ------> TIM4_CH1
PD13 ------> TIM4_CH2
PD14 ------> TIM4_CH3
PD15 ------> TIM4_CH4
*/
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
/* TIM4 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/**
* @description: tim_read_channel_num,读取当前定时器句柄对应的通道
* @Author: https://blog.csdn.net/qq_34672688
* @return {*}
* @param channel_num:访问结构体数组使用的通道号
* @param channel: 读取捕获值使用的通道号
*/
void tim_read_channel_num(TIM_HandleTypeDef *htim, uint8_t* channel_num, uint8_t* channel)
{
switch (htim->Channel)
{
case HAL_TIM_ACTIVE_CHANNEL_1:
*channel_num = 0;
*channel = TIM_CHANNEL_1;
break;
case HAL_TIM_ACTIVE_CHANNEL_2:
*channel_num = 1;
*channel = TIM_CHANNEL_2;
break;
case HAL_TIM_ACTIVE_CHANNEL_3:
*channel_num = 2;
*channel = TIM_CHANNEL_3;
break;
case HAL_TIM_ACTIVE_CHANNEL_4:
*channel_num = 3;
*channel = TIM_CHANNEL_4;
break;
default:
*channel_num = 0xFF;
*channel = 0xFF;
break;
}
}
/**
* @description: TIMCaptureChannelHandle 同定时器捕获多通道进行处理
* @Author: https://blog.csdn.net/qq_34672688
* @return {*}
* @param {TIM_HandleTypeDef} *htim
*/
void TIMCaptureChannelHandle(TIM_HandleTypeDef *htim)
{
uint8_t channel_num, channel;
tim_read_channel_num(htim, &channel_num, &channel);
if(channel_num == 0xFF)
{
return ;
}
if (tim_ic_info[channel_num].edge_seq == IC_RISE_EDGE_FIRST)
{
tim_ic_info[channel_num].cap_val_last = HAL_TIM_ReadCapturedValue(htim, channel);
tim_ic_info[channel_num].edge_seq = IC_RISE_EDGE_SECOND;
}
else
{
tim_ic_info[channel_num].cap_val_cur = HAL_TIM_ReadCapturedValue(htim, channel);
tim_ic_info[channel_num].cap_sum = tim_ic_info[channel_num].cap_val_cur - tim_ic_info[channel_num].cap_val_last + 0xFFFF*tim_ic_info[channel_num].update_cnt;
tim_ic_info[channel_num].freq = ((float)SystemCoreClock / TIM4_PRESCALER_COUNT) / (tim_ic_info[channel_num].cap_sum);
tim_ic_info[channel_num].edge_seq = IC_RISE_EDGE_FIRST;
tim_ic_info[channel_num].update_cnt = 0;
tim_ic_info[channel_num].cap_val_last = tim_ic_info[channel_num].cap_val_cur;
}
}
/* USER CODE BEGIN 1 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4)
{
TIMCaptureChannelHandle(htim);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4) {
for (uint8_t i = 0; i < TIM_IC_MAX; i++)
{
if (tim_ic_info
.update_cnt < TIM_ICOF_MAX)
{
tim_ic_info.update_cnt++;
}
else
{
tim_ic_info.update_cnt = 0;
}
}
}
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
调试效果