声明:本博客为哔哩哔哩up主@江协科技 “STM32入门教程”的听课笔记,仅供学习、参考使用,不得用作其他用途,违者必究。如有版权问题,请联系作者修改。
目录
一、综述
二、TIM库
初始化
2.1、TIM_DeInit 恢复缺省值
2.2、TIM_TimeBaseInit 时基单元初始化
2.3、 TIM_TimeBaseStructInit 时基单元结构体默认赋值
2.4、TIM_Cmd 使能计数器
2.5、TIM_ITConfig 使能中断输出信号
2.6、TIM_InternalClockConfig 选择内部时钟
2.7、TIM_ITRxExternalClockConfig
2.8、TIM_TIxExternalClockConfig
2.9、TIM_ETRClockMode1Config
2.10、TIM_ETRClockMode2Config
2.11、TIM_ETRConfig
2.12、6~11讲解
2.13、TIM结构体讲解
2.13.1、TIM_ClockDivision
2.13.2、TIM_CounterMode
2.13.3、TIM_Period
2.13.4、TIM_Prescaler
2.13.5、TIM_RepetitionCounter
参数变更
2.14、TIM_PrescalerConfig 单独写入预分频值
2.15、TIM_CounterModeConfig 改变计数器的计数模式
2.16、TIM_ARRPreloadConfig自动重装器预装功能配置
2.17、TIM_SetCounter 给计数器写入一个值
2.18、TIM_SetAutoreload 写入自动重装值
2.19、TIM_GetCounter 获取当前计数器的值
2.20、TIM_GetPrescaler 获取当前分频器的值
2.21、TIM_GetFlagStatus 主程序标志位获取
2.22、TIM_ClearFlag 主程序中清除标志位
2.23、TIM_GetITStatus 中断程序中获取标志位
2.24、TIM_ClearITPendingBit 中断程序中清除标志位
三、定时中断完整代码
3.1、Timer.h
3.2、Timer.c
3.2.1、具体步骤
3.2.2、完整代码
3.3、main.c
四、一个问题
五、定时中断完整代码(调整后)
5.1、Timer.h
5.2、Timer.c
5.2.1、具体步骤
5.2.2、完整代码
5.3、main.c
一、综述
上图是定时中断的整个框架结构。我们只需要把这里面的每个模块都打通,就可以让定时器工作了。
具体步骤
第一步:RCC开启时钟。在这里打开时钟之后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了。
第二步:选择时基单元的时钟源。对于定时中断,我们选择内部时钟源。
第三步:配置时基单元。包括这里的与分频器,计数器计数模式,自动重装器等等。这些参数用一个结构体就可以配置好了。
第四步:配置输出中断控制,允许更行中断输出到NVIC。
第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。
整个模块配置完成之后我们还需要使能一下计数器,不然计数器是不会运行的。当定时器使能后,计时器就会开始报数了。当计时器更新时,触发中断。最后再写一个定时器的中断函数,这样这个中断函数每隔一段时间就能自动执行一次了。
二、TIM库
初始化
2.1、TIM_DeInit 恢复缺省值
函数原型:void TIM_DeInit(TIM_TypeDef* TIMx);
函数讲解:恢复缺省配置;
函数定义:
/**
* @brief Deinitializes the TIMx peripheral registers to their default reset values.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @retval None
*/
void TIM_DeInit(TIM_TypeDef* TIMx)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
if (TIMx == TIM1)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, DISABLE);
}
else if (TIMx == TIM2)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2, DISABLE);
}
else if (TIMx == TIM3)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, DISABLE);
}
else if (TIMx == TIM4)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM4, DISABLE);
}
else if (TIMx == TIM5)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM5, DISABLE);
}
else if (TIMx == TIM6)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM6, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM6, DISABLE);
}
else if (TIMx == TIM7)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM7, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM7, DISABLE);
}
else if (TIMx == TIM8)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM8, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM8, DISABLE);
}
else if (TIMx == TIM9)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM9, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM9, DISABLE);
}
else if (TIMx == TIM10)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM10, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM10, DISABLE);
}
else if (TIMx == TIM11)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM11, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM11, DISABLE);
}
else if (TIMx == TIM12)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM12, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM12, DISABLE);
}
else if (TIMx == TIM13)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM13, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM13, DISABLE);
}
else if (TIMx == TIM14)
{
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM14, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM14, DISABLE);
}
else if (TIMx == TIM15)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM15, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM15, DISABLE);
}
else if (TIMx == TIM16)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM16, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM16, DISABLE);
}
else
{
if (TIMx == TIM17)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM17, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM17, DISABLE);
}
}
}
2.2、TIM_TimeBaseInit 时基单元初始化
函数原型:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx,
TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
函数讲解:时基单元初始化;两个参数:第一个TIMx选择某个定时器,第二个是结构体,里面包含了配置时基单元的一些参数。
函数定义:
/**
* @brief Initializes the TIMx Time Base Unit peripheral according to
* the specified parameters in the TIM_TimeBaseInitStruct.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_TimeBaseInitStruct: pointer to a TIM_TimeBaseInitTypeDef
* structure that contains the configuration information for the
* specified TIM peripheral.
* @retval None
*/
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
uint16_t tmpcr1 = 0;
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
tmpcr1 = TIMx->CR1;
if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
(TIMx == TIM4) || (TIMx == TIM5))
{
/* Select the Counter Mode */
tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
}
if((TIMx != TIM6) && (TIMx != TIM7))
{
/* Set the clock division */
tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
}
TIMx->CR1 = tmpcr1;
/* Set the Autoreload value */
TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
/* Set the Prescaler value */
TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
{
/* Set the Repetition Counter value */
TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
}
/* Generate an update event to reload the Prescaler and the Repetition counter
values immediately */
TIMx->EGR = TIM_PSCReloadMode_Immediate;
}
2.3、 TIM_TimeBaseStructInit 时基单元结构体默认赋值
函数原型:
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
函数讲解:这个函数可以把结构体变量赋一个默认值。
函数定义:
/**
* @brief Fills each TIM_TimeBaseInitStruct member with its default value.
* @param TIM_TimeBaseInitStruct : pointer to a TIM_TimeBaseInitTypeDef
* structure which will be initialized.
* @retval None
*/
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
/* Set the default configuration */
TIM_TimeBaseInitStruct->TIM_Period = 0xFFFF;
TIM_TimeBaseInitStruct->TIM_Prescaler = 0x0000;
TIM_TimeBaseInitStruct->TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct->TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct->TIM_RepetitionCounter = 0x0000;
}
2.4、TIM_Cmd 使能计数器
函数原型:void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
函数讲解:这个函数是用来使能计数器的,对应图中的“运行控制”板块。它有两个参数,第一个TIMx选择定时器,第二个NewState新的状态,也就是使能还是失能。使能的话,计数器就可以运行,失能的话,计数器就不能运行。
函数定义:
/**
* @brief Enables or disables the specified TIM peripheral.
* @param TIMx: where x can be 1 to 17 to select the TIMx peripheral.
* @param NewState: new state of the TIMx peripheral.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the TIM Counter */
TIMx->CR1 |= TIM_CR1_CEN;
}
else
{
/* Disable the TIM Counter */
TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
}
}
2.5、TIM_ITConfig 使能中断输出信号
函数原型:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
函数讲解:这个是用来使能中断输出信号的,对应图中的“中断输出控制”。第一个参数TIMx,选择定时器;第二个参数TIM_IT,选择要配置哪个中断输出;第三个参数新的状态,使能还是失能。这种IT_Config之后会经常遇到,就是使能外设的中断输出。
第二个参数可以是下面这些值的任意组合:
* @arg TIM_IT_Update: TIM update Interrupt source
* @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
* @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
* @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
* @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
* @arg TIM_IT_COM: TIM Commutation Interrupt source
* @arg TIM_IT_Trigger: TIM Trigger Interrupt source
* @arg TIM_IT_Break: TIM Break Interrupt source
函数定义:
/**
* @brief Enables or disables the specified TIM interrupts.
* @param TIMx: where x can be 1 to 17 to select the TIMx peripheral.
* @param TIM_IT: specifies the TIM interrupts sources to be enabled or disabled.
* This parameter can be any combination of the following values:
* @arg TIM_IT_Update: TIM update Interrupt source
* @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
* @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
* @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
* @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
* @arg TIM_IT_COM: TIM Commutation Interrupt source
* @arg TIM_IT_Trigger: TIM Trigger Interrupt source
* @arg TIM_IT_Break: TIM Break Interrupt source
* @note
* - TIM6 and TIM7 can only generate an update interrupt.
* - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1,
* TIM_IT_CC2 or TIM_IT_Trigger.
* - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1.
* - TIM_IT_Break is used only with TIM1, TIM8 and TIM15.
* - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17.
* @param NewState: new state of the TIM interrupts.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_IT(TIM_IT));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the Interrupt sources */
TIMx->DIER |= TIM_IT;
}
else
{
/* Disable the Interrupt sources */
TIMx->DIER &= (uint16_t)~TIM_IT;
}
}
2.6、TIM_InternalClockConfig 选择内部时钟
函数原型:void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
函数讲解:选择内部时钟。参数只有一个TIMx。
函数定义:
/**
* @brief Configures the TIMx internal Clock
* @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15
* to select the TIM peripheral.
* @retval None
*/
void TIM_InternalClockConfig(TIM_TypeDef* TIMx)
{
/* Check the parameters */
assert_param(IS_TIM_LIST6_PERIPH(TIMx));
/* Disable slave mode to clock the prescaler directly with the internal clock */
TIMx->SMCR &= (uint16_t)(~((uint16_t)TIM_SMCR_SMS));
}
2.7、TIM_ITRxExternalClockConfig
函数原型:
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx,
uint16_t TIM_InputTriggerSource);
函数讲解:选择ITR其他定时器的时钟。第一个参数是TIMx,选择要配置的定时器,二个参s数是TIM_InputTriggerSource,选择要接入哪个其他的定时器。
函数定义:
/**
* @brief Configures the TIMx Internal Trigger as External Clock
* @param TIMx: where x can be 1, 2, 3, 4, 5, 9, 12 or 15 to select the TIM peripheral.
* @param TIM_ITRSource: Trigger source.
* This parameter can be one of the following values:
* @param TIM_TS_ITR0: Internal Trigger 0
* @param TIM_TS_ITR1: Internal Trigger 1
* @param TIM_TS_ITR2: Internal Trigger 2
* @param TIM_TS_ITR3: Internal Trigger 3
* @retval None
*/
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
{
/* Check the parameters */
assert_param(IS_TIM_LIST6_PERIPH(TIMx));
assert_param(IS_TIM_INTERNAL_TRIGGER_SELECTION(TIM_InputTriggerSource));
/* Select the Internal Trigger */
TIM_SelectInputTrigger(TIMx, TIM_InputTriggerSource);
/* Select the External clock mode1 */
TIMx->SMCR |= TIM_SlaveMode_External1;
}
2.8、TIM_TIxExternalClockConfig
函数原型:
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx,
uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
函数讲解:选择TIx捕获通道的时钟,第一个参数TIMx选择需要配置的定时器;第二个参数TIM_TIxExternalCLKSource,选择TIx具体的某个引脚;第三个参数TIM_ICPolarity输入的极性;第四个参数ICFilter,输入的滤波器。对于外部引脚的波形,一般都会有极性选择和滤波器,这样更灵活一些。
函数定义:
/**
* @brief Configures the TIMx Trigger as External Clock
* @param TIMx: where x can be 1, 2, 3, 4, 5, 9, 12 or 15 to select the TIM peripheral.
* @param TIM_TIxExternalCLKSource: Trigger source.
* This parameter can be one of the following values:
* @arg TIM_TIxExternalCLK1Source_TI1ED: TI1 Edge Detector
* @arg TIM_TIxExternalCLK1Source_TI1: Filtered Timer Input 1
* @arg TIM_TIxExternalCLK1Source_TI2: Filtered Timer Input 2
* @param TIM_ICPolarity: specifies the TIx Polarity.
* This parameter can be one of the following values:
* @arg TIM_ICPolarity_Rising
* @arg TIM_ICPolarity_Falling
* @param ICFilter : specifies the filter value.
* This parameter must be a value between 0x0 and 0xF.
* @retval None
*/
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter)
{
/* Check the parameters */
assert_param(IS_TIM_LIST6_PERIPH(TIMx));
assert_param(IS_TIM_TIXCLK_SOURCE(TIM_TIxExternalCLKSource));
assert_param(IS_TIM_IC_POLARITY(TIM_ICPolarity));
assert_param(IS_TIM_IC_FILTER(ICFilter));
/* Configure the Timer Input Clock Source */
if (TIM_TIxExternalCLKSource == TIM_TIxExternalCLK1Source_TI2)
{
TI2_Config(TIMx, TIM_ICPolarity, TIM_ICSelection_DirectTI, ICFilter);
}
else
{
TI1_Config(TIMx, TIM_ICPolarity, TIM_ICSelection_DirectTI, ICFilter);
}
/* Select the Trigger source */
TIM_SelectInputTrigger(TIMx, TIM_TIxExternalCLKSource);
/* Select the External clock mode1 */
TIMx->SMCR |= TIM_SlaveMode_External1;
}
2.9、TIM_ETRClockMode1Config
函数原型:
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx,
uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
函数原型:选择ETR通过外部时钟模式1输入的时钟。第二个参数TIM_ExtTRGPrescaler,外部触发预分频器,可以对ETR的外部时钟再提前做一个分频,第三个参数TIM_ExtTRGPolarity输入的极性,第四个参数ExtTRGFilter输入的滤波器。
函数定义:
/**
* @brief Configures the External clock Mode1
* @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral.
* @param TIM_ExtTRGPrescaler: The external Trigger Prescaler.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF.
* @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2.
* @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4.
* @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8.
* @param TIM_ExtTRGPolarity: The external Trigger Polarity.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active.
* @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active.
* @param ExtTRGFilter: External Trigger Filter.
* This parameter must be a value between 0x00 and 0x0F
* @retval None
*/
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter)
{
uint16_t tmpsmcr = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST3_PERIPH(TIMx));
assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler));
assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity));
assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter));
/* Configure the ETR Clock source */
TIM_ETRConfig(TIMx, TIM_ExtTRGPrescaler, TIM_ExtTRGPolarity, ExtTRGFilter);
/* Get the TIMx SMCR register value */
tmpsmcr = TIMx->SMCR;
/* Reset the SMS Bits */
tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_SMS));
/* Select the External clock mode1 */
tmpsmcr |= TIM_SlaveMode_External1;
/* Select the Trigger selection : ETRF */
tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_TS));
tmpsmcr |= TIM_TS_ETRF;
/* Write to TIMx SMCR */
TIMx->SMCR = tmpsmcr;
}
2.10、TIM_ETRClockMode2Config
函数原型:
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx,
uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
函数讲解:
选择ETR通过外部时钟模式2输入的时钟。参数同上。对于ETR输入的时钟而言,9和10两个函数是等效的,它们的参数也是一样的。如果不需要触发输入的功能,那两个函数可以互换。
函数定义:
/**
* @brief Configures the External clock Mode2
* @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral.
* @param TIM_ExtTRGPrescaler: The external Trigger Prescaler.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF.
* @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2.
* @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4.
* @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8.
* @param TIM_ExtTRGPolarity: The external Trigger Polarity.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active.
* @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active.
* @param ExtTRGFilter: External Trigger Filter.
* This parameter must be a value between 0x00 and 0x0F
* @retval None
*/
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter)
{
/* Check the parameters */
assert_param(IS_TIM_LIST3_PERIPH(TIMx));
assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler));
assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity));
assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter));
/* Configure the ETR Clock source */
TIM_ETRConfig(TIMx, TIM_ExtTRGPrescaler, TIM_ExtTRGPolarity, ExtTRGFilter);
/* Enable the External clock mode2 */
TIMx->SMCR |= TIM_SMCR_ECE;
}
2.11、TIM_ETRConfig
函数原型:
void TIM_ETRConfig(TIM_TypeDef* TIMx,
uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
函数讲解:单独用来配置ETR引脚的预分频器、极性、滤波器这些参数的。
函数定义:
/**
* @brief Configures the TIMx External Trigger (ETR).
* @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral.
* @param TIM_ExtTRGPrescaler: The external Trigger Prescaler.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF.
* @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2.
* @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4.
* @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8.
* @param TIM_ExtTRGPolarity: The external Trigger Polarity.
* This parameter can be one of the following values:
* @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active.
* @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active.
* @param ExtTRGFilter: External Trigger Filter.
* This parameter must be a value between 0x00 and 0x0F
* @retval None
*/
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter)
{
uint16_t tmpsmcr = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST3_PERIPH(TIMx));
assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler));
assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity));
assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter));
tmpsmcr = TIMx->SMCR;
/* Reset the ETR Bits */
tmpsmcr &= SMCR_ETR_Mask;
/* Set the Prescaler, the Filter value and the Polarity */
tmpsmcr |= (uint16_t)(TIM_ExtTRGPrescaler | (uint16_t)(TIM_ExtTRGPolarity | (uint16_t)(ExtTRGFilter << (uint16_t)8)));
/* Write to TIMx SMCR */
TIMx->SMCR = tmpsmcr;
}
2.12、6~11讲解
上面这六个函数对应的就是时基单元的时钟选择部分,可以选择RCC内部时钟、ETR外部时钟、ITRx其他定时器、TIx捕获通道等。
2.13、TIM结构体讲解
typedef struct
{
uint16_t TIM_Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock.
This parameter can be a number between 0x0000 and 0xFFFF */
uint16_t TIM_CounterMode; /*!< Specifies the counter mode.
This parameter can be a value of @ref TIM_Counter_Mode */
uint16_t TIM_Period; /*!< Specifies the period value to be loaded into the active
Auto-Reload Register at the next update event.
This parameter must be a number between 0x0000 and 0xFFFF. */
uint16_t TIM_ClockDivision; /*!< Specifies the clock division.
This parameter can be a value of @ref TIM_Clock_Division_CKD */
uint8_t TIM_RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounter
reaches zero, an update event is generated and counting restarts
from the RCR value (N).
This means in PWM mode that (N+1) corresponds to:
- the number of PWM periods in edge-aligned mode
- the number of half PWM period in center-aligned mode
This parameter must be a number between 0x00 and 0xFF.
@note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;
2.13.1、TIM_ClockDivision
指定时钟分频,参数可以是TIM_Clock_Division_CKD中的一个值。
时钟分频的作用:
在定时器的外部信号输入引脚, 一般都会有滤波器用来过滤掉信号抖动的干扰。
工作原理是:在一个固定的时钟频率f下进行采样,如果连续N个采样点都为相同的电平,那就代表输入信号稳定了,就把这个采样值输出出去;如果这N个采样值不全都相同,那就说明信号有抖动,这时就保持上一次的输出或者直接输出低电平。这样就能保证输出信号在一定程度上的滤波。
这里的采样频率f和采样点数N都是滤波器的参数。频率越低,采样点数越多,滤波效果越好,不过相应的信号延迟就越大。
滤波器的采样频率f的来源:STM32手册中声明它可以由内部时钟直接而来,也可以是由内部时钟加一个时钟分频而来。分频多少就是由这个参数TIM_ClockDivision决定的。
(这个参数和时基单元关系并不大)
TIM_Clock_Division_CKD:
/** @defgroup TIM_Clock_Division_CKD
* @{
*/
#define TIM_CKD_DIV1 ((uint16_t)0x0000)
#define TIM_CKD_DIV2 ((uint16_t)0x0100)
#define TIM_CKD_DIV4 ((uint16_t)0x0200)
TIM_CKD_DIV1是1分频,即不分频;
TIM_CKD_DIV2是2分频;
TIM_CKD_DIV4是4分频;
2.13.2、TIM_CounterMode
计数器模式。TIM_CounterMode可以是TIM_Counter_Mode中的一个值。
/** @defgroup TIM_Counter_Mode
* @{
*/
#define TIM_CounterMode_Up ((uint16_t)0x0000)
#define TIM_CounterMode_Down ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3 ((uint16_t)0x0060)
#define IS_TIM_COUNTER_MODE(MODE) (((MODE) == TIM_CounterMode_Up) || \
((MODE) == TIM_CounterMode_Down) || \
((MODE) == TIM_CounterMode_CenterAligned1) || \
((MODE) == TIM_CounterMode_CenterAligned2) || \
((MODE) == TIM_CounterMode_CenterAligned3))
从上到下分别是向上计数、向下计数、三种中央对齐模式。
2.13.3、TIM_Period
周期,就是ARR自动重装器的值。时基单元中关键寄存器的参数之一。
计算方法:
注意:
1.预分频器和计数器都有一个数的偏差,所以使用的时候要-1。、
2.PSC和ARR的取值都要在0~65535之间,不要超出范围。
3.PSC和ARR的取值不是唯一的。可以预分频值给少一点、自动重装给多一点,这样就是以一个比较高的频率计比较多的数;也可以预分频值给多一点、自动重装给少一点,这样就是以一个比较低的频率计比较少的数。两种方法都可以达到目标的定时时间。
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
在这里我们是对72MHz进行7200分频,得到的是10k的计数频率。 在10k的频率下计1w个数,正好是1s的时间。
2.13.4、TIM_Prescaler
PSC预分频器的值。时基单元中关键寄存器的参数之一。
2.13.5、TIM_RepetitionCounter
重复计数器的值。时基单元中关键寄存器的参数之一。是高级定时器才有的。
参数变更
2.14、TIM_PrescalerConfig 单独写入预分频值
函数原型:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler,
uint16_t TIM_PSCReloadMode);
函数讲解:
这个函数是用来单独写预分频值的。第一个参数Prescaler就是要写入的预分频值,第二个参数TIM_PSCReloadMode是指写入的模式。
预分频器有一个缓冲器,写入的值是在更新时间发生后才有效的。所以这里有个写入的模式,可以选择听从安排,在更新事件生效,或者是在写入后手动产生一个更新事件,让这个值立刻生效。
函数定义:
/**
* @brief Configures the TIMx Prescaler.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param Prescaler: specifies the Prescaler Register value
* @param TIM_PSCReloadMode: specifies the TIM Prescaler Reload mode
* This parameter can be one of the following values:
* @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event.
* @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately.
* @retval None
*/
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_PRESCALER_RELOAD(TIM_PSCReloadMode));
/* Set the Prescaler value */
TIMx->PSC = Prescaler;
/* Set or reset the UG Bit */
TIMx->EGR = TIM_PSCReloadMode;
}
2.15、TIM_CounterModeConfig 改变计数器的计数模式
函数原型:void TIM_CounterModeConfig(TIM_TypeDef* TIMx,
uint16_t TIM_CounterMode);
函数讲解:这个函数用于改变计数器的计数模式。参数TIM_CounterMode选择新的计数器模式。
函数定义:
/**
* @brief Specifies the TIMx Counter Mode to be used.
* @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral.
* @param TIM_CounterMode: specifies the Counter Mode to be used
* This parameter can be one of the following values:
* @arg TIM_CounterMode_Up: TIM Up Counting Mode
* @arg TIM_CounterMode_Down: TIM Down Counting Mode
* @arg TIM_CounterMode_CenterAligned1: TIM Center Aligned Mode1
* @arg TIM_CounterMode_CenterAligned2: TIM Center Aligned Mode2
* @arg TIM_CounterMode_CenterAligned3: TIM Center Aligned Mode3
* @retval None
*/
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode)
{
uint16_t tmpcr1 = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST3_PERIPH(TIMx));
assert_param(IS_TIM_COUNTER_MODE(TIM_CounterMode));
tmpcr1 = TIMx->CR1;
/* Reset the CMS and DIR Bits */
tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
/* Set the Counter Mode */
tmpcr1 |= TIM_CounterMode;
/* Write to TIMx CR1 register */
TIMx->CR1 = tmpcr1;
}
2.16、TIM_ARRPreloadConfig自动重装器预装功能配置
函数原型:void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx,
FunctionalState NewState);
函数讲解:
自动重装器预装功能配置。有无预装是可以自己选择的。
选择方法:第二个参数NewState赋值为使能or失能。
函数定义:
/**
* @brief Enables or disables TIMx peripheral Preload register on ARR.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param NewState: new state of the TIMx peripheral Preload register
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Set the ARR Preload Bit */
TIMx->CR1 |= TIM_CR1_ARPE;
}
else
{
/* Reset the ARR Preload Bit */
TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_ARPE);
}
}
2.17、TIM_SetCounter 给计数器写入一个值
函数原型:void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
函数讲解:给计数器写入一个值。如果想手动给一个计数值,就可以用这个函数。
函数定义:
/**
* @brief Sets the TIMx Counter Register value
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param Counter: specifies the Counter register new value.
* @retval None
*/
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
/* Set the Counter Register value */
TIMx->CNT = Counter;
}
2.18、TIM_SetAutoreload 写入自动重装值
函数原型:void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
函数讲解:给自动重装器写入一个值。如果想手动给一个自动重装值,就可以用这个函数。
函数定义:
/**
* @brief Sets the TIMx Autoreload Register value
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param Autoreload: specifies the Autoreload register new value.
* @retval None
*/
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
/* Set the Autoreload Register value */
TIMx->ARR = Autoreload;
}
2.19、TIM_GetCounter 获取当前计数器的值
函数原型:uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
函数讲解:获取当前计数器的值。如果想要查看当前计数器计到哪里了,就可以调用一下这个函数,返回当前计数器的值。
函数定义:
/**
* @brief Gets the TIMx Counter value.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @retval Counter Register value.
*/
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
/* Get the Counter Register value */
return TIMx->CNT;
}
2.20、TIM_GetPrescaler 获取当前分频器的值
函数原型:uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
函数讲解:获取当前的预分频器的值。如果想看预分频值,就调用一下这个函数。
函数定义:
/**
* @brief Gets the TIMx Prescaler value.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @retval Prescaler Register value.
*/
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
/* Get the Prescaler Register value */
return TIMx->PSC;
}
2.21、TIM_GetFlagStatus 主程序标志位获取
函数原型:FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
函数讲解:主程序中获取标志位。
函数定义:
/**
* @brief Checks whether the specified TIM flag is set or not.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_FLAG: specifies the flag to check.
* This parameter can be one of the following values:
* @arg TIM_FLAG_Update: TIM update Flag
* @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag
* @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag
* @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag
* @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag
* @arg TIM_FLAG_COM: TIM Commutation Flag
* @arg TIM_FLAG_Trigger: TIM Trigger Flag
* @arg TIM_FLAG_Break: TIM Break Flag
* @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag
* @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag
* @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag
* @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag
* @note
* - TIM6 and TIM7 can have only one update flag.
* - TIM9, TIM12 and TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1,
* TIM_FLAG_CC2 or TIM_FLAG_Trigger.
* - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1.
* - TIM_FLAG_Break is used only with TIM1, TIM8 and TIM15.
* - TIM_FLAG_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17.
* @retval The new state of TIM_FLAG (SET or RESET).
*/
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{
ITStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_GET_FLAG(TIM_FLAG));
if ((TIMx->SR & TIM_FLAG) != (uint16_t)RESET)
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
2.22、TIM_ClearFlag 主程序中清除标志位
函数原型:void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
函数讲解:主程序中清除标志位。
函数定义:
/**
* @brief Clears the TIMx's pending flags.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_FLAG: specifies the flag bit to clear.
* This parameter can be any combination of the following values:
* @arg TIM_FLAG_Update: TIM update Flag
* @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag
* @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag
* @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag
* @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag
* @arg TIM_FLAG_COM: TIM Commutation Flag
* @arg TIM_FLAG_Trigger: TIM Trigger Flag
* @arg TIM_FLAG_Break: TIM Break Flag
* @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag
* @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag
* @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag
* @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag
* @note
* - TIM6 and TIM7 can have only one update flag.
* - TIM9, TIM12 and TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1,
* TIM_FLAG_CC2 or TIM_FLAG_Trigger.
* - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1.
* - TIM_FLAG_Break is used only with TIM1, TIM8 and TIM15.
* - TIM_FLAG_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17.
* @retval None
*/
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG));
/* Clear the flags */
TIMx->SR = (uint16_t)~TIM_FLAG;
}
2.23、TIM_GetITStatus 中断程序中获取标志位
函数原型:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
函数讲解:中断程序中获取标志位。
函数定义:
/**
* @brief Checks whether the TIM interrupt has occurred or not.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_IT: specifies the TIM interrupt source to check.
* This parameter can be one of the following values:
* @arg TIM_IT_Update: TIM update Interrupt source
* @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
* @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
* @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
* @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
* @arg TIM_IT_COM: TIM Commutation Interrupt source
* @arg TIM_IT_Trigger: TIM Trigger Interrupt source
* @arg TIM_IT_Break: TIM Break Interrupt source
* @note
* - TIM6 and TIM7 can generate only an update interrupt.
* - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1,
* TIM_IT_CC2 or TIM_IT_Trigger.
* - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1.
* - TIM_IT_Break is used only with TIM1, TIM8 and TIM15.
* - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17.
* @retval The new state of the TIM_IT(SET or RESET).
*/
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
ITStatus bitstatus = RESET;
uint16_t itstatus = 0x0, itenable = 0x0;
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_GET_IT(TIM_IT));
itstatus = TIMx->SR & TIM_IT;
itenable = TIMx->DIER & TIM_IT;
if ((itstatus != (uint16_t)RESET) && (itenable != (uint16_t)RESET))
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
2.24、TIM_ClearITPendingBit 中断程序中清除标志位
函数原型:void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
函数讲解:中断程序中清除标志位。
函数定义:
/**
* @brief Clears the TIMx's interrupt pending bits.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_IT: specifies the pending bit to clear.
* This parameter can be any combination of the following values:
* @arg TIM_IT_Update: TIM1 update Interrupt source
* @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
* @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
* @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
* @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
* @arg TIM_IT_COM: TIM Commutation Interrupt source
* @arg TIM_IT_Trigger: TIM Trigger Interrupt source
* @arg TIM_IT_Break: TIM Break Interrupt source
* @note
* - TIM6 and TIM7 can generate only an update interrupt.
* - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1,
* TIM_IT_CC2 or TIM_IT_Trigger.
* - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1.
* - TIM_IT_Break is used only with TIM1, TIM8 and TIM15.
* - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17.
* @retval None
*/
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_IT(TIM_IT));
/* Clear the IT pending Bit */
TIMx->SR = (uint16_t)~TIM_IT;
}
三、定时中断完整代码
3.1、Timer.h
#ifndef _TIMER_H
#define _TIMER_H
void timer_init(void);
#endif
3.2、Timer.c
3.2.1、具体步骤
第一步:RCC开启时钟。在这里打开时钟之后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了。
/*第一步:配置rcc时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
第二步:选择时基单元的时钟源。对于定时中断,我们选择内部时钟源。
TIM_InternalClockConfig(TIM2);
第三步:配置时基单元。包括这里的与分频器,计数器计数模式,自动重装器等等。这些参数用一个结构体就可以配置好了。
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
第四步:配置输出中断控制,允许更行中断输出到NVIC。
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
/*中断通道*/
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
整个模块配置完成之后我们还需要使能一下计数器,不然计数器是不会运行的。当定时器使能后,计时器就会开始报数了。当计时器更新时,触发中断。最后再写一个定时器的中断函数,这样这个中断函数每隔一段时间就能自动执行一次了。
TIM_Cmd(TIM2,ENABLE);
3.2.2、完整代码
#include "stm32f10x.h" // Device header
extern uint16_t num;
void timer_init(void)
{
/*第一步:配置rcc时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*配置时基单元*/
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
/*使能更新中断*/
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
/*此时已经开启了更新中断到NVIC的通路*/
/*配置NVIC通路*/
/*NVIC优先级分组:*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
/*中断通道*/
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
/*检查中断标志位*/
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
3.3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t num;
int main(void)
{
OLED_Init();
timer_init();/*初始化定时器*/
OLED_ShowString(1, 1, "num:");
while (1)
{
OLED_ShowNum(1,5,num,5);
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
四、一个问题
烧录代码观察现象,发现num是从1开始计数的。但是num的初始值为0,按理说应该是从0开始计数的,但是num一经上电就立刻变为1了,说明中断函数在初始化后就立刻进入了一次。
在TIM_TimeBaseInit的函数定义中:
/**
* @brief Initializes the TIMx Time Base Unit peripheral according to
* the specified parameters in the TIM_TimeBaseInitStruct.
* @param TIMx: where x can be 1 to 17 to select the TIM peripheral.
* @param TIM_TimeBaseInitStruct: pointer to a TIM_TimeBaseInitTypeDef
* structure that contains the configuration information for the
* specified TIM peripheral.
* @retval None
*/
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
uint16_t tmpcr1 = 0;
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
tmpcr1 = TIMx->CR1;
if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
(TIMx == TIM4) || (TIMx == TIM5))
{
/* Select the Counter Mode */
tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
}
if((TIMx != TIM6) && (TIMx != TIM7))
{
/* Set the clock division */
tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
}
TIMx->CR1 = tmpcr1;
/* Set the Autoreload value */
TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
/* Set the Prescaler value */
TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
{
/* Set the Repetition Counter value */
TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
}
/* Generate an update event to reload the Prescaler and the Repetition counter
values immediately */
TIMx->EGR = TIM_PSCReloadMode_Immediate;
}
其中最后一行代码上面的注释:
/* Generate an update event to reload the Prescaler and the Repetition counter values immediately */
翻译为:立刻生成一个更新事件,来重新装载预分频器和重复计数器的值。
TIM_TimeBaseInit函数中有 “TIMx->EGR = TIM_PSCReloadMode_Immediate;”这句的原因是:
预分频器是有一个缓冲寄存器的,我们写入的值只有在更新事件时才会真正起作用。所以这里为了让值立刻起作用,就在函数的最后手动生成了一个更新事件,这样,预分频器的值就有效了。
但同时,它的副作用就是更新事件和更新中断是同时发生的,更新中断会置更新标志位。当我们之后一旦初始化结束,更新中断就会立刻进入。这就是我们刚一上电就立刻进入中断的原因。
解决方法:
在TIM_TimeBaseInit()的后面,开启中断的前面,调用一下TIM_ClearFlag()函数,手动将TIM_FLAG_Update更新标志位清除一下,就能避免刚初始化结束就立刻进入中断的问题了。
五、定时中断完整代码(调整后)
5.1、Timer.h
#ifndef _TIMER_H
#define _TIMER_H
void timer_init(void);
#endif
5.2、Timer.c
5.2.1、具体步骤
第一步:RCC开启时钟。在这里打开时钟之后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
第二步:选择时基单元的时钟源。对于定时中断,我们选择内部时钟源。
TIM_InternalClockConfig(TIM2);
第三步:配置时基单元。包括这里的与分频器,计数器计数模式,自动重装器等等。这些参数用一个结构体就可以配置好了。
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
第四步:配置输出中断控制,允许更行中断输出到NVIC。
/*使能更新中断*/
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
/*中断通道*/
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
整个模块配置完成之后我们还需要使能一下计数器,不然计数器是不会运行的。当定时器使能后,计时器就会开始报数了。当计时器更新时,触发中断。最后再写一个定时器的中断函数,这样这个中断函数每隔一段时间就能自动执行一次了。
TIM_Cmd(TIM2,ENABLE);
5.2.2、完整代码
#include "stm32f10x.h" // Device header
extern uint16_t num;
void timer_init(void)
{
/*第一步:配置rcc时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*配置时基单元*/
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
/*使能更新中断*/
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
/*此时已经开启了更新中断到NVIC的通路*/
/*配置NVIC通路*/
/*NVIC优先级分组:*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
/*中断通道*/
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
/*检查中断标志位*/
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
5.3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t num;
int main(void)
{
OLED_Init();
timer_init();/*初始化定时器*/
OLED_ShowString(1, 1, "num:");
while (1)
{
OLED_ShowNum(1,5,num,5);
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}