TIM定时器
本篇教程针对STM32F103ZET6,因为C8T6的定时器个数和ZET6可能对不上
STM32当中一共有8个定时器,其中这八个定时器分为基本、通用和高级定时器。
基本定时器只能往上递增计数,从0到1、2、3……。由于基本定时器没有捕获比较通道,所以不能产生PWM波,也不能对外界输入脉冲进行捕获。
其余定时器可以递增计数也可以递减计数
单片机的八个定时器都可以产生DMA
一、基本定时器功能详解
基本定时器的主要功能就是定时,它用来计数和产生PWM波。
定时器定时也是通过计算固定周期的脉冲个数来定时的,像51单片机通过对机器周期计数(如果外部晶振为2M,则一个机器周期为1us),计数1000次脉冲则定时了1000us。
STM32的基本定时器定时方法也是类似的,我们需要先了解基本定时器定时的脉冲来源,基本定时器的脉冲来源于内部时钟(即图中的1号红圈圈所示)。
通过查看外设模块框图可知(中文数据手册),基本/通用定时器的定时脉冲来源于内部的APB1时钟总线上,而高级定时器的脉冲来源于APB2时钟总线上。
由时钟树可知,36MHz信号从APB1出来,没有直接进入定时器,而是经过了预分频。
其中如果APB1的分频系数等于1,那么时钟信号仍然保持36MHz,即不分频。如果该倍频系数不等于1时,则该时钟信号就会被倍频,即36MHz*2 = 72MHz,将72MHz的时钟信号提供给这些定时器。
在程序控制上,是由static void SetSysClockTo72(void);这个函数来控制时钟频率的(system_stm32f10x.c文件中可查)
这里的预分频系数代码已经设置成2了。
当72MHz进入定时器后还需要再经过一个PSC预分频(即红圈圈2),这里的分频系数等于PSC+1,经过这里的预分频后,时钟频率就变成了72MHz/PSC+1
如果不经过该预分频器,每个脉冲的周期时间为1/72MHz,即便将计数器计数65535次,计数的时间依旧是极短的,所以需要将频率降下来,方便计数器计数和计时。
在STM32当中,计数器计数溢出不需要计数到65535计满后溢出,而是可以通过自动重装载计数器(即4号红圈圈部分)来指定计数的最大值,将该值命名为ARR,这个值一样也是16位的。
如果ARR被设置为1000,则计数器的计数范围为0-1000,这时候再来一个脉冲,产生溢出之后又回到0,如此反复。(所以ARR实际上就是计数的上限值)
所以实际上计数周期/次数,一共有ARR+1次
因此定时的时间为单个脉冲的周期时间乘以总的定时的次数,即:定时时间T= ((PSC+1)/72M * (ARR + 1))秒
如果需要将定时时间的单位计算为毫秒,则可以乘以1000,即:定时时间T= ((PSC+1)/72000 * (ARR + 1))毫秒
根据定时时间可以对两个参数进行灵活的取值,这两个参数都是16位的,PSC和ARR可以取在0-65535之间的任意值。
取零问题:
如果PSC取0的话,就相当于预分频器不分频;可是如果在代码中ARR1取0时,代码会一直进入中断(从0开始计数后下一个脉冲又从0开始),所以取0在这里是没有意义的,因此ARR在代码中是不赋值为0的。
二、定时器定时的代码实现
对于F103C8T6单片机而言,一共有4个Timer定时器,其中TIM1为高级定时器,TIM2、3、4为标准定时器。
而F103ZET6单片机的定时器如开头所说,有8个TIM定时器。
1、打开外设时钟
使用TIMER2这个定时器,首先要先打开TIMER的时钟,这里使用RCC库当中的函数: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
2、基本初始化TIM定时器
调用TIM库当中的函数:
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = 1; //再分频的分频系数
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //定时器的计数模式
TIM_TimeBaseInitStruct.TIM_Period = 999; //计数周期(次数)
TIM_TimeBaseInitStruct.TIM_Prescaler = 7199; //预分频的分频系数
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //设置需要定时几次后进入中断
//对TIM2进行基本初始化
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
其中计数模式一共有三种,分别是向上计数模式(从0开始计数到规定值),向下模式(从规定值计数到0),和中央对齐模式(从0开始计数到规定值,再从规定值计数到0)。
其中中央对其模式还分为以下三种:
内部时钟再分频因子可将进入TIM定时器的72MHz频率继续分频,一般为了方便计算不再继续分频,这里设置为1:
如果因子设置为2,则周期为原来的2倍;设置为4则周期为原来的4倍。
3、开启定时器的更新中断
使用TIM库函数:
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能TIM2定时器的更新中断
4、设置中断的优先级
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; //中断通道号
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能开关
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //从优先级
//对中断优先级进行配置
NVIC_Init(&NVIC_InitStruct);
5、打开定时器
//启动定时器
TIM_Cmd(TIM2, ENABLE);
6、编写中断服务函数
在启动文件中找到TIM2的中断服务函数:
void TIM2_IRQHandler();
同样的,进入中断函数后还要对中断标志位进行判断:
完整代码实现:
#include "Timer.h"
#include "stdio.h"
void Timer2_init() {
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟再分频因子
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //定时器的计数模式
TIM_TimeBaseInitStruct.TIM_Period = 999; //计数周期(次数)ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 7199; //预分频的分频系数 PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //设置需要定时几次后进入中断
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; //中断通道号
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能开关
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //从优先级
//打开TIM2的外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//对TIM2进行基本初始化
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
//打开定时器的更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//对中断优先级进行配置
NVIC_Init(&NVIC_InitStruct);
//启动定时器
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler() {
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
printf("You into the interrupt!\n");
printf("The time is 100ms.");
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// TIM_ClearFlag(TIM2, TIM_IT_Update);
}
三、定时器产生PWM波形
STM32F103ZET6一共有8个定时器,除了6和7这两个基本定时器不能产生PWM,其余的6个都可以,高级定时器1个可产生7路,通用定时器1个可以产生4路,因此一共可以生成2x7+4x4=30路
- PWM1在向上计数时,CNT<CRR,则输出的为有效电平
- PWM2在向上计数时,CNT<CRR,则输出的为无效电平
CRRx = 25% = (ARR+1)/4
定时器定时原理:
- 首先通过CNT寄存器负责脉冲计数,在经过分频系数分频过后,确定每个脉冲的时间(如果不经过分频,每个脉冲的时间就是1/72us)。
- 当设置的限制值ARR = 999时,即设置脉冲的计数值为999;当脉冲计数到999时,脉冲计数到达设置值,在下一个脉冲计数时进入中断并将计数值清零。所以一个技术周期所需要的脉冲数为999 + 1。
- PWM的生成和定时器定时同理,也是设置脉冲计数值,这里是CCRx寄存器负责PWM的脉冲计数。
PWM生成原理:
- 如果CCRx中的匹配值设置为200,当脉冲计数到200与匹配值相等时,将输出的电平取反;当脉冲计数到达ARR = 999时,电平反转回原来的状态。
- 由图可知此时PWM的周期与定时器的定时时间是相同的,其高电平的时间取决于CCRx这个寄存器内匹配值的大小以及每个计数脉冲的周期长短。
编程步骤:
1、使能时钟
- TIM3通道1 = PA6/PA7
- GPIO_AF_PP
2、定时器复位
- TIMDeinit
3、定时器初始化
- OC1Init(通道1)
- 电平高
- 解调1
- Pulse占空比参数
- Mode = PWM模式
- 状态enable
- Baseinit(基本初始化)
4、cmd使能
四、定时器的输入捕获
输入捕获可以用来测量脉冲宽度或者测量频率
在C8T6中每个定时器都有输入捕获功能;而ZET6除了TIM6和TIM7之外,其余定时器都有输入捕获功能。
输入捕获的过程:
输入检测就是检测定时器通道上的边沿信号,在边沿信号发生跳变(上升沿/下降沿)时,将当前定时器的计数值(TIMx_CNT)存放到对应通道的捕获/比较寄存器(TIMx_CCRx)当中,由此完成一次捕获。同时还可以配置捕获时是否触发中断/DMA等。