03:TIM定时器

目录

一:TIM

1:介绍

2:定时器的分类

3:基本定时器

4:通用定时器

5:高级定时器

6:定时器的基本结构

二:定时中断功能

A:定时器定时器中断

1:连接图

​编辑

2:步骤

3:函数介绍

4:代码

三:外部时钟功能

A:定时器外部时钟

1:连接图

2:函数介绍

3:外部时钟代码


一:TIM

1:介绍

TIM(Timer)定时器

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

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

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

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

2:定时器的分类

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

3:基本定时器

1 : 预分频器+CNT计数器+自动重装载寄存器=时基单元

2 : 基本定时器只能选择内部时钟;他们都为16位的

预分频器: 可以对72MHZ的计数时钟进行预分频处理; 对输入的基准频率提前进行一个分频的操作

eg : 预分频器写0,那就是不分频,或者说是1分频 ; 输出频率=输入频率=72MHz

预分频器写1,那就是2分频,输出频率(实际分频系数)=输入频率/2=36MHz

预分频器写2,那就是3分频,输出频率(实际分频系数)=输入频率/3= 24MHz

所以预分频器的值和实际的分频系数相差了1; 实际分频系数=预分频器的值+1

计数器: 计数器可以对预分频后的计数时钟进行计数,预分频器每来一个上升沿计数器就+1

所以计数器的值在计时过程中会不断地自增运行,直到达到目标值(自动重装载寄存器)然后产生中断,然后在重新开始计数

自动重装载寄存器(固定值) : 储存的是我们的计数目标**,**产生中断的目标值,(当计数器达到目标值就产生中断)

流程: 基准时钟------->预分频器------>计数器<--------->自动重装载计数器

计数器不断自增,会和自动重装载寄存器比较,当两个的值相同时,产生更新中断和更新事件;

cpu会响应更新中断

4:通用定时器

5:高级定时器

6:定时器的基本结构

二:定时中断功能

A:定时器定时器中断

我们使用的是通用定时器TIM2在案列中(内部时钟)

1:连接图

2:步骤

1: 开启时钟 (RCC)

2: 选择时基单元的时钟 (TIM_InternalClockConfig--选择内部时钟 )

3: 配置时基单元 (TIM_TimeBaseInit)

4 : 使能更新中断( TIM_ITConfig中断时钟控制 )

5: NICV的配置 (见 02: STM32)

6: 启动定时器 (TIM_Cmd)

3:函数介绍

在stm32f10x tim.h文件中的函数-----时钟源选择函数 (选择时基单元的时钟)

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_TIM_TIxExternalCLKSource,

uint16_t TIM_ICPolarity, uint16_t ICFilter);

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

void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,

uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

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

TIM_InternalClockConfig: 选择内部时钟

TIM_ITRxExternalClockConfig : 选择TIR其他定时器的时钟

TIM_TIxExternalClockConfig : 选择TIx捕获通道的时钟

TIM_ETRClockMode1Config : 选择ETR通过外部时钟模式1输入的时钟

TIM_ETRClockMode2Config : 选择ETR通过外部时钟模式2输入的时钟

**TIM_ETRConfig :**单独用来配置ETR引脚的预分频器、极性、滤波器这些参数的

在stm32f10x tim.h文件中的函数-------时基单元函数

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

**TIM_TimeBaseInit :**时基单元初始化,;TIMX选择某个定时器; TIM_TimeBaseInitStruct:结构体包含了TIM配置的一些参数;

在stm32f10x tim.h文件中的函数-------中断输出控制函数

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

TIM_ITConfig : 使能中断输出信号

在stm32f10x tim.h文件中的函数-------运行控制函数函数

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

TIM_Cmd : 选择启动那个定时器, 选择使能还有失能

在stm32f10x tim.h文件中的函数-------单独修改初始化函数中的重要参数

不能为了某一个参数.直接重新初始化,太关于麻烦,直接更改某一个参数即可

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);

TIM_PrescalerConfig : 单独写预分频值

TIM_CounterModeConfig: 改变计数器的计数模式

TIM_ARRPreloadConfig : 自动重装器预装功能配置

TIM_SetCounter : 给计数器写入一个值

TIM_SetAutoreload : 给自动重装器写入一个值

TIM_GetCounter : 获取当前计数器的值

TIM_GetPrescaler :获取当前的预分频器的值

在stm32f10x tim.h文件中的函数--其他函数

void TIM_DeInit(TIM_TypeDef* TIMx);

void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

TIM_DeInit : 恢复缺省配置

TIM_TimeBaseStructInit : 结构体变量赋一个默认值

4:代码

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Timer.h"
int16_t Num;

extern int16_t Num;
void Timer_init(void){
//第一步是开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//第二步,选择时基单元的时钟 (stm23上电默认使用的是内部时钟,这一行代码可以省略)
TIM_InternalClockConfig(TIM2);
//第三步,配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
/*计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
					       = CK_PSC / (PSC + 1) / (ARR + 1
	定时频率=72M/(PSC+1)/(ARR+1)
	72MHZ=72000KHZ
	72000KHZ/7200=10KHZ=10000HZ
	T=1/F   T=1/10000hz=0.0001s=0.1ms
	然后以0.1ms的周期计10 000个数,所以就是1s
	
	*/
TIM_TimeBaseInitStructure.TIM_Period=10000-1;			//自动重装载寄存器ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;  //预分频器PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//高级定时器特有的(重复寄存器)
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
//第四使能更新中断

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	
TIM_ClearFlag(TIM2, TIM_FLAG_Update);  //手动清除更新中断标志位
	
//第五步NICV的配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
//第六步启动定时器
TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler(){
	//检查中断标志位
	if (	TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		//清除标志位
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}


int main(void)
{
	OLED_Init();
	Timer_init();
	OLED_ShowString(1, 1, "Num:");
	
	while (1)
	{
		OLED_ShowNum(1,5,Num,5);
	}
}

为什么要清除中断标志位

响应中断条件是:中断使能中断标志同时成立

单片机要靠查询中断标志来判断是否要进入中断,如果你不清除中断标志,本次中断退出,单片机又会检测到中断标志,因此重复进入中断

在STM32微控制器中,中断是一种重要的机制,用于响应外部事件或内部条件的变化。当一个中断事件发生时,相应的中断标志位会被置位(1),以表示中断事件已经发生。但是,在处理完中断之后,必须清除中断标志位(0),以确保下一次中断事件的正确触发。

清除中断标志位的主要目的有以下几个方面:

  1. 防止重复触发:如果不清除中断标志位,当中断处理程序退出后,如果中断标志位仍然保持置位状态,可能会导致重复触发中断。这样会导致中断处理程序不停地执行,影响系统正常运行。

  2. 确保正确的中断优先级:在STM32微控制器中,不同的外设和中断源具有不同的优先级。当多个中断源同时触发时,只有优先级最高的中断源会被处理。如果不清除中断标志位,可能会导致错误的中断源被处理,影响系统的功能和性能。

  3. 确保正确的中断嵌套:STM32微控制器支持中断的嵌套执行。当一个高优先级的中断正在执行时,如果有一个更高优先级的中断进来,系统会自动挂起当前中断,转而执行更高优先级的中断。在挂起期间,中断标志位可能会保持置位状态。当更高优先级的中断执行完毕后,必须清除该中断的标志位,以便继续执行之前挂起的中断。

因此,为了确保中断系统的正确运行,必须在中断处理程序中清除相应的中断标志位。这可以通过写入相应的寄存器或调用相应的函数来实现。

时间的计算

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

首先72M进行7200分频,得到是10K的计数频率

在10K频率下,记10000个数,就是1s的时间

Arr是自动重装 psc预分频

方表明最大值是0-65535。-1表示有偏差

频率的单位是Hz; 周期的单位是s;

Hz<KHz<MHz; 都是千进的

s秒,ms毫秒,us微秒,ns纳秒; 都是千进的

72M进行7200分频,得到10KHz的计数频率。

T=1/F 所以T=1/10 000hz = 0.0001s = 0.1ms

然后以0.1ms的周期计10 000个数,所以就是1s

72MHZ=72000KHZ

72000KHZ/7200=10KHZ=10000HZ

T=1/F T=1/10000hz=0.0001s=0.1ms

然后以0.1ms的周期计10 000个数,所以就是1s

三:外部时钟功能

A:定时器外部时钟

1:连接图

2:函数介绍

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);

void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,

uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

TIM_GetCounter : 它用于获取定时器计数器的当前值

TIM_ETRClockMode2Config : 选择ETR通过外部时钟模式2输入的时钟

3:外部时钟代码

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
extern int16_t Num;
void Timer_init(void){
//第一步是开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
GPIO_InitTypeDef A;
A.GPIO_Mode=GPIO_Mode_IPU;  //上拉
A.GPIO_Pin=GPIO_Pin_0;
A.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&A);
//第二步,选择时基单元的时钟 (stm23上电默认使用的是内部时钟,这一行代码可以省略)
	//TIM_ExtTRGPolarity_NonInverted 高电平或者上升沿有效
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0f);//选择ETR通过外部时钟模式2输入的时钟

	
//第三步,配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
/*计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
					       = CK_PSC / (PSC + 1) / (ARR + 1
	定时频率=72M/(PSC+1)/(ARR+1)
	72MHZ=72000KHZ
	72000KHZ/7200=10KHZ=10000HZ
	T=1/F   T=1/10000hz=0.0001s=0.1ms
	然后以0.1ms的周期计10 000个数,所以就是1s
	
	*/
TIM_TimeBaseInitStructure.TIM_Period=10-1;			//自动重装载寄存器ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;  //预分频器PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//高级定时器特有的(重复寄存器)
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
//第四使能更新中断

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//手动清除更新中断标志位
	
//第五步NICV的配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
//第六步启动定时器
TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler(){
	//检查中断标志位
	if (	TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		//清除标志位
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}

}



uint16_t  Timer_GetCounter(void){
	//TIM_GetCounter。它用于获取定时器计数器的当前值。
	return TIM_GetCounter(TIM2);  
}



uint16_t Num;

int main(void)
{
	OLED_Init();
	Timer_init();
	
	OLED_ShowString(1, 1, "Num:");
	OLED_ShowString(2, 1, "CNT:");
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);
		OLED_ShowNum(2, 5, Timer_GetCounter(), 5);
	}
}

本实验实验了外部时钟(对外式红外传感计数器),当对外式红外传感计数器达到了某个数值是触发定时器

相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen4 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森6 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白6 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D6 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术9 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt10 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘10 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang10 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n12 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件