A strong man will struggle with the storms of fate.(Thomas Addison)
强者能同命运的风暴抗争。(爱迪生)
通用定时器之精准延时
通用定时器概述
STM32定时器分类
定时器分类:高级定时器 通用定时器 基本定时器
通用定时器:TIM2 TIM3 TIM4 TIM5 TIM9 TIM10 TIM11 TIM12 TIM13 TIM14
定时器都挂在APB1及APB2下
挂在APB1定时器: TIM2、TIM3、TIM4、TIM5、TIM6、TIM7、TIM12、TIM13、TIM14
挂在APB2定时器: TIM1、TIM8、TIM9、TIM10、TIM11
如果APB预分频器为1,定时器时钟频率等APB域的频率。否则,等于APB域的频率的两倍(×2)
挂在APB1定时器频率:APB1X2 = 42MHZ X2 = 84MHZ
挂在APB2定时器频率:APB2X2 = 84MHZ X2 = 168MHZ
STM32F4 的定时器有14个,其中TIM2~TIM5,TIM9~TIM14属于通用定时器,TIM1 和TIM8 则属于高级控制定时器,TIM6和TIM7是基本定时器。

TIM2 到 TIM5 主要特性
16 位(TIM3 和 TIM4)或 32 位(TIM2 和 TIM5) 递增、递减和递增/递减自动重载计数器。
16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改) ,分频系数介于 1 到 65536 之间。
发生如下事件时生成中断/DMA 请求(6个独立的IRQ/DMA请求生成器):
TIM9到 TIM14 通用定时器具有以下特性:
16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 和 65536 之间
通用定时器的应用:
STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。
通用定时器的计数模式:
- 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
- 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
- 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

定时器的时钟来源有 4 个:
- 内部时钟(CK_INT)
- 外部时钟模式 1:外部输入脚(TIx)
- 外部时钟模式 2:外部触发输入(ETR) ,仅适用于 TIM2、TIM3、TIM4
- 内部触发输入(ITRx) :使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟) 。
这些时钟,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置。这里的 CK_INT时钟是从 APB1 倍频的来的,除非 APB1 的时钟分频数设置为 1(一般都不会是 1) ,否则通用定时器 TIMx 的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx的时钟就等于 APB1 的时钟。这里还要注意的就是高级定时器以及 TIM9~TIM11 的时钟不是来自 APB1,而是来自 APB2 的。
定时器中断的配置
定时器相关的库函数主要集中在固件库文件 stm32f4xx_tim.h 和 stm32f4xx_tim.c 文件中。定时器配置步骤如下:
1、能定时器时钟。根据定时器挂在APB1及APB2还是下
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)//定时器挂在APB1
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)//定时器挂在APB22、初始化定时器,配置ARR,PSC。
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)3、启定时器中断,配置NVIC。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)4、设置 TIM3_DIER 允许更新中断
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)5、使能定时器。
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)6、编写中断服务函数。
TIMx_IRQHandler();定时器时钟实现例程
tim.c
#include "tim.h"
static __IO uint32_t TimingDelay;
//延迟函数
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
/*
定时器说明
TIM3 -- APB1
定时器TIM3:84MHZ
16位定时器:值范围:0~65535
*/
void Tim3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //初始化定时器结构体
NVIC_InitTypeDef NVIC_InitStruct; //配置NVIC结构体
//1、能定时器时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStruct.TIM_Prescaler = 84-1; //84分频 84MHZ/84 = 1MHZ
TIM_TimeBaseInitStruct.TIM_Period = 1000-1; //计1000个数 在1MHZ下,用时1ms
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStruct.TIM_ClockDivision= TIM_CKD_DIV1; //分频因子
//2、初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;//NVIC通道,在stm32f4xx.h可查看通道 (可变)
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority= 0x01; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能
//3、启用定时器中断,配置NVIC。
NVIC_Init(&NVIC_InitStruct);
//4、设置 TIM3_DIER 允许更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
//5、使能定时器。
TIM_Cmd(TIM3, ENABLE);
}
//编写中断服务函数。这个函数不需要程序员在主函数调用,满足条件CPU自行调用的函数
//每来一次中断就TimingDelay的值就减一
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h"
#include "tim.h"
int main(void)
{
//NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//LED灯初始化
Led_Init();
//定时器3初始化
Tim3_Init();
while(1)
{
GPIO_ToggleBits(GPIOE, GPIO_Pin_14);
Delay(1000);
}
return 0;
}