STM32-TIM定时器中断

2023-12-13 12:35:48

目录

一、TIM(Timer)定时器简介

二、定时器类型

2.1基本定时器结构

2.2通用定时器结构

2.3高级定时器结构

三、定时中断基本结构

四、时序图分析

4.1 预分频器时序

4.2 计数器时序

4.3 计数器无预装时序(无影子寄存器)

4.4?计数器有预装时序(有影子寄存器)

五、RCC时钟树?

六、开发步骤

七、定时器函数

八、实验

8.1定时器定时中断

8.2定时器外部时钟


一、TIM(Timer)定时器简介

①定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
②16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

计数器:执行计数定时的一个寄存器,每来一个时钟,计数器加1

预分频器:对计数器的时钟进行分频,让这个计数更加灵活

自动重装寄存器:计数的目标值,计多少个时钟来申请一次中断
③不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
④根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

二、定时器类型

2.1基本定时器结构

主模式触发DAC功能:把定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,TRGO直接接到DAC的触发转换引脚上,这样就不需要通过中断来触发DAC转换了。实现了硬件的自动化。??

?通用定时器和高级定时器除了向上计数模式,还有向下计数模式和中央对齐模式

2.2通用定时器结构

2.3高级定时器结构

三、定时中断基本结构

四、时序图分析

4.1 预分频器时序

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

4.2 计数器时序

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

计数器时间:(PSC + 1)(ARR + 1)/CK_PSC

4.3 计数器无预装时序(无影子寄存器)

4.4?计数器有预装时序(有影子寄存器)

五、RCC时钟树?

作用:产生和配置时钟,将配置好的时钟发送到各个外设系统

开发技巧:

在SystemInit函数中,首先启动内部8MHz时钟为系统时钟运行,然后再启动外部时钟,进入PLL锁相环进行倍频,8MHz倍频9倍,得到72MHz,锁相环输出稳定之后,选择锁相环输出为系统时钟,这样就把系统时钟由8MHz变成了72MHz。

实际问题:

如果外部晶振出现问题,程序时钟慢了大概10倍,定时器定时1s,结果过了大概10s才进中断。是因为现在是以内部时钟8MHz运行的

六、开发步骤

①RCC打开时钟

②选择时基单元的时钟源

③结构体配置时基单元(预分频器,自动重装器,计数模式)

④配置输出中断控制,允许更新中断输出到NVIC

⑤配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级

⑥运行控制,使能计数器

⑦写定时器中断函数

七、定时器函数

=====================================================================

=================================基本函数=============================

void TIM_DeInit(TIM_TypeDef* TIMx);

//定时器恢复缺省配置


void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* 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选择定时器,第二个参数:哪个中断输出,第三个参数:使能或失能

=====================================================================

=========================配置时钟输入的函数=============================

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,第二个参数:TIMx具体哪个引脚,第三、四个参数:输入极性和滤波器


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

//ETR通过外部时钟模式1输入时钟

//第一个参数:TIMx,第二个参数:外部触发预分频器,第三、四个参数:输入极性和滤波器


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

//ETR通过外部时钟模式2输入时钟

//第一个参数:TIMx,第二个参数:外部触发预分频器,第三、四个参数:输入极性和滤波器


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

//单独配置ETR引脚的预分频器、极性、滤波器

//第一个参数:TIMx,第二个参数:外部触发预分频器,第三、四个参数:输入极性和滤波器

=====================================================================

===================更改关键参数函数(预分频值,自动重装载值)==============

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

//修改预分频值

//第一个参数:TIMx,第二个参数:预分频值,第三个参数:写入模式


void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);

//修改计数器的计数模式

//第一个参数:TIMx,第二个参数:新的计数器模式


void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

//自动重装器预装功能配置

//第一个参数:TIMx,第二个参数:预装功能使能或失能

void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);

//给计数器值

//第一个参数:TIMx,第二个参数:计数器值


void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);

//给自动重装器写入值

//第一个参数:TIMx,第二个参数:自动重装值

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

八、实验

8.1定时器定时中断

实验现象:1s计数加一

代码实现

Timer.c

#include "stm32f10x.h"                  // Device header

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;//ARR自动重装器值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//PSC预分频器值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器值(高级定时器才有)
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	/*避免刚初始化就进入中断(复位就是1,而不是0)*/
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	
	/*四、配置输出中断控制,允许更新中断输出到NVIC*/
	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)
	{
		
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
*/

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

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:");
	OLED_ShowString(2,1,"CNT:");

	while (1)
	{
		OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//观察CNT计数器值的变化情况
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

8.2定时器外部时钟

方法:定时器指定的外部引脚输入一个方波信号,来提供定时器计数的时钟?

实验现象:利用对射式红外传感器来手动模拟一个外部时钟,用挡光片,依次遮挡、移开来模拟一个方波,定时器计数值(CNT)逐次加一,当CNT到9后,产生一次中断,Num加一,CNT清零重新计数

代码实现

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	/*一、RCC开启时钟 + GPIOA的初始化*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(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);
	//通过ETR的外部时钟模式2,不分频,不反向(高电平/上升沿有效),外部触发滤波器
	
	/*三、配置时基单元(预分频器,自动重装器,计数模式)*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//ARR自动重装器值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;//PSC预分频器值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器值
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	/*避免刚初始化就进入中断(复位就是1,而不是0)*/
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	
	/*四、配置输出中断控制,允许更新中断输出到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);
}

/*查看CNT的值*/
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);
	}
}
*/

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
uint16_t Timer_GetCounter(void);

#endif

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:");
	OLED_ShowString(2,1,"CNT:");

	while (1)
	{
		OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,Timer_GetCounter(),5);//观察CNT计数器值的变化情况
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

文章来源:https://blog.csdn.net/qq_52902991/article/details/134790153
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。