利用通用定时器TIM2进行精确延时
1. 通用定时器概述及性能
1.1 概述
在作者所使用的stm32芯片上,共有TIM1 1个高级定时器以及TIM2、TIM3、TIM4共3个通用定时器。其中各通用定时器均由一个通过可编程预分频器驱动的16位自动装载计数器构成。适用于多种场合,包括测量输入信号的脉冲长度(输入采集)或者产生输出波形(输出比较和PWM)。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。定时器是完全独立的,而且没有互相共享任何资源。它们可以一起同步操作。
1.2 性能
通用TIMx定时器特性包括:
- 16 位向上,向下,向上/向下自动装载计数器
- 16 位可编程预分频器,计数器时钟频率的分频系数为1~65535 之间的任意数值
- 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
- 使用外部信号控制定时器和定时器互连的同步电路
- 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动,停止,初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
1.3 部分寄存器
由于通用定时器较为复杂,此处仅介绍如何利用TIM2进行精确延时。下面介绍是TIM中与本篇内容有关的寄存器。
1.3.1控制寄存器1(TIMx_CR1)
1.3.2 DMA/中断使能寄存器(TIMx_DIER)
本篇中只关心其第6位及第0位
由于需要TIM在更新时产生中断,因此这两位都必须置1。
1.3.3 预分频器(TIMx_PSC)
该分频器对时钟进行分频,并作为计数器时钟。
时钟来源有4种:
1)内部时钟(CK_INT)
2)外部时钟模式1:外部输入脚(TIx)
3)外部时钟模式2:外部触发输入(ETR)
4)内部触发输入(ITRx):使用A定时器作为B定时器的预分频器(A为B提供时钟)。
具体选择可以通过TIMx_SMCR寄存器相关位来设置。
1.3.4 自动重装载寄存器(TIMx_ARR)
1.3.5 状态寄存器(TIMx_SR)
本篇中主要关心该寄存器第0位
当产生溢出时,该标记位被置1,执行更新事件后由软件清0。
其他众多关于TIM寄存器可查阅《STM32F10x参考手册》
1.4基本计数中断过程
按照自己的理解,使能中断及定时器后,根据时钟来源以及预分频器(TIMx_PSC)所设置的分频数开始计数,当计数达到自动重装载寄存器(TIMx_ARR)中所存储的值时,状态寄存器(TIMx_SR)中对应的标志位(第0位)置1,通过软件检查该位则可以更新事件。
例如:系统中APB1(TIM2被挂在APB下)时钟频率为36MHz,设置TIMx_PSC分频为36000,则作为定时器TIM2的时钟频率为36000000/36000=1000Hz,即1秒内内部开关计数1000次,每次1ms,设置TIMx_ARR重装值为1000,则计数达1000次时,TIMx_SR中第0位标志位被置1,此时为延迟1秒。
2 TIM固件库函数
2.1 函数TIM_DeInit
函数名 |
TIM_DeInit |
函数原型 |
void TIM_DeInit(TIM_TypeDef* TIMx) |
功能描述 |
将外设TIMx寄存器重设为缺省值 |
输入参数 |
TIMx:x可以是2、3或4,来选择TIM外设 |
被调用函数 |
RCC_APB1PeriphClockCmd(). |
例:重设TIM2
TIM_DeInit(TIM2);
2.2 函数TIM_TimeBaseInit
函数名 |
TIM_TimeBaseInit |
函数原型 |
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) |
功能描述 |
根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 |
输入参数1 |
TIMx:x可以是2、3或4,来选择TIM外设 |
输入参数2 |
TIMTimeBase_InitStruct:指向结构TIM_TimeBaseInitTypeDef的指针,包含了TIMx时间基数单位的配置信息 参阅Section:TIM_TimeBaseInitTypeDef查阅更多该参数允许取值范围. |
TIM_TimeBaseInitTypeDef structure
TIM_TimeBaseInitTypeDef定义于文件“stm32f10x_tim.h”:
typedef struct
{
u16 TIM_Period;
u16 TIM_Prescaler;
u8 TIM_ClockDivision;
u16 TIM_CounterMode;
} TIM_TimeBaseInitTypeDef;
TIM_Period
TIM_Period设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。它的取值必须在0x0000和0xFFFF之间。
TIM_Prescaler
TIM_Prescaler设置了用来作为TIMx时钟频率除数的预分频值。它的取值必须在0x0000和0xFFFF之间。
TIM_ClockDivision
TIM_ClockDivision设置了时钟分割。该参数取值见下表。
TIM_ClockDivision |
描述 |
TIM_CKD_DIV1 |
TDTS = Tck_tim |
TIM_CKD_DIV2 |
TDTS =2 Tck_tim |
TIM_CKD_DIV4 |
TDTS =4 Tck_tim |
TIM_CounterMode
TIM_CounterMode选择了计数器模式。该参数取值见下表。
TIM_CounterMode |
描述 |
TIM_CounterMode_Up |
TIM向上计数模式 |
TIM_CounterMode_Down |
TIM向下计数模式 |
TIM_CounterMode_CenterAligned1 |
TIM*对齐模式1计数模式 |
TIM_CounterMode_CenterAligned2 |
TIM*对齐模式2计数模式 |
TIM_CounterMode_CenterAligned3 |
TIM*对齐模式3计数模式 |
例:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 0xF;
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, & TIM_TimeBaseStructure);
2.3 函数TIM_ClearFlag
函数名 |
TIM_ClearFlag |
函数原型 |
void TIM_ClearFlag(TIM_TypeDef* TIMx, u32 TIM_FLAG) |
功能描述 |
清除TIMx的待处理标志位 |
输入参数1 |
TIMx:x可以是2,3或者4,来选择TIM外设 |
输入参数2 |
TIM_FLAG:待清除的TIM标志位 参阅Section:TIM_FLAG查阅更多该参数允许取值范围 |
TIM_FLAG值
TIM_FLAG |
描述 |
TIME_FLAG_Update |
TIM更新标志位 |
…… |
…… |
例:
TIM_ClearFlag(TIM2,TIME_FLAG_Update);
2.4 函数TIM_ITConfig
函数名 |
TIM_ITConfig |
函数原型 |
void TIM_ITConfig(TIM_TypeDef* TIMx, u16 TIM_IT, FunctionalState NewState) |
功能描述 |
使能或者失能指定的TIM中断 |
输入参数1 |
TIMx:x可以是2,3或者4,来选择TIM外设 |
输入参数2 |
TIM_IT:待使能或者失能的TIM中断源 参阅Section:TIM_IT 查阅更多该参数允许取值范围 |
输入参数3 |
NewState:TIMx中断的新状态 这个参数可以取:ENABLE或者DISABLE |
TIM_IT值
TIM_FLAG |
描述 |
TIME_FLAG_Update |
TIM中断源 |
…… |
…… |
例:
TIM_ITConfig(TIM2,TIME_FLAG_Update,ENABLE);
2.5 函数TIM_Cmd
函数名 |
TIM_Cmd |
函数原型 |
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) |
功能描述 |
使能或者失能TIMx外设 |
输入参数1 |
TIMx:x可以是2,3或者4,来选择TIM外设 |
输入参数2 |
NewState:TIMx中断的新状态 这个参数可以取:ENABLE或者DISABLE |
例:
TIM_Cmd(TIM2,ENABLE);
3 例程程序
本例程主要使用TIM2进行精准延时并亮灭LED灯,其中NVIC部分暂做了解,后面再继续深入。另外,需要注意3.0以后版本的固件库相比2.0版有所更改,如删除旧版NVIC部分函数,或移动至misc.c文件中,通道名TIM2_IRQChannel更改为TIM2_IRQn等。完整构架:
完整代码:
#include "stm32f10x.h"
void delay1ms(u32 nTimer);
void GPIO_Configuration(void);
void TIM2_IRQHandler(void);
void Timer2_Configuration(void);
void NVIC_Configuration(void);
int main(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能GPIOC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2时钟GPIO_Configuration();NVIC_Configuration();//配置中断Timer2_Configuration();//配置定时器 while(1) { GPIO_ResetBits(GPIOC,GPIO_Pin_7|GPIO_Pin_6); GPIO_SetBits(GPIOC,GPIO_Pin_9|GPIO_Pin_8); delay1ms(1000); GPIO_ResetBits(GPIOC,GPIO_Pin_9|GPIO_Pin_8); GPIO_SetBits(GPIOC,GPIO_Pin_7|GPIO_Pin_6); delay1ms(1000); GPIO_Write(GPIOC,0x0140); delay1ms(2000); GPIO_Write(GPIOC,0x0280); delay1ms(2000); }} void GPIO_Configuration(void){ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_Init(GPIOC,&GPIO_InitStructure);} void Timer2_Configuration(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_DeInit(TIM2);//使用缺省值初始化TIM外设寄存器 TIM_TimeBaseStructure.TIM_Period=1;//自动重装载寄存器值为1 TIM_TimeBaseStructure.TIM_Prescaler=(36000-1);//时钟预分频数为36000 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//采样分频倍数1,未明该语句作用。 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//上升模式 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除更新标志位 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能中断 TIM_Cmd(TIM2,ENABLE);//使能TIM2定时器} void NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;NVIC_SetPriorityGrouping(NVIC_PriorityGroup_0);NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //3.0版以后的函数库将各通道TIM2_IRQChanel改名TIM2_IRQnNVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//NVIC_Init函数被包含在misc.c文件中。} volatile u32 gTimer;void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)//检查溢出信号 { TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除溢出标志 gTimer--; }} void delay1ms(u32 nTimer){ gTimer=nTimer; while(gTimer);}
完成编译并烧录后,开发板上四个LED灯先相邻两两亮灭,亮灭时间延迟1秒,然后交替两两亮灭,亮灭时间延迟2秒。
参考文献
[1] Sharkdo.STM32用定时器精确延时的方法 [EB/OL].http://www.cnblogs.com/sharkdo/archive/2011/03/23/1993036.html,2011-03-23/2012-10-14
[2] Cdzlllfe.stm32 通用定时器精确延时程序[EB/OL].
http://blog.sina.com.cn/s/blog_88534dff01010t1a.html,2011-12-17/2012-10-14
[3]福州芯达工作室.《STM32入门系列教程——定时器与蜂鸣器》[EB/OL]. http://ishare.iask.sina.com.cn/f/10918196.html,2010-10-20/2012-10-14.
[4]正点电子.《Stm32不完全手册》[EB/OL]. http://www.amobbs.com/forum.php?mod=viewthread&tid=4517523,2011-01-17/2012-10-15
[5]半壶水,《STM32 菜鸟学习手册-罗嗦版》,http://wenku.baidu.com/view/fc7c7d20ccbff121dd3683da.html, 2012-08-19.
[6] ST.《如何从STM32F10xxx固件库V2.0.3 升级为STM32F10xxx标准外设库V3.0.0》[EB/OL]. http://ishare.iask.sina.com.cn/f/18297257.html?from=like,2011-08-22/2012-09-09.