时间之河奔腾不息,子在川上曰:“逝者如斯夫,不舍昼夜。”
我们使用各种方法来估量时间,具体到STM32,当然是少不了定时器!
本文关于定时器的内容,分为下面几部分:
1,定时器功能的实现(TIM1);
2,定时间隔的计算公式;
3,依赖的库文件;
4,另一个定时器的实现(TIM14);
先简单介绍下开发环境,芯片类型是stm32F030C8,集成开发环境用的是Keil5 MDK-ARM,仿真器使用JLINK。
定时器功能代码,主要分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);
}
中断处理函数:
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;
while(gTimer);
}
先要确定系统工作的主频
在我的系统中,没有对系统时钟做设置,使用了默认的内部晶振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
在本工程下的目录: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系统的了解。
相对于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_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,会导致计数不符合预期。
https://download.csdn.net/download/lintax/10838049