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

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能;
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。


 

 基本定时器:有定时中断和主从触发DAC功能

RCC时钟树如图:

时基单元: 自动重装载寄存器,预分频器和CNT计数器组成时基单元,完成计数计时的功能。

流程:

预分频器之前,连接的是基准计数时钟的输入,由于基本定时器只能选择内部时钟,所以可以看做内部时钟(CK INT)直接连接的是预分频器,来自RCC的TINxCLK一般都是72MHZ;实际分频系数=预分频器值+1,预分频器是16位的,可以写65535,因此最大分频系数为65536。计数器是对分频后的时钟进行计数,每来一个上升沿,计数器值+1,计数器是16位的,因此最大计数值的范围是0-65535;当计数器的值等于自动重装载寄存器的值时,产生一次中断信号,并且计数器的值清零,图中向上的箭头表示更新中断,向下的箭头表示更新事件,CPU响应更新中断,定时器中断的任务就完成了。

更新事件:外设上升沿或者下降沿直接驱动的中断,与EXTI外部中断相似。

一次定时器中断溢出的时间可以由以下的公式计算得来:

Tout= ((arr+1)*(psc+1))/Tclk

arr:自动重装值

psc:预分频系数

Tclk:输入到时基单元的时钟频率


定时中断基本结构如下:

 使用基本定时器定时中断的步骤:
1.开启GPIO时钟和GPIO外设。
2.选择时基单元的时钟源。定时器定时中断选择内部时钟源。
3.配置时基单元。配置预分频器,自动重装器和计数器这三个寄存器。
4.配置输出中断控制,允许更新中断输出到NVIC.
5.配置NVIC,在NVIC 中打开定时器中断的通道,并分配一个优先级。
6.运行控制。
7.使能定时器。开中断。
8.写定时器中断服务程序。
 

基本定时器中断代码如下:

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//TIM2挂在APB2上
	
	TIM_InternalClockConfig(TIM2);//使用RCC内部时钟72MHZ
	
	//定时器初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //  是否划分72MHZ时钟频率
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计次
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;  //  自动重装器的值  最大为65535
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;  //预分频系数  最大为65535
	//  1/(72000000/7200-1+1)*10000=1s  也就是说每一秒进一次中断
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //是否重复计数
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);  //清除标志位
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  //启用或者禁用定时器中断,中断输出控制模块:开启更新中断到NVIC的通路
	//通过调用TIM_ITConfig()函数配置定时器中断类型后,只是使能了定时器内部的中断触发,但是并没有使能外部中断,即没有将中断请求发送到NVIC。
	//要使能外部中断,需要在NVIC中使能对应的中断线,这样当定时器中断触发时,中断请求就会被发送到NVIC,
	//NVIC再根据中断优先级和抢占规则来决定是否将中断请求发送到CPU,进而执行中断处理函数。
	
	//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)  //TM2的中断函数
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

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:");
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);
	}
}

//对应中断函数  实现了1s记一次数,也就是时钟功能
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

外部时钟定时器中断代码:

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	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, 0x0F);
	//第二个参数是外部触发器预分级器。  第三个参数:工作在上升沿或者下降沿  第四个参数:过滤器
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);  //清除挂起标志  TIM_FLAG_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);  //获取TIMx计数器值
}

/*
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

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

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

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐