微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > STM32系统滴答_及不可不知的延时技巧上

STM32系统滴答_及不可不知的延时技巧上

时间:11-19 来源:互联网 点击:
我想每个单片机爱好者及工程开发设计人员都有过点灯的经历。流水灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。

然在最初入门时,如何让这些小灯们按照我们的想法欢快地跑起来呢,绝大多数小朋友的做法是:在一个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); }

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top