STM32学习笔记——利用通用定时器TIM2进行精确延时

利用通用定时器TIM2进行精确延时

1. 通用定时器概述及性能

1.1 概述

在作者所使用的stm32芯片上,共有TIM1 1个高级定时器以及TIM2TIM3TIM43个通用定时器。其中各通用定时器均由一个通过可编程预分频器驱动的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设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。它的取值必须在0x00000xFFFF之间。

TIM_Prescaler 

TIM_Prescaler设置了用来作为TIMx时钟频率除数的预分频值。它的取值必须在0x00000xFFFF之间。

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

例程程序

本例程主要使用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_IRQn
NVIC_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=like2011-08-22/2012-09-09.

<think>嗯,用户想找关于STM32延时函数的资料和实现方法。好的,我需要先回忆一下STM32延时函数有哪些常见的方法。记得之前学过几种不同的实现方式,比如简单的循环延时、使用SysTick定时器,还有用其他定时器外设。首先,用户可能需要了解这些方法的基本原理和各自的优缺点。 首先,最简单的延时应该是循环延时,也就是通过执行空循环来消耗时间。这种方法不需要外设,但精度不高,而且受主频影响大。比如在51单片机里常用,STM32也可以用,但需要注意主频变化时的调整。可能需要示例代码,比如用for循环嵌套的那种。 然后是SysTick定时器,这是Cortex-M内核自带的,通常用来提供系统时钟节拍,比如给操作系统用。使用SysTick可以实现更精确延时,比如HAL库中的HAL_Delay函数就是基于这个的。用户可能需要知道如何配置SysTick,以及如何自己编写延时函数,比如初始化SysTick,设置重装载值,然后等待计数器到零。 另外,还可以用其他定时器外设,比如TIM1、TIM2等,通过配置定时器的预分频和重装载值来生成精确延时。这种方法灵活性高,适合需要高精度或复杂定时的场景。可能需要提到如何配置定时器,启动计数器,并等待更新事件或中断。 用户可能还关心如何选择适合自己项目的方法,比如如果只是简单应用,循环延时可能足够;如果需要高精度或者多任务,SysTick或定时器更好。还要注意在中断中使用延时的问题,比如HAL_Delay在中断中使用可能导致问题,这时候可能需要其他方法。 还要考虑参考资料,比如ST的官方文档、HAL库的用户手册,以及相关的学习笔记或社区文章。比如引用中的内容提到SysTick_Config函数和示例代码,可能需要指出这些参考来源。 用户的问题可能还涉及具体的代码示例,比如如何用循环实现微秒级延时,如何用SysTick实现毫秒级延时,以及如何用TIM2实现更灵活的延时。需要确保代码示例正确,并符合STM32的编程规范,比如使用HAL库或者直接寄存器操作。 可能还需要提到注意事项,比如主频变化时的调整,中断对延时的影响,以及不同型号STM32的差异。比如如果使用HAL库,主频改变后HAL_Delay会自动调整,但自己写的延时函数可能需要手动更新参数。 最后,生成的相关问题应该覆盖用户可能进一步想了解的内容,比如如何提高延时精度、不同定时器的比较、在实时操作系统中的使用,以及如何验证延时是否正确。这些问题可以帮助用户深入理解并解决实际开发中的问题。</think>### STM32延时函数实现方法详解 #### 一、循环延时(基础方法) 通过空循环消耗时间实现,适用于简单场景。 **示例代码(微秒级延时)**: ```c #define DELAY_LOOP_CYCLES 6 // 根据主频调整的循环周期数 void delay_us(uint32_t us) { for(uint32_t i=0; i<us; i++){ volatile uint32_t count = DELAY_LOOP_CYCLES; while(count--); } } ``` *特点*:无需外设,精度受主频影响较大,需通过示波器校准[^3]。 #### 二、SysTick定时器(推荐方法) 利用Cortex-M内核自带的24位系统定时器,精度更高。 **配置步骤**: 1. 初始化SysTick时钟源(通常选择HCLK) 2. 设置重装载值`ticks = (SystemCoreClock / 1000000) * us` 3. 启动定时器并等待计数完成 **示例代码(毫秒级延时)**: ```c void SysTick_Init(void) { SysTick->CTRL = 0; // 禁用SysTick SysTick->LOAD = (SystemCoreClock/1000) - 1; // 1ms中断 SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; } void delay_ms(uint32_t ms) { while(ms--) { SysTick->VAL = 0; while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); } } ``` *注意*:HAL库中`HAL_Delay()`即基于此实现[^2]。 #### 三、硬件定时器(高精度需求) 使用TIM2/TIM3等通用定时器,支持微秒级高精度延时。 **配置流程**: ```c // TIM2初始化(示例) void TIM2_Init(void) { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->PSC = SystemCoreClock/1000000 - 1; // 1MHz计数 TIM2->ARR = 0xFFFF; TIM2->CR1 |= TIM_CR1_CEN; } void delay_us(uint16_t us) { TIM2->CNT = 0; while(TIM2->CNT < us); } ``` *优势*:可达±0.5us精度,支持中断模式[^4]。 #### 四、参考文档建议 1. 《Cortex-M3权威指南》:SysTick工作原理详解 2. ST官方文档《RM0008 Reference Manual》:定时器配置章节 3. HAL库用户手册:`HAL_Delay()`源码分析[^1]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值