抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

定时器TIMx介绍

定时器是一个设备,要区别于时钟的概念,时钟是用来配置产生一定频率的电平信号,用于驱动像定时器、ADC等外设工作。TIMx中的 x 即具体的哪一个定时器

分类

  • 基本定时器(TIM6、TIM7)
  • 通用定时器(TIM2—TIM5)
  • 高级控制定时器(TIM1、TIM8)

内部构成模块

自动重装载寄存器:TIMx_ARR

预分频器:TIMx_PSC

image-20210929000716395

这是TIM6、TIM7的主要内部构成(TIM2~5多了一个捕获/比较寄存器TIMx_CCR)

核心部分由自动重装载寄存器TIMx_ARR和CNT计数器构成

来自RCC的时钟信号TIMxCLK(通常是72MHZ)经过预分频器分频后,计数器开始计数(TIM6、TIM7只能从0开始向上计数),直到计数值达到TIMx_ARR中存放的值后,重新回到0,继续重复这一过程。

由此得出3个配置步骤:

  1. 选择时钟源 TIMxCLK 频率
  2. 预分频系数(注意要-1,比如要选择分频系数为72,那么放入的值应该是71)
  3. 确定需要延时多长时间,进而确定自动重装载的值 TIMx_ARR(注意也要-1)

延时时间 = (TIMx_ARR+1) * (TIMx_PSC+1) / TIMxCLK

计数模式

  • 向上计数(基本定时器TIM6/TIM7只有这种模式)
  • 向下计数
  • 双向计数

三种定时器

大家都具有的功能或特点

  • 内部预分频器位数:16
  • 内部计数器位数:16
  • 可以更新中断和DMA

基本定时器

基本定时器包括TIM6、TIM7,只具备基本的定时功能,即累计时钟脉冲数超过装载值后,产生溢出;如果使能了中断或者DMA事件,则使能中断或者DMA操作;

另外,基本定时器可以作为通用定时器的时基,可以为数模转换器DAC提供时钟(事实上,TIM6、TIM7被直接连接到DAC并通过触发直接驱动DAC);

其它特性:

  • 时钟源:APB1输出

  • 计数模式:向上计数

通用定时器

通用定时器包括TIM2~TIM5共四个

特性:

  • 相比基本定时器添加了 捕获/比较寄存器:TIMx_CCRx (注意每个通用定时器都有4个捕获比较寄存器,因为有4个输出口OCx)
  • 时钟源:APB1(通常提供72MHz)、ITRx、TIx(外部时钟模式1)、ETR(外部时钟模式2);[初学只需要掌握第一种即可]
  • 计数模式:向上、向下、双向均可

PWM输出模式

PWM即脉冲宽度调制(废话就不多说,不懂的可以去百度等查找相关介绍),通用定时器因为拥有捕获/比较寄存器,所以可以配置PWM输出模式

通用定时器如何配置PWM输出模式?

  1. 配置计数模式(此处设置为向上计数)
  2. 配置TIMx_ARR预设值为N(记得要-1),配置捕获/比较寄存器预设值A
  3. 配置输出方式:假设配置为当计数值X<A时, 输出高电平;当X>=A时,输出低电平。这样输出波形占空比为A/(N+1)
PWM输出模式
  1. 模式1 TIM_OCMode_PWM1:
    • 向上计数时,当计数值<CCR存放的值时,输出有效电平,否则为无效电平(之后可以设置低电平有效还是高电平有效)
    • 向下计数时,与向上计数一样
  2. 模式2 TIM_OCMode_PWM2:
    • 向上计数时,当计数值<CCR存放的值时,输出无效电平,否则为有效电平(之后可以设置低电平有效还是高电平有效)
    • 向下计数时,与向上计数一样

这是配置PWM输出的主要步骤,如图:

image-20211003215959242

配置PWM输出详细步骤:

例如配置TIM3的CH2通道为PWM输出,输出重映射到PB5端口

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
void TIM3_CH2_PWM_Init(u16 per,u16 psc){
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//改变指定管脚的映射,这里使用部分重映射,即通过PB5输出

TIM_TimeBaseInitStructure.TIM_Period= per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

//从这里开始是输出PWM配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式为PWM1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //有效电平设置为低电平
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC2Init(TIM3,&TIM_OCInitStructure); //输出比较通道2初始化


TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIMx在 CCR2 上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器

TIM_Cmd(TIM3,ENABLE); //使能定时器

}

PWM输入模式

PWM输入模式用于测量外部输入信号的脉宽,流程是:

  1. 从外部输入一个(方波)信号TI,当从输入端第一次捕获到TI的上升沿时,计数器TIMx_CNT复位为0,然后开始不断向上计数(TIMx_ARR足够大);
  2. 当捕获到下降沿时,TIMx_CRR2寄存器记录下当前计数器的计数值;(得出:高电平期间计数次数=CRR2+1,加1是因为计数是从0开始的)
  3. 当再次捕获到上升沿时,TIMx_CRR1寄存器记录当前计数器的计数值。(得出:一个周期内计数器计数次数=CRR1+1)
  4. 计算得出:占空比=(CRR2+1)/(CRR1+1)

image-20210930000858313

高级定时器

高级定时器包括TIM1、TIM8

功能特性:

  • 时钟源:APB2(72MHz)
  • 计数模式:向上、向下、双向
  • 具备通用定时器所有功能
  • 是6个通道的三相PWM发生器
  • 整体结构和基本、通用定时器相同(多出BPK、DTG两个结构),配置方法差不多
  • 死区时间控制(源于BPK、DTG的结构)

定时器相关库函数

定时器库函数存放在标准外设库的 stm32f10x_tim.hstm32f10x_tim.c 文件中,使用时要在用户程序中导入(#include stm32f10x_tim.h,或者在 stm32f10x_conf.h中去除掉相关注释);

常用库函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
TIM_DeInit();
TIM_TimeBaseInit(); //根据指定结构体初始化TIMx
TIM_OCxInit(); //初始化外设TIM的通道x
TIM_Cmd(); // 使能或禁止定时器
TIM_GetFlagStatus(); //检测指定TIMx的定时器标志位
TIM_ClearFlag();
TIM_ITConfig(); //使能或禁止指定TIMx的中断
TIM_GetITStatus(); //检测中断标志位状态,判断中断是否发生,中断标志位要软件清零
TIM_ClearITPendingBit(); //清除TIMx中断挂起位
TIM_CtrlPWMOutputs();
TIM_ARRPreloadConfig(); //使能或禁止TIMx在ARR上的预装载寄存器
TIM_OCxPreloadConfig(); //使能或禁止TIMx在CCRx上的预装载寄存器

定时器中断

当定时器计数值溢出时,可以让其产生中断信号,然后配置中断服务函数(即产生中断后该做些什么,通常是PPP_IRQHandler()形式的函数)

以定时器TIM2为例,如何初始化让其产生中断

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
void TIM2_init(u16 arr, u16 psc){
TIM_TimeBaseInitTypeDef TIM_Structure;
NVIC_InitTypeDef NVIC_Structure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

//延时时间(单位:s):arr/(72000000/psc)
TIM_Structure.TIM_Period = arr - 1; //装载值
TIM_Structure.TIM_Prescaler = psc-1; //分频系数
TIM_Structure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_Structure);

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启定时器TIM2中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIMx中断挂起位
NVIC_Structure.NVIC_IRQChannel = TIM2_IRQn; //指明中断服务函数通道
NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_Structure.NVIC_IRQChannelSubPriority = 1; //子优先级
NVIC_Structure.NVIC_IRQChannelCmd = ENABLE; //中断使能配置
NVIC_Init(&NVIC_Structure);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_Cmd(TIM2, ENABLE);
}

//中断服务函数
void TIM2_IRQHandler(void){

if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){
LED_Flag = !LED_Flag;
if(LED_Flag) {
SetLED_On(LEDE);
SetLED_Off(LEDB);
}
else {
SetLED_Off(LEDE);
SetLED_On(LEDB);
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //软件清除中断挂起位
}
}
color:blue 中断相关知识

优先级:数字越小优先级越高

抢占优先级:优先级越高(数字越小)越先被执行,优先级高的可以打断优先级低的,优先级相同时看子优先级

子优先级(也叫响应优先级):

  1. 两个中断同时到达,抢占优先级相同,子优先级高的先执行;
  2. 当子优先级高的中断到来时,遇到子优先级低的中断正在执行(两者抢占优先级一样),那子优先级高的中断也要等待子优先级低的中断先执行完;

优先级分组:

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级(指优先级这个数字,不能超过分配给它的最大存储值);

在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

  1. 所有8位用于指定响应优先级
  2. 最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
  3. 最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
  4. 最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
  5. 最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
  6. 最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
  7. 最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
  8. 最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。

Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:
第0组:所有4位用于指定响应优先级,此时不会发生中断嵌套
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

1
2
3
4
5
NVIC_PriorityGroup_0 //选择第0组
NVIC_PriorityGroup_1 // 选择第1组
NVIC_PriorityGroup_2 // 选择第2组
NVIC_PriorityGroup_3 // 选择第3组
NVIC_PriorityGroup_4 // 选择第4组

如何指定中断源的抢占式优先级和响应优先级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// 使能EXTI9_5中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

评论