STM32学习——TIM(Timer)定时器(1)

简介

定时器也是计数器,可以对输入的时钟进行计数,在计数值达到设定值时触发中断

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

定时器类型

类型编号总线功能
高级定时器TIM1、TIM8APB2有通用定时器的全部功能,并且额外有重复定时器、死区生成、互补输出、刹车输入等功能
通用定时器TIM2、TIM3、TIM4、TIM5APB1用于基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器TIM6、TIM7APB1定时中断、主模式触发ADC的功能

STM32F103C8T6的定时器资源:TIM1、TIM2、TIM3、TIM4(一个高级,三个通用)

基本定时器

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

名词:

UI:更新中断,更新中断之后通往NVIC,再配置好NVIC的定时器通道,定时器的更新中断就可以得到CPU的响应

U:更新事件,不会触发中断,但是可以触发内部其他电路的工作

计数时间到,就会触发更新中断和更新事件

主从触发DAC模式

可以让内部的硬件再不受程序控制下实现自动运行,可以减轻CPU的负担

用途:在使用DAC的时候,可能要用DAC输出一段波形,就需要每隔一段时间来触发一次ADC,让它输出下一个电压点,使用主模式,可以把定时器的更新事件映射到出发输出TRGO的位置,这样,定时器的更新就不需要用中断来触发DAC转换了

通用定时器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结:外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚
在这里插入图片描述

定时器中断基本图

在这里插入图片描述

定时中断和内外时钟源选择

涉及的结构:时基单元、RCC内部时钟、ETR外部时钟、内部时钟模式、外部时钟模式2

时序图
预分频器时序

在这里插入图片描述

在这里插入图片描述

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

计数器时序图

在这里插入图片描述

记到0036发生溢出,再来一个上升沿,计数器清零

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

用72MHz/ (PSC + 1) / (ARR + 1)就能得到溢出频率,如果想算溢出事件,只需要取倒数

1.1 定时器基本定时功能

实现功能:定时中断和内外时钟源选择

定一个时间,然后让定时器每隔这个时间产生一次中断,来实现每隔一定时间就实现特定程序的目的

步骤:

第一步:RCC开启时钟,打开时钟后,定时器的基准时钟和整个外设的工作时钟就会同时打开

第二步:选择时基单元的时钟源,对于定时中断,选择内部时钟源

第三步:配置时基单元,包括预分频器、自动重装器、计数模式等

第四步:配置输出中断控制,允许更新中断输出到NVIC

第五步:配置NVIC,再NVIC中打开定时器中断的通道,并分配一个优先级

第六步:运行控制,使能计数器,否则不会运行

当定时器使能后,计数器就会开始计数,当计数器更新时,触发中断,最后再写一个定时器的中断函数,这样中断函数每隔一段事件就会中断一次

相关定时器库函数

void TIM_DeInit(TIM_TypeDef* TIMx);

恢复缺省配置

时基单元选择

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

时基单元初始化,用来配置时基单元,第一个参数TIMx选择某个定时器,第二个结构体TIM_TimeBaseInitStruct,包含配置时基单元的一些参数

void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

可以把结构体变量赋一个默认值

运行控制

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

用来使能计数器(对应运行控制),两个参数,第一个TIMx选择寄存器,第二个参数选择状态,使能还是失能

中断输出控制

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

用来使能中断输出信号,对应中断输出控制(第四步),第一个参数TIMx选择定时器,第二个参数TIM_IT选择要配置哪个中断输出,第三个参数新的状态

时钟源选择:对应时基单元的时钟选择部分的函数,可以选择RCC内部时钟、ETR外部时钟,ITRx其他定时器、TIX捕获通道(第二步)

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

选择内部时钟

void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择ITRX其他定时器的时钟,第一个参数选择要配置的定时器,第二个参数选择要接入哪个其他定时器

void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);

选择TIx捕获通道的时钟,第一个参数TIMx选择定时器,第二个参数TIM_TIxExternalCLKSource选择TIx具体的某个引脚,剩下两个参数分别是输入的极性和滤波器

void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);

选择ETR通过外部时钟模式1输入的时钟,第二个参数TIM_ExtTRGPrescaler为外部触发预分频器,可以对ETR的外部时钟再提前做一个分频

void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

选择ETR通过外部时钟模式2输入的时钟,第二个参数是外部触发预分频器,第三个参数是外部触发的极性,第四个参数是外部触发滤波器

void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);

单独用来配置ETR引脚的预分频器、极性和滤波器等参数

其他函数

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

用来单独写预分频值的, Prescaler参数就是要写入的预分频值,TIM_PSCReloadMode参数就是要写入的模式,可以选择是听从安排,在更新事件后生效,或者是在写入后,手动产生一个更新事件,让这个值立刻生效

void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);

用来改变计数器的计数模式,参数TIM_CounterMode用来选择新的计数器模式

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);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

用来获取标志位和清除标志位

注:

如果想跨文件使用变量:

(1)可以在使用变量的那个文件上面,用extern声明一下要用的变量,比如在Timer.c文件上面写上extern uint16_t Num;这样就可以在Timer.c文件中使用主函数的Num变量

(2)直接把中断函数复制到主函数后面,就不需要extern函数

配置代码示例

以下文件均为Timer.c文件

内部时钟配置

#include "stm32f10x.h"                  // Device header
//初始化TIM2,通用定时器,定时中断的初始化函数
void Timer_Init(void)
{
	//第一步:开启时钟
	//RCC_APB1Periph_TIM2表示RCC_APB1外设TIM2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//第二步:选择时基单元的时钟
	TIM_InternalClockConfig(TIM2);
	//第三步:配置时基单元
	//结构体类型名 结构体变量名
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//Specifies the clock division.指定时钟分频,随便配一个就可以
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	//ARR自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Period=10000-1;
	//PSC预分频器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;
	//重复计数器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	//1s时间:定时1s也就是定时频率为1Hz,预分频是对72M进行7200分频,得到10K的计数频率
	//在10K的频率下,记10000个数字就是1s的时间
	
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	//第四步:使能更新中断
	//手动清除更新中断标志位,避免按下复位键刚初始化完就立刻进中断
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	//第五步:配置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)
//	{
//		
//		//清除标志位
//		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
//	}
//}

外部时钟配置

#include "stm32f10x.h"                  // Device header
//初始化TIM2,通用定时器,定时中断的初始化函数
void Timer_Init(void)
{
	//第一步:开启时钟
	//RCC_APB1Periph_TIM2表示RCC_APB1外设TIM2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	//第二步:选择时基单元的时钟
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
	//第三步:配置时基单元
	//结构体类型名 结构体变量名
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//Specifies the clock division.指定时钟分频,随便配一个就可以
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	//ARR自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Period=10-1;
	//PSC预分频器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;
	//重复计数器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	//1s时间:定时1s也就是定时频率为1Hz,预分频是对72M进行7200分频,得到10K的计数频率
	//在10K的频率下,记10000个数字就是1s的时间
	
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	//第四步:使能更新中断
	//手动清除更新中断标志位,避免按下复位键刚初始化完就立刻进中断
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	//第五步:配置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);
}

uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);
}

//当定时器产生更新中断时,下面的函数自动被执行
//void TIM2_IRQHandler(void)
//{
//	//检查中断标志位
//	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
//	{
//		
//		//清除标志位
//		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
//	}
//}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值