您的位置 首页 5G

STM32体系滴答_及不行不知的延时技巧上

我想每个单片机爱好者及工程开发设计人员都有过点灯的经历。流水灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。然在最初入…

我想每个单片机爱好者及工程开发规划人员都有过点灯的阅历。流水灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。

然在开始入门时,怎么让这些小灯们依照咱们的主意欢快地跑起来呢,绝大多数小朋友的做法是:在一个while循环里加上延时程序,让小灯在每个状况下逗留一段时刻,再进入下一个状况,这样小灯们就会在不同的状况中切换,就能够依据咱们规划的程序业绩了。

这样这儿就会涉及到一个延时程序的编写的问题,而一般的做法是一个for循环里去减一个很大的数,直到为0,则延时完结,那个数的值则是依据时钟频率和指令运转周期,预算出来的,还记得较久曾经看过一篇帖子介绍51单片机准确延时的几种办法,有一种办法是在keil中设定好时钟频率,然后经过软件仿真试来算延时时刻,以到达较准确守时。

但这些办法一般都不行便利,延时也不行准确,更高阶一点的办法便是开一个守时器,在守时中止晒干计数到达准确延时的意图。

STM32的使用中,可考虑使用SysTick体系嘀嗒守时器来完结。但在STM32开发手册中对它的介绍却很少,简直到没有的程度。由于它是Cortex内核的部分,CM3为它专门开出一个反常类型,而且在中止向量表中占有一席之地(反常号15),这样它能够很便利的移植到不同厂商出CM3内核的芯片上,而且关于有实时操作体系的软件,它一般会作为整个体系的时基,这个对操作体系非常重要。有关SysTick的详细介绍可参阅《Cortex-M3威望攻略》第133页第八章及第179页第十三章。

SysTick总共有四个寄存器:

1、

对应于软件中SysTick->CTRL;

2、

对应于软件中SysTick-> LOAD;

3、

对应于软件中SysTick-> VAL;

4、

对应于软件中SysTick-> CALIB(),没有用过,也不常用,暂不作介绍。

这几个寄存器的偏移量如下图所示:

寄存器结构体的界说在CMSISCM3CoreSupport core_cm3.h中,如下

/**@addtogroupCMSIS_CM3_SysTickCMSISCM3SysTick memorymappedstructureforSysTick @{ */ typedefstruct { __IOuint32_tCTRL;/*!

SysTick是一个24 位的守时器,即一次最多能够计数 224个时钟脉冲,这个脉冲计数值被保存到SysTick->VAL当时计数值寄存器中,它只能向下计数,每接收到一个时钟脉冲SysTick->VAL的值就向下减1,直至0,然后由硬件主动把重载寄存器SysTick->LOAD中的值到SysTick->VAL从头计数,而且当SysTick->VAL值计数到0时,触发反常,调用void SysTick_Handler(void)函数,能够在此中止服务函数中处理守时中止工作了,一般是对设定值进行递减计数操作。只需不把它在SysTick操控及状况寄存器SysTick->CTRL中的第0位使能位铲除,就永不暂停。

SysTick中止优先级问题这儿需求强调下。

它归于体系反常,是内核级中止,而且优先级是能够设置的,详细设置也是在 core_cm3.h中

/** *@briefInitializeandstarttheSysTickcounteranditsinterrupt. * *@paramticksnumberofticksbetweentwointerrupts *@return1=failed,0=successful * *Initialisethesystemticktimeranditsinterruptandstartthe *systemticktimer/counterinfreerunningmodetogenerate *periodicalinterrupts. */ static__INLINEuint32_tSysTick_Config(uint32_tticks) { if(ticks>SysTick_LOAD_RELOAD_Msk)return(1);/*Reloadvalueimpossible*/  SysTick->LOAD=(ticks&SysTick_LOAD_RELOAD_Msk)-1;/*setreloadregister*/ NVIC_SetPriority(SysTick_IRQn,(1<<__NVIC_PRIO_BITS)-1); SysTick->VAL=0; SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk| SysTick_CTRL_TICKINT_Msk| SysTick_CTRL_ENABLE_Msk; return(0);/*Functionsuccessful*/ }

其间如下这句便是设置优先级的函数,此函数对内核中止优先级和外部中止优先级设置通吃,比较强壮,但需求手动算出来抢占和从优先级,不太便利,当跳进此函数,咱们能够算出Systick默许优先是最低的(作用相当于SCB->SHP[11] = 0xF0;)

NVIC_SetPriority(SysTick_IRQn,(1<<__NVIC_PRIO_BITS)-1);

此刻若其它外部中止优先级设置比它高时,能够掠夺它轿车转向外部中止。

能够做如下试验验证:

先设置一工作中止,把优先级设置高一些,

voidExti_Config(void) { EXTI_InitTypeDefEXTI_InitStructure; NVIC_InitTypeDefNVIC_InitStructure; EXTI_InitStructure.EXTI_Line=EXTI_Line1; EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Event; EXTI_InitStructure.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStructure);  NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); }

注:中止分组我在试验中,开始初始化设置为如下:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

设为第二组。

voidSysTick_Handler(void) { EXTI_GenerateSWInterrupt(EXTI_SWIER_SWIER1); LED_1=ON; Delay(); }

体系滴答中止里触发外部中止工作,并点亮LED1 。

外部中止处理函数如下

voidEXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1)!=RESET) { EXTI_ClearITPendingBit(EXTI_Line1); LED_0=ON; Delay(); } }

此延时函数为堵塞延时如下:

voidDelay(void) { u32i; for(i=0;i<0xFFFFF;i++){} }

参加延时是为了看出来哪个灯先亮。

当外部中止优先级比较高时,它能够抢占Systick中止先碑文,以上代码试验成果为,LED0先点亮后,再回到LED1再点亮。

当把外部中止设置为与systick相同的优先级时,则systick优先级就会相对较高,例如把上面的优先级改为

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;

则会LED1先亮,碑文完SysTick_Handle函数后才轮到EXTI1_IRQHandler碑文。

个人认为,若要完结systick准确延时,最好把systick优先级设置高一些,例如

NVIC_SetPriority(SysTick_IRQn,0);

即把SCB->SHP[11] = 0x00;则可到达systick优先级高于任合外部中止的作用,此刻延时会比较精准。

其他关于SysTick的时钟源的挑选,要注意它的时钟源可挑选内部时钟(FCLK,CM3上的自在运转时钟,STM32中对应是AHB),或许是外部时钟( CM3处理器上的STCLK信号,STM32中对应是AHB/8)

可参阅如下图

它是在SysTick->CTRL第二位CLKSOURCE时钟源挑选中设置。

有关systick延时函数的编写可参阅野火《零死角玩转stm32-初级篇》。

至此咱们能够简略的完结一流水灯程序

while(1) { LED_0=OFF; LED_1=ON; Delay_ms(500); LED_0=OFF; LED_1=ON; Delay_ms(500); }

但是这样做真的好吗?这儿用的是堵塞延时哦,CPU的功率很大一部分就耗在了空转上了,太浪费资源。

假定体系时钟频率为72MHZ或许几十上百MHZ时,当完结一个循环只需求几十或十几纳秒级或许更短,而在这个循环之中堵塞延时个几十至几百毫秒的话,就像是在高速公路上忽然横出一条坑坑洼洼的泥泞路,那可想整条路都会因此而慢下来,甚至会呈现灾难性的结果,个人认为,一般在体系初始化过程中,各芯片的时序对时刻有要求,能够用下堵塞延时,只需求体系启动时运转一下,当体系跑起来之后,最好就别再傻呼呼的这么做了。

这时首要选用的是在守时器里计数,在外部循环中对变量查询,到达某个值时再碑文某个动作,到达延时的作用,而在时刻未届时,体系还能够不断的跑圈圈,做其他工作去。

gticks在守时中止里每毫秒计数一次

while(1) { if(500==gticks) { LED_0=OFF; LED_1=ON; }  if(1000==gticks) { LED_0=OFF; LED_1=ON; gticks=0 } Do_others(); }

以上需求在工作处理过程中对gticks进行处理,增加了代码的耦合度,更简单犯错,如果在一个工作处理中对gticks铲除了,而下个工作中又需求查询它,这样就可能导致处理时序的紊乱,彼此搅扰。

能否在工作处理中只提供查询功用,而守时的工作就交给守时自己去做?

下节高手将上台了,为我们介绍个我曾在一项目中学到的,非堵塞延时的精妙规划。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/yingyong/5g/265828.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部