Timer定时器

本文详细介绍了STM32中的定时器分类及功能,包括基本定时器、通用定时器和高级定时器。重点阐述了时基单元的构成,如预分频器、计数器和自动重装载寄存器的工作原理。此外,还提供了TIM系列函数的使用说明,以及如何通过配置时钟、初始化结构体、设置中断等步骤来初始化和使用定时器。最后,展示了TIM2中断服务例程的编写方法。
摘要由CSDN通过智能技术生成

定时器在STM32中可以说是最复杂的了,麻烦!

定时器分类:基本定时器,通用定时器,高级定时器

基本定时器:拥有定时中断,主模式触发DAC的功能

通用定时器拥有基本定时器全部功能,并有格外的内部时钟源选择,输入捕获,输出比较,编码器接口,主从触发模式

高级定时器:拥有通用定时器全部功能,格外还有重复计数器,死区生成,互补输出,刹车输入等功能

基本定时器:

其中最重要的是时基单元:由预分频器,计数器,自动重装载寄存器三个部分构成时基单元

预分频器的时钟是由基准计数时钟的输入的

 基准计数时钟又是从内部时钟CK_INT经过控制器来的(基本定时只能选择内部时钟)

内部时钟CK_INT又是由系统主频TRCC_IM_CLK决定的

预分频器:(16位)

把这个看成一根火腿肠,写0就是切0刀

写1就是切1刀,切一刀就是两节,这里分频也是一样的写0就是不分频,写1就是2分频,依次内推

分频系数可以是0-65535

自动重装载寄存器(16位)

这就是我们要计数到多少所填的值和下面计数器相关联的,我想让它计数到100就重新开始计数那么就填100

计数器(16位)

因为我们输入的时钟实际就是一个方波一样的高低变化着的波形,计数器呢捕获到1个上升沿计数+1,直到计数到我们的自动重装载寄存器所设置的值,那么就会产生一个中断信号(跟新中断)把计数器置0重新开始计时(从0开始持续计数到一定数又清0重新开始这个我们一般叫做向上计数模式)。大概执行过程就是这样子的。

主模式触发DAC:

提供跟新事件把主模式映射到TRGO,然后TRGO就直接触发DAC,这点我也没搞懂下次补上

通用定时器:

我看着都头疼,既然头疼就算了吧

接下来对TIM 常用函数介绍

void TIM_DeInit(TIM_TypeDef* TIMx);
调用该函数会清除TIM的配置和之前讲到的中断一样
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
这个函数配置时基单元的,参数1时选择定时器几TIMx,参数2是一个结构体地址,初始化结构体的成员后把结构体传入进来
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
这个函数是使能定时器的,第一个参数就是选择定时器TIMx,第二个参数就是使能或者不使能
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
这个函数是用来使能中断输出信号的,参数1选择定时器,第二个选择要配置哪个中断输出,第三个参数是能还是不使能

接下来是对时钟的配置

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择ITRx其他定时器的时钟 参数1是定时器几,参数2选择要接入哪个其他的定时器
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
选择TIx捕获通道的时钟,第一个参数:定时器几,第二个参数:选择TIx具体的某个引脚,最后两个参数是极性和滤波器
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
选择ETR1通过外部时钟模式1输入的时钟
参数1:选择定时器几,参数2:可以对外部时钟再做一个分频,参数3和4也是极性和滤波器
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
选择ETR2通过外部时钟模式2输入的时钟,参数和上边哪个ETR1一样的
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);
这个函数用于单独配置ETR的预分频器,极性,滤波器这些

更改计数器模式

void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
参数1:定时器几,参数2:选择新的计数器模式

更改预分频置函数

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
这个函数用来单独写预分频值的,第一个参数:定时器几,第而个参数:要写入的预分频值,第三个参数:写入的模式

自动重装器预装功能配置

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
配置自动重装器的功能配置,传入指定定时器,选择使能或者不使能就行了

给计数器写入一个值

void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
给计数器写入一个值

给自动重装器写入一个值

void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
给自动重装器写入一个值

查看当前计数器值

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
查看当前计数器计数到什么值了

获取当前预分频的值

uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
获取当前预分频的值

标志位的获取和清除

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
检查指定的 TIM 标志位设置与否
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
清除TIM 标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
检查指定的 TIM 中断发生与否
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
清除中断标志位

/*********************************************************************************************************/

代码实现:还是一步步来(通用定时器TIM2挂载到APB1的)

首先从结构框图可以看出TIM1是在APB2总线上的,我们反其道而行之选择APB1上面的TIM2所以我们要初始化APB1的时钟

1.开启时钟

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
}

2.配置时基单元时钟 (我们选择内部时钟)也可以选择其他时钟参考最上面的时钟配置

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置
  TIM_InternalClockConfig(TIM2);
}

3.初始化时基单元结构体

void Time_Init(void)
{
	//开启APB1时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元 先初始化结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
}

4. 选择分频一般是1分频

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//选择分频一般是1分频
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
}

5.计数模式

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//选择分频一般是1分频
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	//计数模式 向上计数
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
}

6.配置三个时基单元的重要寄存器,把结构体传入进去

这里补充一下:

赫兹代表的是运动的频率,1s就是1hz代表1秒运动一次,10k赫兹代表1秒运动10000次

注意哈!既然10k赫兹代表1秒运动10000次,那么10k赫兹频率下运动一次是多少呢?

1s=1000 ms=1000000 us;

1000000us/10k赫兹(10000)=100us(也就是运动一次是100us)

我没有刻意去记住那些换算单位,这是我自己配置时候用的方法大家可以参考一下。

再举个例子我如果定时500ms该咋个操作呢?

一样的还是采用改一下计数周期就行了,既然运动一次是100us,我运动5000次不就是500ms吗

我运动10000次不就是1s咯

void Time_Init(void)
{
	//开启APB1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//选择分频一般是1分频
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	//计数模式
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;

	//分频为7200也就是分频后是10k赫兹  每间隔100us计数加1

	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;

    //加到10000次后,也就10000*1000us,也就是1s后清零重新开始

    TIM_TimeBaseInitStruct.TIM_Period=10000-1;


	//RepetitionCounter是重复计数器的值 高级定时器才有
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
}

7.更新中断(去tim.h里面找)

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//选择分频一般是1分频
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	//计数模式
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;

	TIM_TimeBaseInitStruct.TIM_Period=10000-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;

	//RepetitionCounter是重复计数器的值 高级定时器才有
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	//更新中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
}

8.配置NVIC并启动定时器(我上一章EXTI里面有详细讲解如何配置NVIC)

void Time_Init(void)
{
	//开启APB1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//选择分频一般是1分频
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	//计数模式
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	
	TIM_TimeBaseInitStruct.TIM_Period=10000-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;

	//RepetitionCounter是重复计数器的值 高级定时器才有
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;

	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
     //清除标志位
		TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	//更新中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	//配置NVIC
	//先分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	//优先级
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);
	
}

9.去启动文件里面找到中断函数名称

void TIM2_IRQHandler(void)
{
	//判断中断标志位是否置为1
	if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==1)
	{
	  //清除标志位
		TIM_ClearFlag(TIM2,TIM_FLAG_Update);
      //填自己要执行的代码
	
	}

}

10.补充

extern :表示该变量外部已经声明过了,是外部变量

下面是实际运行效果

进入中断LED翻转一次(10ms) 

每隔500ms进入一次TIM中断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值