STM32-定时器

时间之河奔腾不息,子在川上曰:“逝者如斯夫,不舍昼夜。”

我们使用各种方法来估量时间,具体到STM32,当然是少不了定时器!

本文关于定时器的内容,分为下面几部分:

1,定时器功能的实现(TIM1);
2,定时间隔的计算公式;
3,依赖的库文件;
4,另一个定时器的实现(TIM14);

先简单介绍下开发环境,芯片类型是stm32F030C8,集成开发环境用的是Keil5 MDK-ARM,仿真器使用JLINK。


1,定时器功能的实现(TIM1)
定时器功能代码,主要分2部分:初始化函数,实现中断处理函数。
另外,实现了一个延时函数,供外部调用。

初始化函数:

void TIM1_Init(void)
{
    TIM_TimeBaseInitTypeDef          TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef                NVIC_InitStructure;

//    系统中TIM1用的是APB2,TIM14时钟用的是APB1
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);  //tim1时钟使能,APB1时钟8M
        

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2; //分频系数为2   //是对APB1的2倍频进行分频,分频系数为2,所以频率还是8M
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数

        TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数设置  //对于TIM1是必须设置的

//    计算定时周期: t=(9+1)*1/f=10/(8M/(799+1))=10/10k(s)=1ms
        TIM_TimeBaseInitStructure.TIM_Period = 9;             //定时1000us - 1ms  //最大65536
//        TIM_TimeBaseInitStructure.TIM_Period = 99;             //定时10ms,降低进入中断的频率  //最大65536
    TIM_TimeBaseInitStructure.TIM_Prescaler = 799;  //时钟8M

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);


    TIM_ClearITPendingBit(TIM1,TIM_IT_Update);//清除TIM1的中断待处理位:TIM 中断源
    TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);     //允许定时器1更新中断
    TIM_Cmd(TIM1,ENABLE); //使能定时器1


//    设置中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_UP_TRG_COM_IRQn; //定时器1中断
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

}
 

中断处理函数:

void TIM1_BRK_UP_TRG_COM_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM1,TIM_IT_Update) != RESET) //溢出中断
    {
            if(gTimer>0){
                gTimer--;
            }
    }
    TIM_ClearITPendingBit(TIM1,TIM_IT_Update);  //清除中断标志位
}

延时函数:

void delay_ms_tim(uint32_t nTimer)  
{  
        gTimer=nTimer;
//      gTimer=nTimer/10;  //定时10ms,降低进入中断的频率。也降低了定时的准确性。
      while(gTimer);


2,定时间隔的计算公式

先要确定系统工作的主频
在我的系统中,没有对系统时钟做设置,使用了默认的内部晶振HSI,主频为8M。
TIM1连接在APB2上,APB2与系统频率一致,即8M。

具体到每个定时器,又有一个分频系数,我们是这么设置的:    
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2; 
由于系统中是对APB1的2倍频进行分频,这里取的分频系数为2,所以TIM1的频率还是8M。

定时器的预分频参数与周期数如下设置的:
    TIM_TimeBaseInitStructure.TIM_Prescaler = 799;  //时钟8M
    TIM_TimeBaseInitStructure.TIM_Period = 9;             //定时1000us - 1ms  //最大65536
    计算定时周期: 

t=(TIM_Period+1)*1/(f/(TIM_Prescaler+1))
t=(9+1)*1/(f/(799+1))=10/(8M/800)=10/10k(s)=1/1000(s)=1ms

看到这里,我们就知道了,这样设置的TIM1,每1ms进入一次溢出中断。

假如我想减少进入中断的次数,例如每10ms进入一次,应该怎么改呢?
有两种方法:
1,其他设置不变,加大TIM_Period,如下:
        TIM_TimeBaseInitStructure.TIM_Period = 99;             //定时10ms,降低进入中断的频率  
t=(99+1)*1/(f/(799+1))=100/(8M/800)=100/10k(s)=10/1000(s)=10ms
2,其他设置不变,加大TIM_Prescaler,如下:
        TIM_TimeBaseInitStructure.TIM_Prescaler = 7999; 
t=(9+1)*1/(f/(7999+1))=10/(8M/8000)=10/1k(s)=10ms

3,依赖的库文件

在本工程下的目录:STM32F030x8_Timer\STM32F03x_FWLib\src\stm32f0xx_tim.c
相应的头文件:stm32f0xx_tim.h
结构体:TIM_TimeBaseInitTypeDef
关键函数,例如:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
适当的查看库函数名、库函数的实现,会增加对stm32系统的了解。


4,另一个定时器的实现(TIM14)

相对于TIM,主要的改变就是,将TIM1修改为TIM14。
另外还有3个地方需要注意:

1,不同定时器,所在外设总线不同,在stm32系统中TIM14时钟用的是APB1,TIM1用的是APB2。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);  //tim1时钟使能,APB1时钟8M

2,TIM1是高级计数器,必须将重复计数设置为0
repetitioncounter是重复计数,就是重复溢出多少次才给你来一个溢出中断,
  是在本次定时结束后,再重装载定时几次,才进入中断。对于通常的应用,设置为0。
    TIM_TimeBaseInitStructure.repetitioncounter = 0;//重复计数设置  
当然,在TIM14的初始化中也这样设置上,也是没错的。
3,中断入口函数名的差异:
TIM1:TIM1_BRK_UP_TRG_COM_IRQn()
TIM14:TIM14_IRQHandler()

最后的实现如下:

初始化函数:
void TIM14_Init(void)
{

    TIM_TimeBaseInitTypeDef          TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef                NVIC_InitStructure;

//    系统中TIM14时钟用的是APB1,TIM1用的是APB2
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);  //tim14时钟使能,APB1时钟8M
        

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2; //分频系数为2   //是对APB1的2倍频进行分频,分频系数为2,所以频率还是8M
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数设置  //对于TIM1是必须设置的

//    计算定时周期: t=(9+1)*1/f=10/(8M/(799+1))=10/10k(s)=1ms
        TIM_TimeBaseInitStructure.TIM_Period = 9;             //定时1000us - 1ms  //最大65536
//        TIM_TimeBaseInitStructure.TIM_Period = 99;             //定时10ms,降低进入中断的频率  //最大65536
    TIM_TimeBaseInitStructure.TIM_Prescaler = 799;  //时钟8M

    TIM_TimeBaseInit(TIM14, &TIM_TimeBaseInitStructure);


    TIM_ClearITPendingBit(TIM14,TIM_IT_Update);//清除TIM14的中断待处理位:TIM 中断源
    TIM_ITConfig(TIM14,TIM_IT_Update,ENABLE);     //允许定时器14更新中断
    TIM_Cmd(TIM14,ENABLE); //使能定时器14


//    设置中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = TIM14_IRQn;//定时器14中断
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

}

中断处理函数:
void TIM14_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM14,TIM_IT_Update) != RESET) //溢出中断
    {
            if(gTimer>0){
                gTimer--;
            }                
    }
    TIM_ClearITPendingBit(TIM14,TIM_IT_Update);  //清除中断标志位
}

延时函数没有变化。
注意,此工程中不能同时将TIM1与TIM14的初始化打开,若打开了,由于两个中断处理里面操作了同一个全局变量gTimer,会导致计数不符合预期。

5,这个demo的工程代码,可以从如下地址获取:

https://download.csdn.net/download/lintax/10734630

猜你喜欢

转载自blog.csdn.net/lintax/article/details/84918791