当前位置:   article > 正文

stm32——定时器_stm32定时器

stm32定时器

一、定时器介绍

1.简介

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

2.定时器类型

在这里插入图片描述

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

3.基本定时器

在这里插入图片描述
基本定时器可分为3部分

a.时钟源

时钟源来自RCC的TIMXCLK,就是内部时钟(CK_INT)直接经过控制器传给时基单元充当CK_PSC

b.控制器

控制定时器的复位、使能、计数、DAC触发

c.基本时基单元

1>预分频器(PSC)perscaler
分频、得到计时器的时钟,即CNT计数1次所需要的时间,预分频器时16位的寄存器、所以可分频为1-65536。
基本定时器只能选择内部时钟,故预分频器直接输入端,即内部时钟CK_INT(一般为72MHz)
预分频器(PSC)写0(1分频或者不分频):则SK_CNT输出72MHz,
写1(2分频):输出36MHz,写2(3分频):输出24MHz。
故实际分频系数=预分配系数+1(分频器是16位,故最大位65525)。

2>计数器(CNT)counter
用来计数,到达设定值后,会产生中断。

3>自动重装寄存器(ARR)Auto Reload Register
CNT加到ARR的值之后,会产生一个事件或中断或DMA请求,中断用得比较多

UI(向上的箭头)中断响应,更新中断后会通向NVIC,再通向CPU
U(向下的箭头)事件响应:触发其他外设工作

d.主模式触发DAC功能

1)能让内部的硬件再不受程序的控制下实现自动运行,若利用好,可以极大地减轻CPU的负担。
2)用途:使我们使用DAC的时候,用DAC输出一段波形,则需要每隔一段事件触发一次DAC,让它输出下一个电压点。
用正常思维实现:先设置一个定时器产生中断,每隔一段时间再中断程序中调用代码手动触发一次DAC转换,然后DAC输出。这样会使主程序处于频繁被中断的状态,会影响主程序的运行和其他中断的响应
故定时器设置了一个主模式,使用主模式可以把定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不需要用中断来触发DAC(数模转换)转换了
仅仅需要将更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用

在这里插入图片描述

时序

在这里插入图片描述
有框框的有影子寄存器,即预分频缓冲器,可以进行缓存,更改的值下个周期才生效(比如你改手机套餐,这个月内你还是原套餐,你改的套餐下个月才生效)
在这里插入图片描述

4.通用定时器

在这里插入图片描述

区别:
1.计数器支持向上、向下、中央对齐计数模式
2.可用外部时钟作为主频率
3.时钟来自其他定时器

未完待续…

二、实例

1. 基本思路

在这里插入图片描述第一步:RCC开启时钟
第二步:选择时基单元的时钟源,对于定时中断,选择内部时钟源
第三步:配置时基单元,包括预分频器、自动重装器、计数模式(用结构体配置)
第四步:配置输出中断控制,允许更新中断输出到NVIC
第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
第六步:运行控制,整个模块配置完成后,使能计数器,否则计数器不运行, 计数器使能后,计数器就会开始计数,当计数器更新时,触发 中断,最后写个定时器中断函数,这样中断中断函数就能每隔一段时间自动执行一次

2.定时器中断

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //RCC开启时钟
	
	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;  //周期,即重装值(减一原因见时序表)
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;  //预分频值(对72Mhz进行7200分频率,即10K计数频率,再计数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
	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);
	}
}
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

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

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3.定时器外部时钟

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //配置GPIO口
	
	GPIO_InitTypeDef GPIO_InitSturcture;
	GPIO_InitSturcture.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitSturcture.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitSturcture.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitSturcture);
	
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);  //更改为外部中断2模式(相较于1模式(4条通道),该模式只有1条通道,且可分频,比较简单,推荐使用)
	
	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_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	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)  //看CNT的值
{

	return TIM_GetCounter(TIM2);
}

/*
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

Timer.h

#ifndef  __TIMER_H__
#define  __TIMER_H__

void Timer_Init(void);
uint16_t Timer_getcounter(void);

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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(1,5,Timer_getcounter(), 5);
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

4.其他知识

extern:告诉编译器,已经在别的文件中定义了该变量,(可以实现在文件之间传递值)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/942231
推荐阅读
相关标签
  

闽ICP备14008679号