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.直接操作寄存器
下面,咱们先用直接操作寄存器的方法,写一个毫秒延时函数:
voiddelay_ms(uint16_tms){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寄存器bit0写1,翻开守时器,守时器计数器开端从0递加。
第五条句子,检测状况寄存器SR中的bit0UIF是否置1,置1的时分,守时值就到达溢出值了,阐明守时时刻到了。
第六条句子,铲除状况寄存器SR中方才溢出形成的UIF位。
2.运用库函数
下面,咱们看看怎样运用库函数完成毫秒延时函数:
voidTIM6_Delay_ms(uint16_tms){/*界说一个守时器根本守时初始化结构体变量*/TIM_TimeBaseInitTypeDefTIM_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),咱们翻开这个函数,如下所示:
voidTIM_Cmd(TIM_TypeDef*TIMx,FunctionalStateNewState){/*Checktheparameters*/assert_param(IS_TIM_ALL_PERIPH(TIMx));assert_param(IS_FUNCTIONAL_STATE(NewState));if(NewState!=DISABLE){/*EnabletheTIMCounter*/TIMx->CR1|=TIM_CR1_CEN;}else{/*DisabletheTIMCounter*/TIMx->CR1&=(uint16_t)(~((uint16_t)TIM_CR1_CEN));}}
把参数TIM6和ENABLE带进去,你会发现,实际上,便是咱们直接操作寄存器的TIM6->CR1|=0X1。
其它的库函数,你能够自己翻开研究一下。
别的,库函数和咱们直接操作寄存器有个不同的当地,便是TIM_TimeBaseInit()这个函数中的最终一条句子,它给TIMx->EGR寄存器的bit0写了1,使得SR中的bit0UIF置1,所以在履行完TIM_TimeBaseInit()这个函数之后,咱们后边用TIM_ClearFlag(TIM6, TIM_IT_Update);函数把UIF清0,要不然的话,你在后边会直接检测到UIF置1,可是,这个置1不是溢出发生的,而是方才给EGR写1发生的,就达不到延时的作用了。
总结,在运用以上两个延时函数的时分,记住先答应TIM6外设,即给APB1ENR寄存器的bit4写1。或许直接用库函数RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6 , ENABLE)。从上面库函数和直接操作寄存器的延时函数,你能够看出,运用库函数,即便不必注释,也能够看出来你是在干嘛,而直接操作寄存器,就看不出来了。在实际运用中,没必要纠结库函数和直接操作寄存器的挑选,我就喜爱一会用库函数,一瞬间直接操作寄存器,你想咋地?不过,公司的老板肯定是期望运用库函数,由于假如你辞去职务了,顶替你的人看到一堆操作寄存器的代码,让他情何以堪?