5、STM32之定时器(TMI)

本文详细介绍了STM32定时器的工作原理,包括基本定时器的主从触发、预分频系数调整,通用定时器的多样功能如时钟选择、输入捕获和输出比较,以及高级定时器的特性。通过代码示例演示了如何配置TIM3,配合LED显示计数,展示了定时器在实际项目中的应用。
摘要由CSDN通过智能技术生成

文章参考有
https://www.bilibili.com/video/BV1th411z7sn?p=14&share_source=copy_web

一、简介

TIM (Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
在这里插入图片描述

1、基本定时器

在这里插入图片描述
在这里插入图片描述
主从触发模式:让内部的硬件在不受程序的控制下实现自动运行,使用得好可减轻CPU负担。
这个紫色部分就是主从触发模式的一个实例。
当我们使用DAC的时候,可能需要用DAC输出一段波形,就需要每隔一段时间就来触发一下来输出下一个电压点。也是会牵涉到中断。
使用主模式可以把这个定时器的更新事件,映射到这个触发输出TRGO,然后TRGO直接接到DAC的引脚上。(跟着动,不用再额外去申请中断)
在这里插入图片描述
预分频器、CNT计数器、自动重装载寄存器构成了最基本的计数电路,也叫做微时基单元。
预分频器、CNT计数器、自动重装载寄存器都是16位。
预分频器对收到的时钟(CK_PSC)进行分频:如果是写1,表示是2分频,输出=输入/2。预分频器和实际的分频系数差1。

(计数器计数频率)CK_CNT = CK_PSC(预分频器的值) / PSC(分频赋的值)+1

计数器可对预分频后的计数时钟进行计数。计数时钟每来一个上升沿,计数器就加1。达到自动重装器的值后产生中断信号,并且清零计数器,开启下一次计数。
当每一次计数的事件都一样,计数跟计时就没啥区别。

计数器溢出频率 f=计数器计数频率 / ARR(重装载寄存器的值)+1

定时的时间 T = 1 / f

在这里插入图片描述
在这里图上画的一个向上的折线箭头。就代表这里会产生中断信号
这种计数值等于自动重装值产生的中断,一般把他叫做更新中断。这个更新中断之后就会通到NVIC。我们再配置好NMC的定时器通道。那定时器的更新中断就能够得到CPU的响应。

向下的箭头代表事件,这里对应的叫更新事件。更新事件不会触发中断。但可以触发肉部其他电路的工作。

关于预分频系数从1变到2的计数器时序图

在这里插入图片描述
首先预分频系统的频率一直没变,是系统时钟。在CNT_EN置于0时(没开),定时器CK_CNT不启动。CNT_EN=1时CK_CNT启动。然后在开启后的第一个上升沿开始计数。
当预分频系数由1变为2时,首先写入预分频控制寄存器。在计数器完成这个周期(到达FC后),触发更新事件,预分频器缓冲器置为1.预分频计数器变为010101这么延续,在为0时CK_CNT上升沿触发一次。

我的理解是,预分频控制寄存器是虚假的皇帝,预分频控制缓存器才是摄政王,更新事件是玉玺。只要玉玺盖了剩下的才开始动。
当然这么也有可能存在问题,比如你在刚开始计数就更改时序,结果还要等到他把这一堆跑完才可以更改。所以在stm32库函数里这个摄政王是可以设置开不开的。
在这里插入图片描述
在这里插入图片描述

2、通用计时器

拥有定时中断、主模式触发DAC的功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能

在这里插入图片描述
在这里插入图片描述
中间的部分就是基本的定时中断。
上部分是有时钟源选择,编码接口,主从模式。
下部分是输入捕获和输出比较。

当计数值计到自动重装值时。计数值清零同时产生更新中断和更新事件
对于通用定时器而言。这个计数器的计数模式就不止向上计数这一种(也就是计数器从开始,向上自增,计到重装值,清零同时申请中断。)
通用定时器和高级定时器还支持向下计数模式和中央对齐模式。
向下计数模武就是从重装值况始。向下自减,减到0,回到重装值同时申请中断,这样循环。
中央对齐模式:先上升到重装值,申请中断,再向下自减,减到0,再申请中断。继续下一轮循环。
不过一般都是向上计数用得多。

时钟选择

在这里插入图片描述

在这里插入图片描述
内部时钟源
在这里插入图片描述
外部时钟源模式1
当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。
在这里插入图片描述
外部时钟源模式2
选定此模式的方法为:令TIMx_SMCR寄存器中的ECE=1
计数器能够在外部触发ETR的每一个上升沿或下降沿计数

输入捕获和输出比较

https://blog.csdn.net/wei348144881/article/details/109091539

3、高级定时器

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

二、代码

在这里插入图片描述

#include "Timer.h"
#include "stm32f10x.h"  

void TIM3_Init()
{
	TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	//1、时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2、初始化定时器参数
	TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   //时钟划分
	TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;   //计数器模式向上计数
	TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1;
	TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
	TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
	
	//3、允许TMI3更新中断
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	
	//4、配置NVIC
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
	//5、使能TIM3
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
	
}
 void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
		//此处写入中断后要干的活的代码
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		
		}
}

1、关于时钟总线的外设情况

TIM3挂载在APB1上
在这里插入图片描述

2、初始化定时器参数

在这里插入图片描述

溢出时间 = (自动重载值period + 1)*(分频系数prescaler + 1)/ 输入时钟频率

一般情况下这个输入时钟频率是内部时钟的情况下就是72MHz

输出频率(最后使用的频率)f = 输入时钟频率 / (分频系数prescaler + 1)

f的意思是,1s计X个数 。计1个数的时间是 :1 / f

所以溢出的总时间是 = 计一个数的时间(1/f) * 个数(重装载值)

1s = 1000ms = 1000000us
M = 10的六次方

	TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure; //这个定义必须放在函数开头,这里为了方便理解放在了一起
//2、初始化定时器参数
	TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   //时钟划分
	TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;   //计数器模式向上计数
	TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1; //自动重载
	TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
	TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);

记1个数的时间=7200 / 72000000 = 0.0001s = 0.1ms
总时间 = 0.1ms * 10000 = 1s

注意这两个数都是16位,最大值是65535

3、关于中断

配置NVIC时选择TIM3中断
在这里插入图片描述
TIM3中断函数
在这里插入图片描述

4、一些别的函数

恢复缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);
时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
使能计数器
TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
使能输出中断信号
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
选择内部时钟,默认情况下就是选择内部时钟,所以有时候可以省略
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

选择ITRx其他定时器时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择TIx捕获通道的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
单独更改时基函数中的配置
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t 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);

三、LED显示计数

led屏幕的使用函数是用的正点原子的函数

#include "stm32f10x.h"
#include "KEY.h"
#include "LED.h"
#include "delay.h"
#include "EXTI.h"
#include "lcd.h"
#include "Timer.h"


uint8_t num;
 int main(void)
 {	
	
	
	delay_init();	    	 //延时函数初始化	  
	LCD_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	TIM3_Init();
  while(1)
	{
		LCD_ShowxNum(30,40,num,7,24,0);	
	
	}
 }

 void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
			num++;
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		
		}
}

#include "Timer.h"
#include "stm32f10x.h"  

void TIM3_Init()
{
	TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	//1、时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2、初始化定时器参数
	TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   //时钟划分
	TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;   //计数器模式向上计数
	TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1; //自动重载
	TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
	TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
	
	//3、允许TMI3更新中断
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	
	//4、配置NVIC
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
	//5、使能TIM3
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
	
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值