定时器与延时函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaolongwoaini99/article/details/80202591

STM32定时器包含基本定时器、通用定时器和高级定时器,其中TIM6和TIM7是STM32当中的基本定时器,作为初学者,先从最基本的学起最容易,下面我们用这个定时器实现毫秒延时函数来入门STM32定时器的应用。

学习单片机,就是学习使用它的寄存器。即便你用库函数,寄存器也是必须要学习的。

TIM6 TIM7的寄存器如下所示:

基本定时器寄存器一览表

先略览一下寄存器,CR1和CR1是控制寄存器,SR是状态寄存器,ARR就是溢出值寄存器,CNT就是计数器的当前值,PSC是预分频寄存器。预分频寄存器?听的傻眼了吧,前面几个个寄存器听的还能理解,一听到预分频寄存器,好像不知道是干嘛用的。瑞生来给你解释一下吧,你可以给预分频寄存器里面写一个从0~65535的值,这个值+1,就是定时器运行的时钟。举个例子,比如单片机工作在主频72MHz,预分频寄存器写0,预分频系数就是0+1=1,定时器的时钟就是72MHz/1=72MHz;再举个例子,比如单片机还是工作在主频72MHz,预分频寄存器写71,预分频系数就是71+1=72,定时器的时钟就是72MHz/72=1MHz。知道定时器的时钟有什么用?相信很多初学者不清楚,定时器的时钟关乎定时器计数器CNT递增的时间间隔,根据频率和周期的公式f=1/T,定时器计数器递增的时间间隔就是1/定时器的时钟,例如当定时器时钟为1MHz时,定时器计数器递增的时间间隔就是1/1MHz=1微秒,这时,如果你把溢出值设置为1000,就是1000*1us=1ms溢出。

1.直接操作寄存器

下面,我们先用直接操作寄存器的方式,写一个毫秒延时函数:

void delay_ms(uint16_t ms)
{ RCC->APB1ENR|=1<<1;	//TIM6时钟开启
 TIM6->PSC=35999;
 TIM6->ARR=ms*2;
 TIM6->CR1|=(1<<3);//更新请求
 TIM6->CR1|=0x1;//使能定时器
 while((TIM6->SR&0X1)==0);
 TIM6->SR=0;
}

第一条语句,设置预分频系数为35999+1=36000,所以定时器的时钟为72000000/36000=2000Hz,那么定时时间间隔就是1/2000=0.0005秒,即0.5毫秒。

第二条语句,设置溢出值为ms乘以2,假如要延时1秒,函数的参数ms就是1000,溢出值就是1000*2=2000,2000*0.5毫秒=1000毫秒,即1秒。这时候,有人会说,为什么不干脆把预分频值PSC设置为71999,即预分频系数为72000,定时器的时钟就是72000000/72000=1000Hz,定时时间就是1毫秒,那么直接把函数的参数ms给了溢出值寄存器ARR就可以了,就不必乘以2了。想法是可以,但是你得知道,定时器都是16位的,所以PSC的值最大到65535,到不了71999。这下你明白了吧?

第三条语句,CR1寄存器bit3写1,由寄存器定义得知,这是把定时器设置为一旦发生溢出,就停止定时器,因为我们做的是延时函数,延时到了以后,就没有必要让定时器再不断递增了,所以要这样设置。

CR1中的OPM位

第四条语句,CR1寄存器bit0写1,打开定时器,定时器计数器开始从0递增。

CR1中的CEN

第五条语句,检测状态寄存器SR中的bit0UIF是否置1,置1的时候,定时值就达到溢出值了,说明定时时间到了。

第六条语句,清除状态寄存器SR中刚才溢出造成的UIF位。

2.使用库函数

下面,我们看看怎么使用库函数实现毫秒延时函数:

void TIM6_Delay_ms(uint16_t ms)
{
 /* 定义一个定时器基本定时初始化结构体变量 */
 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
 
 /* 时钟预分频数为36000,在主频72M时,计数器每500us加1*/
 TIM_TimeBaseInitStruct.TIM_Prescaler= 35999;
 
 /* 自动重装载寄存器值 */
 TIM_TimeBaseInitStruct.TIM_Period=ms*2;
 
 /* 把上面的值配置到寄存器 */
 TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStruct);
 
 /* 设置定时时间到了以后停止定时器计数 */
 TIM_SelectOnePulseMode(TIM6, TIM_OPMode_Single);
 
 /* 清除SR中的UIF标志 */
 TIM_ClearFlag(TIM6, TIM_IT_Update);
 
 /* 打开定时器6 */
 TIM_Cmd(TIM6, ENABLE);
 
 /* 检测定时时间是否到来 */
 while(TIM_GetFlagStatus(TIM6, TIM_IT_Update)==RESET);

 /* 软件清除更新标志 */
 TIM_ClearFlag(TIM6, TIM_IT_Update);
}

你可以细细观察一下上面的库函数,实际上,和直接操作寄存器是一样的。比如说,我们看打开定时器的库函数TIM_Cmd(TIM6,ENABLE),我们打开这个函数,如下所示:

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
 /* Check the parameters */
 assert_param(IS_TIM_ALL_PERIPH(TIMx));
 assert_param(IS_FUNCTIONAL_STATE(NewState));
 
 if (NewState != DISABLE)
 {
 /* Enable the TIM Counter */
 TIMx->CR1 |= TIM_CR1_CEN;
 }
 else
 {
 /* Disable the TIM Counter */
 TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
 }
}


上面两个方式虽能达到普通定时器制作延时函数的目的,但是却不能us级别延时函数,让人很是头疼!所以不得已不用软件延时来实现,代码如下:

void Delay_us( __IO u32 nTime )

    u16 i=0;        

  while(nTime--){
        i=5;
        while(i--) ; 
}
}




猜你喜欢

转载自blog.csdn.net/xiaolongwoaini99/article/details/80202591
今日推荐