
本文共 16543 字,大约阅读时间需要 55 分钟。
文章目录
前言
本文主要讲解自己实现的捕获程序。本文程序借鉴了正点原子的例程。关于正点原子的程序详解,可以参考博客<>。
本文介绍的程序的不同主要如下:
- 通过高级时钟TIM8实现捕获功能。
- 通过通道1和通道2分别捕获PC6管脚上的上升沿与下降沿。
- 使用定时器的复位模式,实现硬件复位。
本文使用的HAL库的版本为:STM32Cube_FW_F4_V1.25.0
本文使用的STM32CubeMX版本为:6.1.1该工程的下载地址为:
- keil版本:
- Cube版本:
结构体
/** * @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;
SlaveMode(从模式)
从模式的参数选择如下:
/** @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 *//** * @} */
从模式的在寄存器上对应解释如下:

InputTrigger(触发源)
触发源的参数选择如下:
/** @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 *//** * @} */
其对应寄存器解释为:

TriggerPolarity(触发极性)
/** @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 *//** * @} */

TriggerPrescaler(外部触发预分频)
/** @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. *//** * @} */
TriggerFilter(外部触发滤波器)
keil版本
初始化
/** * @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个部分:
- RCC时钟初始化
- 基本时钟初始化
- 输入通道配置设置
- 设置SLAVE模式
- 启动定时器
在基本时钟初始化中,不同于正点原子程序,本文同时使用通道1和通道2分别捕获PC6的上升沿和下降沿。对应的,在第5步中,启动定时器tim8的通道1和通道2。
在第4步中,设置RESET模式。使用到结构体TIM_SlaveConfigTypeDef
。其具体定义如上文详解。
需要尤其注意的是,第5步中,__HAL_TIM_URS_ENABLE(&htim8)启动URS功能。该功能只能减少触发更新中断的中断源。只有当计数器溢出时,才会触发更新中断。
从模式设置
整个从模式设置,在HAL库中的调用顺序为:
HAL_TIM_SlaveConfigSynchro()//调用子函数,禁用Trigger的中断与DMATIM_SlaveTimer_SetConfig()//设置SMCR,根据触发源分别调用子函数TIM_TI1_ConfigInputStage()//
下面分别介绍:
HAL_TIM_SlaveConfigSynchro
/** * @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;}
整体分成三个部分:
- 参数检测。
- 调用子函数
- 禁用Trigger中断和DMA。
其中在配置之前将定时器句柄上锁,且句柄的状态变为BUSY。
在配置之后,将定时器句柄解锁,且句柄状态变为READY。TIM_SlaveTimer_SetConfig
/** * @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寄存器
- 根据触发源不同分别调用子函数。
在设置SMCR寄存器中,实现了两个功能:
- 设置触发源
- 设置从模式的模式
TIM_TI1_ConfigInputStage
/** * @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
的功能为:
- 禁用通道1.
- 设置通道1滤波器。
- 设置极性。
- 将临时值写入寄存器。
函数TIM_TI1_SetConfig
与之相比就是多了一个设置信号源功能。
CUBE版本
初始化函数
/** * @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_Base_Init:基本时钟初始化
- HAL_TIM_ConfigClockSource:时钟源
- HAL_TIM_IC_Init:基本时钟初始化
- HAL_TIM_SlaveConfigSynchro:从模式初始化
- HAL_TIMEx_MasterConfigSynchronization:主模式初始化
- HAL_TIM_IC_ConfigChannel:输入通道配置
其中,前三个步骤的代码其实是重复的。HAL_TIM_IC_Init
函数中会自动调用HAL_TIM_Base_Init
。
为当选择内部时钟为时钟源时,第二步是没有意义的:
case TIM_CLOCKSOURCE_INTERNAL: { assert_param(IS_TIM_INSTANCE(htim->Instance)); break; }
后面的代码与keil的版本相同,不再详细解释。
发表评论
最新留言
关于作者
