本文主要讲解自己实现的捕获程序。本文程序借鉴了正点原子的例程。关于正点原子的程序详解,可以参考博客
本文介绍的程序的不同主要如下:
本文使用的HAL库的版本为:STM32Cube_FW_F4_V1.25.0
本文使用的STM32CubeMX版本为:6.1.1
该工程的下载地址为:
/**
* @brief TIM Slave configuration Structure definition
*/
typedef struct
{
uint32_t SlaveMode; /*!< Slave mode selection
This parameter can be a value of @ref TIM_Slave_Mode */
uint32_t InputTrigger; /*!< Input Trigger source
This parameter can be a value of @ref TIM_Trigger_Selection */
uint32_t TriggerPolarity; /*!< Input Trigger polarity
This parameter can be a value of @ref TIM_Trigger_Polarity */
uint32_t TriggerPrescaler; /*!< Input trigger prescaler
This parameter can be a value of @ref TIM_Trigger_Prescaler */
uint32_t TriggerFilter; /*!< Input trigger filter
This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
} TIM_SlaveConfigTypeDef;
从模式的参数选择如下:
/** @defgroup TIM_Slave_Mode TIM Slave mode
* @{
*/
#define TIM_SLAVEMODE_DISABLE 0x00000000U /*!< Slave mode disabled */
#define TIM_SLAVEMODE_RESET TIM_SMCR_SMS_2 /*!< Reset Mode */
#define TIM_SLAVEMODE_GATED (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_0) /*!< Gated Mode */
#define TIM_SLAVEMODE_TRIGGER (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1) /*!< Trigger Mode */
#define TIM_SLAVEMODE_EXTERNAL1 (TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0) /*!< External Clock Mode 1 */
/**
* @}
*/
触发源的参数选择如下:
/** @defgroup TIM_Trigger_Selection TIM Trigger Selection
* @{
*/
#define TIM_TS_ITR0 0x00000000U /*!< Internal Trigger 0 (ITR0) */
#define TIM_TS_ITR1 TIM_SMCR_TS_0 /*!< Internal Trigger 1 (ITR1) */
#define TIM_TS_ITR2 TIM_SMCR_TS_1 /*!< Internal Trigger 2 (ITR2) */
#define TIM_TS_ITR3 (TIM_SMCR_TS_0 | TIM_SMCR_TS_1) /*!< Internal Trigger 3 (ITR3) */
#define TIM_TS_TI1F_ED TIM_SMCR_TS_2 /*!< TI1 Edge Detector (TI1F_ED) */
#define TIM_TS_TI1FP1 (TIM_SMCR_TS_0 | TIM_SMCR_TS_2) /*!< Filtered Timer Input 1 (TI1FP1) */
#define TIM_TS_TI2FP2 (TIM_SMCR_TS_1 | TIM_SMCR_TS_2) /*!< Filtered Timer Input 2 (TI2FP2) */
#define TIM_TS_ETRF (TIM_SMCR_TS_0 | TIM_SMCR_TS_1 | TIM_SMCR_TS_2) /*!< Filtered External Trigger input (ETRF) */
#define TIM_TS_NONE 0x0000FFFFU /*!< No trigger selected */
/**
* @}
*/
/** @defgroup TIM_Trigger_Polarity TIM Trigger Polarity
* @{
*/
#define TIM_TRIGGERPOLARITY_INVERTED TIM_ETRPOLARITY_INVERTED /*!< Polarity for ETRx trigger sources */
#define TIM_TRIGGERPOLARITY_NONINVERTED TIM_ETRPOLARITY_NONINVERTED /*!< Polarity for ETRx trigger sources */
#define TIM_TRIGGERPOLARITY_RISING TIM_INPUTCHANNELPOLARITY_RISING /*!< Polarity for TIxFPx or TI1_ED trigger sources */
#define TIM_TRIGGERPOLARITY_FALLING TIM_INPUTCHANNELPOLARITY_FALLING /*!< Polarity for TIxFPx or TI1_ED trigger sources */
#define TIM_TRIGGERPOLARITY_BOTHEDGE TIM_INPUTCHANNELPOLARITY_BOTHEDGE /*!< Polarity for TIxFPx or TI1_ED trigger sources */
/**
* @}
*/
/** @defgroup TIM_Trigger_Prescaler TIM Trigger Prescaler
* @{
*/
#define TIM_TRIGGERPRESCALER_DIV1 TIM_ETRPRESCALER_DIV1 /*!< No prescaler is used */
#define TIM_TRIGGERPRESCALER_DIV2 TIM_ETRPRESCALER_DIV2 /*!< Prescaler for External ETR Trigger: Capture performed once every 2 events. */
#define TIM_TRIGGERPRESCALER_DIV4 TIM_ETRPRESCALER_DIV4 /*!< Prescaler for External ETR Trigger: Capture performed once every 4 events. */
#define TIM_TRIGGERPRESCALER_DIV8 TIM_ETRPRESCALER_DIV8 /*!< Prescaler for External ETR Trigger: Capture performed once every 8 events. */
/**
* @}
*/
/**
* @brief 捕获初始化
* @note 无
* @param {*}无
* @retval 无
*/
void CaptureInit(void)
{
/* 1.RCC时钟初始化 */
__HAL_RCC_TIM8_CLK_ENABLE();
/* 2.基本时钟初始化 */
TIM_HandleTypeDef htim8 = {
0};
htim8.Instance = TIM8; //时钟为TIM8
htim8.Init.Prescaler = 1 - 1; //预分频为0
htim8.Init.Period = 0xFFFF; //使用最大值
htim8.Init.RepetitionCounter = 0; //无重复模式
htim8.Init.AutoReloadPreload = TIM_AUTOMATICOUTPUT_ENABLE; //支持影子寄存器
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //不分频
htim8.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
HAL_TIM_IC_Init(&htim8); //初始化TIM8
/* 3.输入通道配置设置 */
TIM_IC_InitTypeDef initTim8Ic = {
0};
initTim8Ic.ICFilter = 0; //不用滤波
initTim8Ic.ICPrescaler = TIM_ICPSC_DIV1; //不分频
initTim8Ic.ICPolarity = TIM_ICPOLARITY_RISING; //捕捉上升沿
initTim8Ic.ICSelection = TIM_ICSELECTION_DIRECTTI; //使用默认捕捉源
HAL_TIM_IC_ConfigChannel(&htim8, &initTim8Ic, TIM_CHANNEL_1); //初始化TIM8通道1
initTim8Ic.ICPolarity = TIM_ICPOLARITY_FALLING; //捕捉下降沿
initTim8Ic.ICSelection = TIM_ICSELECTION_INDIRECTTI; //使用替换捕捉源
HAL_TIM_IC_ConfigChannel(&htim8, &initTim8Ic, TIM_CHANNEL_2); //初始化TIM8通道2
/* 4.设置SLAVE模式 */
TIM_SlaveConfigTypeDef configTim8Slave = {
0};
configTim8Slave.SlaveMode = TIM_SLAVEMODE_RESET; //复位模式
configTim8Slave.TriggerFilter = 0; //不滤波
configTim8Slave.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1; //不预分频
configTim8Slave.InputTrigger = TIM_TS_TI1FP1; //触发源为TI1FP1
configTim8Slave.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING; //上升沿触发
HAL_TIM_SlaveConfigSynchro(&htim8, &configTim8Slave); //配置TIM8的通道1位触发源
/* 5.启动定时器 */
HAL_TIM_IC_Start_IT(&htim8, TIM_CHANNEL_1); //启动通道1
HAL_TIM_IC_Start_IT(&htim8, TIM_CHANNEL_2); //启动通道2
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE); //启动更新中断
__HAL_TIM_URS_ENABLE(&htim8); //更新中断只有溢出时触发
}
整个捕获初始化程序可以大致分成5个部分:
在基本时钟初始化中,不同于正点原子程序,本文同时使用通道1和通道2分别捕获PC6的上升沿和下降沿。对应的,在第5步中,启动定时器tim8的通道1和通道2。
在第4步中,设置RESET模式。使用到结构体TIM_SlaveConfigTypeDef
。其具体定义如上文详解。
需要尤其注意的是,第5步中,__HAL_TIM_URS_ENABLE(&htim8)启动URS功能。该功能只能减少触发更新中断的中断源。只有当计数器溢出时,才会触发更新中断。
整个从模式设置,在HAL库中的调用顺序为:
HAL_TIM_SlaveConfigSynchro()//调用子函数,禁用Trigger的中断与DMA
TIM_SlaveTimer_SetConfig()//设置SMCR,根据触发源分别调用子函数
TIM_TI1_ConfigInputStage()//
下面分别介绍:
/**
* @brief Configures the TIM in Slave mode
* @param htim TIM handle.
* @param sSlaveConfig pointer to a TIM_SlaveConfigTypeDef structure that
* contains the selected trigger (internal trigger input, filtered
* timer input or external trigger input) and the Slave mode
* (Disable, Reset, Gated, Trigger, External clock mode 1).
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro(TIM_HandleTypeDef *htim, TIM_SlaveConfigTypeDef *sSlaveConfig)
{
/* 1.参数检测 */
/* Check the parameters */
assert_param(IS_TIM_SLAVE_INSTANCE(htim->Instance));
assert_param(IS_TIM_SLAVE_MODE(sSlaveConfig->SlaveMode));
assert_param(IS_TIM_TRIGGER_SELECTION(sSlaveConfig->InputTrigger));
__HAL_LOCK(htim);
htim->State = HAL_TIM_STATE_BUSY;
/* 2.调用子函数 */
if (TIM_SlaveTimer_SetConfig(htim, sSlaveConfig) != HAL_OK)
{
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_ERROR;
}
/* 3.禁用Trigger中断和DMA */
/* Disable Trigger Interrupt */
__HAL_TIM_DISABLE_IT(htim, TIM_IT_TRIGGER);
/* Disable Trigger DMA request */
__HAL_TIM_DISABLE_DMA(htim, TIM_DMA_TRIGGER);
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_OK;
}
整体分成三个部分:
其中在配置之前将定时器句柄上锁,且句柄的状态变为BUSY。
在配置之后,将定时器句柄解锁,且句柄状态变为READY。
/**
* @brief Slave Timer configuration function
* @param htim TIM handle
* @param sSlaveConfig Slave timer configuration
* @retval None
*/
static HAL_StatusTypeDef TIM_SlaveTimer_SetConfig(TIM_HandleTypeDef *htim,
TIM_SlaveConfigTypeDef *sSlaveConfig)
{
uint32_t tmpsmcr;
uint32_t tmpccmr1;
uint32_t tmpccer;
/* 1.设置SMCR */
/* Get the TIMx SMCR register value */
tmpsmcr = htim->Instance->SMCR;
/* Reset the Trigger Selection Bits */
tmpsmcr &= ~TIM_SMCR_TS;
/* Set the Input Trigger source */
tmpsmcr |= sSlaveConfig->InputTrigger; //设置触发源
/* Reset the slave mode Bits */
tmpsmcr &= ~TIM_SMCR_SMS;
/* Set the slave mode */
tmpsmcr |= sSlaveConfig->SlaveMode; //设置从模式
/* Write to TIMx SMCR */
htim->Instance->SMCR = tmpsmcr;
/* 2.设置触发分频,滤波器,极性 */
/* Configure the trigger prescaler, filter, and polarity */
switch (sSlaveConfig->InputTrigger)
{
case TIM_TS_ETRF:
{
/* Check the parameters */
assert_param(IS_TIM_CLOCKSOURCE_ETRMODE1_INSTANCE(htim->Instance));
assert_param(IS_TIM_TRIGGERPRESCALER(sSlaveConfig->TriggerPrescaler));
assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
/* Configure the ETR Trigger source */
TIM_ETR_SetConfig(htim->Instance,
sSlaveConfig->TriggerPrescaler,
sSlaveConfig->TriggerPolarity,
sSlaveConfig->TriggerFilter);
break;
}
case TIM_TS_TI1F_ED:
{
/* Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
if (sSlaveConfig->SlaveMode == TIM_SLAVEMODE_GATED)
{
return HAL_ERROR;
}
/* Disable the Channel 1: Reset the CC1E Bit */
tmpccer = htim->Instance->CCER;
htim->Instance->CCER &= ~TIM_CCER_CC1E;
tmpccmr1 = htim->Instance->CCMR1;
/* Set the filter */
tmpccmr1 &= ~TIM_CCMR1_IC1F;
tmpccmr1 |= ((sSlaveConfig->TriggerFilter) << 4U);
/* Write to TIMx CCMR1 and CCER registers */
htim->Instance->CCMR1 = tmpccmr1;
htim->Instance->CCER = tmpccer;
break;
}
case TIM_TS_TI1FP1:
{
/* Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
/* Configure TI1 Filter and Polarity */
TIM_TI1_ConfigInputStage(htim->Instance,
sSlaveConfig->TriggerPolarity,
sSlaveConfig->TriggerFilter);
break;
}
case TIM_TS_TI2FP2:
{
/* Check the parameters */
assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
assert_param(IS_TIM_TRIGGERPOLARITY(sSlaveConfig->TriggerPolarity));
assert_param(IS_TIM_TRIGGERFILTER(sSlaveConfig->TriggerFilter));
/* Configure TI2 Filter and Polarity */
TIM_TI2_ConfigInputStage(htim->Instance,
sSlaveConfig->TriggerPolarity,
sSlaveConfig->TriggerFilter);
break;
}
case TIM_TS_ITR0:
case TIM_TS_ITR1:
case TIM_TS_ITR2:
case TIM_TS_ITR3:
{
/* Check the parameter */
assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
break;
}
default:
break;
}
return HAL_OK;
}
函数整体分成两个部分:
在设置SMCR寄存器中,实现了两个功能:
/**
* @brief Configure the Polarity and Filter for TI1.
* @param TIMx to select the TIM peripheral.
* @param TIM_ICPolarity The Input Polarity.
* This parameter can be one of the following values:
* @arg TIM_ICPOLARITY_RISING
* @arg TIM_ICPOLARITY_FALLING
* @arg TIM_ICPOLARITY_BOTHEDGE
* @param TIM_ICFilter Specifies the Input Capture Filter.
* This parameter must be a value between 0x00 and 0x0F.
* @retval None
*/
static void TIM_TI1_ConfigInputStage(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICFilter)
{
uint32_t tmpccmr1;
uint32_t tmpccer;
/* Disable the Channel 1: Reset the CC1E Bit */
tmpccer = TIMx->CCER;
TIMx->CCER &= ~TIM_CCER_CC1E;
tmpccmr1 = TIMx->CCMR1;
/* Set the filter */
tmpccmr1 &= ~TIM_CCMR1_IC1F;
tmpccmr1 |= (TIM_ICFilter << 4U);
/* Select the Polarity and set the CC1E Bit */
tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
tmpccer |= TIM_ICPolarity;
/* Write to TIMx CCMR1 and CCER registers */
TIMx->CCMR1 = tmpccmr1;
TIMx->CCER = tmpccer;
}
该函数和TIM_TI1_SetConfig
的功能基本一致,该函数参见博客
函数TIM_TI1_ConfigInputStage
的功能为:
函数TIM_TI1_SetConfig
与之相比就是多了一个设置信号源功能。
/**
* @brief TIM8 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {
0};
TIM_SlaveConfigTypeDef sSlaveConfig = {
0};
TIM_MasterConfigTypeDef sMasterConfig = {
0};
TIM_IC_InitTypeDef sConfigIC = {
0};
/* USER CODE BEGIN TIM8_Init 1 */
/* USER CODE END TIM8_Init 1 */
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 0xffff;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &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(&htim8, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
if (HAL_TIM_IC_ConfigChannel(&htim8, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
/* USER CODE END TIM8_Init 2 */
}
该函数总共调用了以下几个库函数:
其中,前三个步骤的代码其实是重复的。HAL_TIM_IC_Init
函数中会自动调用HAL_TIM_Base_Init
。
为当选择内部时钟为时钟源时,第二步是没有意义的:
case TIM_CLOCKSOURCE_INTERNAL:
{
assert_param(IS_TIM_INSTANCE(htim->Instance));
break;
}
后面的代码与keil的版本相同,不再详细解释。