打印
[其他ST产品]

STM32-定时器

[复制链接]
407|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
舒伯特玫瑰|  楼主 | 2023-1-30 23:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我们使用各种方法来估量时间,具体到STM32,当然是少不了定时器!

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

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

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



使用特权

评论回复
评论
舒伯特玫瑰 2023-1-30 23:35 回复TA
———————————————— 版权声明:本文为CSDN博主「lintax」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/lintax/article/details/84918791 
沙发
舒伯特玫瑰|  楼主 | 2023-1-30 23:35 | 只看该作者
一、定时器功能的实现(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_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);

}

使用特权

评论回复
板凳
舒伯特玫瑰|  楼主 | 2023-1-30 23:36 | 只看该作者
中断处理函数:

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);  //清除中断标志位
}

使用特权

评论回复
地板
舒伯特玫瑰|  楼主 | 2023-1-30 23:36 | 只看该作者
延时函数:

void delay_ms_tim(uint32_t nTimer)  
{  
        gTimer=nTimer;
      while(gTimer);
}

使用特权

评论回复
5
舒伯特玫瑰|  楼主 | 2023-1-30 23:37 | 只看该作者
二、定时间隔的计算公式
先要确定系统工作的主频
在我的系统中,没有对系统时钟做设置,使用了默认的内部晶振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

使用特权

评论回复
6
舒伯特玫瑰|  楼主 | 2023-1-30 23:38 | 只看该作者
计算定时周期:

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

使用特权

评论回复
7
舒伯特玫瑰|  楼主 | 2023-1-30 23:38 | 只看该作者
三、依赖的库文件
在本工程下的目录: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系统的了解。

使用特权

评论回复
8
舒伯特玫瑰|  楼主 | 2023-1-30 23:39 | 只看该作者
四、另一个定时器的实现(TIM14)
相对于TIM,主要的改变就是,将TIM1修改为TIM14。
另外还有3个地方需要注意:

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

使用特权

评论回复
9
舒伯特玫瑰|  楼主 | 2023-1-30 23:40 | 只看该作者
2,TIM1是高级计数器,必须将重复计数设置为0
repetitioncounter是重复计数,就是重复溢出多少次才给你来一个溢出中断,
  是在本次定时结束后,再重装载定时几次,才进入中断。对于通常的应用,设置为0。
    TIM_TimeBaseInitStructure.repetitioncounter = 0;//重复计数设置  
当然,在TIM14的初始化中也这样设置上,也是没错的。

使用特权

评论回复
10
舒伯特玫瑰|  楼主 | 2023-1-30 23:40 | 只看该作者
3,中断入口函数名的差异:
TIM1:TIM1_BRK_UP_TRG_COM_IRQn()
TIM14:TIM14_IRQHandler()

使用特权

评论回复
11
舒伯特玫瑰|  楼主 | 2023-1-30 23:41 | 只看该作者
最后的实现如下:

初始化函数:
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_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);

}

使用特权

评论回复
12
舒伯特玫瑰|  楼主 | 2023-1-30 23:42 | 只看该作者
中断处理函数:
void TIM14_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM14,TIM_IT_Update) != RESET) //溢出中断
    {
            if(gTimer>0){
                gTimer--;
            }               
    }
    TIM_ClearITPendingBit(TIM14,TIM_IT_Update);  //清除中断标志位
}

使用特权

评论回复
13
舒伯特玫瑰|  楼主 | 2023-1-30 23:46 | 只看该作者
延时函数没有变化。
注意,此工程中不能同时将TIM1与TIM14的初始化打开,若打开了,由于两个中断处理里面操作了同一个全局变量gTimer,会导致计数不符合预期。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

51

主题

264

帖子

2

粉丝