历史上的今天
返回首页

历史上的今天

今天是:2025年03月13日(星期四)

正在发生

2019年03月13日 | 【STM32】SysTick滴答定时器(delay延时函数讲解)

2019-03-13 来源:eefocus

STM32F1xx官方资料:

《Cortex-M3权威指南-中文》-第8章最后一个小节:Systick定时器


SysTick定时器

Systick定时器,是一个简单的定时器,对于CM3、CM4内核芯片,都有Systick定时器。Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。


Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。


SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。Systick中断的优先级也可以设置。


实际上,Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断,称之为滴答中断操作系统进行运转的时候,也会有时间节拍。它会根据节拍来工作,把整个时间段分成很多小小的时间片,而每个任务每次只能运行一个时间片的时间长度,超时就退出给别的任务运行,这样可以确保任何一个任务都不会霸占操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统的节拍。只要不把它在SysTick控制及状态寄存器中的使能位清除,就一直执行。 


SysTick相关寄存器

SysTick有四个寄存器,分别为CTRL(控制与状态寄存器)、LOAD(自动重装载值寄存器)、VAL(当前值寄存器)、CALIB(校准值寄存器)。


在MDK的core_m3.h文件中定义了一个结构体SysTick_Type,里面也包括了这四个寄存器。


typedef struct

{

  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register */

  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       */

  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      */

  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        */

} SysTick_Type;

它们的各位描述如下面的表格所述:


CTRL寄存器各位描述

位段 名称 类型 复位值 描述

16 COUNTFLAG R 0

如果在上次读取本寄存器后,SysTick已经数到了0,是该位为1.如果读


取该位,该位自动清零。


2 CLKSOURCE R/W 0

0 外部时钟源(STCLK)


1 内核时钟(FCLK)


1 TICKINT R/W 0

1 SysTick倒数到0时产生SysTick异常请求


0 数到0时无动作


0 ENABLE R/W 0 SysTick定时器的使能位

对于STM32,外部时钟源(STCLK)是HCLK(AHB总线时钟)的1/8,内核时钟(FCLK)是HCLK(AHB总线时钟)。


LOAD寄存器各位描述

位段 名称 类型 复位值 描述

23:0 RELOAD R/W 0 当倒数至0是,将被重新装载的值

VAL寄存器各位描述

位段 名称 类型 复位值 描述

23:0 CORRENT R/Wc 0

读取时返回当前倒计数的值,写它则使之清零,同时还会清


除在CTRL寄存器的COUNTFLAG位


SysTick相关库函数

SysTick_CLKSourceConfig(),这是Systick的时钟源选择,直接配置CTRL寄存器的值。假设HCLK为72MHz,选用外部时钟源,那么SysTick的时钟即9MHz。这就意味着,SysTick的计数器VAL每减1代表时间过去了1/9us。


具体的定义在misc.c文件中。


void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)

{

  /* Check the parameters */

  assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));

  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)

  {

    SysTick->CTRL |= SysTick_CLKSource_HCLK;

  }

  else

  {

    SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;

  }

}

SysTick_Config(uint32_t ticks) ,这是SysTick的初始化函数,时钟为HCLK,并开启SysTick中断。其中函数的参数表示两次中断之间时间间隔期间的SysTick周期,即两次中断之间有多少个SysTick周期。


具体的定义在core_cm3.h文件中。


static __INLINE uint32_t SysTick_Config(uint32_t ticks)

  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */

                                                               

  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */

  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */

  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */

  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 

                   SysTick_CTRL_TICKINT_Msk   | 

                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */

  return (0);                                                  /* Function successful */

}

void SysTick_Handler(void),这是SysTick的中断服务函数。我们举一个例子,利用中断的方式实现delay延时函数,见下面的程序:


static __IO uint32_t TimingDelay;

void Delay(__IO uint32_t nTime)

   TimingDelay = nTime;

   while(TimingDelay != 0);

}

void SysTick_Handler(void)

{

    if (TimingDelay != 0x00) 

     { 

       TimingDelay--;

     }

}

 int main(void)

 {  …

    if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms

     {

     while (1);

     }

    while(1)

     { Delay(200);//200ms

     … 

     }

}

 


Delay延时函数讲解

之前利用中断实现延时函数,但是一直使用中断会造成资源的浪费,不建议这样实现,我们利用查询的方式实现delay延时。下面主要介绍Delay延时函数的实现:


delay_init()

首先是delay_init(),延时初始化函数。利用Syst_CLKSourceConfig()函数选择SysTick时钟源,选择外部时钟(HCLK的1/8);同时初始化fac_us和fac_ms两个变量。


void delay_init()

{

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8

fac_us=SystemCoreClock/8000000;       //为系统时钟的1/8,实际上也就是在计算1usSysTick的VAL减的数目

fac_ms=(u16)fac_us*1000; //代表每个ms需要的systick时钟数,即每毫秒SysTick的VAL减的数目   

}

delay_ms()

其次,delay_ms(),此函数用来延时指定的ms。


此时要注意nms的范围,SysTick->LOAD为24位寄存器,所以最大延时为:nms<=0xffffff*8*1000/SYSCLK;SYSCLK单位为Hz,nms单位为ms。对72M条件下,nms<=1864。如果超出这个值,建议多次调用此函数来实现。


void delay_ms(u16 nms)

{     

u32 temp;    

SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)

SysTick->VAL =0x00;           //清空计数器

SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数  

do

{

temp=SysTick->CTRL;

}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,看CTRL的第16位(COUNTFLAG)是否为1,看STRL的第0位(ENABLE)是否为1   

SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器

SysTick->VAL =0X00;       //清空计数器       

这段代码其实就是先把延时的时间换算成SysTick的时钟周期数,然后写入LOAD寄存器。然后清空当前寄存器VAL的内容,再开启倒数功能。等倒数结束即延时了nms、最后关闭SysTick,清空VAL的值,实现一次延时的操作。


这里特别说一下,temp&0x01,这一句用来判断SysTick定时器是否还处在开启的状态,可以防止SysTick被意外关闭导致的死循环。


delay_us()

最后,delay_us(),此函数用来延时指定的us。具体的逻辑和上面一个函数类似,就不介绍了。


void delay_us(u32 nus)

{

u32 temp;      

SysTick->LOAD=nus*fac_us; //时间加载    

SysTick->VAL=0x00;        //清空计数器

SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数   

do

{

temp=SysTick->CTRL;

}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达   

SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器

SysTick->VAL =0X00;       //清空计数器  

}

delay延时的相关函数在SYSTEM文件夹下的delay子文件夹,在使用delay_ms()或者delay_us()函数之前一定不要忘记先初始化delay_init()。


推荐阅读

史海拾趣

DBM REFLEX公司的发展小趣事

DBM REFLEX深知品质是企业的生命线。因此,公司建立了严格的品质管理体系,从原材料采购到生产过程的每一个环节都进行严格的把控。公司还引进了先进的检测设备和技术,确保每一件产品都符合高品质的标准。这种对品质的执着追求,使DBM REFLEX的产品在市场上赢得了客户的信赖和认可。

Axiohm公司的发展小趣事

Axiohm公司最初是一家小型电子元件制造商,面对激烈的市场竞争,公司创始人李先生意识到唯有技术创新才能脱颖而出。他带领团队投入大量研发资源,成功开发出一种具有高性价比的新型半导体材料。这一创新不仅降低了生产成本,还提高了产品的性能稳定性,迅速赢得了市场的青睐。随着销量的不断增长,Axiohm逐渐在电子行业中崭露头角。

EIC [EIC discrete Semiconductors]公司的发展小趣事

在国内市场取得一定成绩后,EIC公司开始将目光投向国际市场。公司积极参加国际电子展会,与国际知名企业进行技术交流与合作,不断拓展海外市场。通过国际化战略的实施,EIC的产品逐渐进入了欧洲、北美等发达国家和地区,并在当地建立了完善的销售和服务网络。这一过程中,EIC不仅提升了品牌影响力,也积累了丰富的国际化运营经验。

广东爱晟电子(exsense)公司的发展小趣事

为了进一步扩大市场份额,爱晟电子制定了国际化发展战略。公司积极参加国际电子展会和论坛,与国际同行进行交流合作。同时,公司还加大了对海外市场的拓展力度,产品出口到欧美、东南亚等多个国家和地区。这些举措使得爱晟电子在国际市场上获得了更多的认可和机会。

台湾唯圣(GW)公司的发展小趣事
提供稳定、可调的电流输出。
亿佰特(EBYTE)公司的发展小趣事

亿佰特在技术创新的基础上,积极拓展市场。公司凭借优质的产品和服务,成功打开了国内外市场的大门。产品远销美国、加拿大、德国等50多个国家和地区,覆盖了物联网、消费电子、工控医疗等多个行业。亿佰特通过深入了解市场需求,不断优化产品结构和市场策略,实现了市场的快速扩张。

问答坊 | AI 解惑

绝对完整的 Usb ISP 的下载线制做过程和资料!!!!

经过一下午的摆弄,我的下载器终于可以为我所用了,兴奋之余将整个过程记录下来做个范例提供给那些想做但又因制做过程不清晰不敢下手的AVR友人们。使大家都能用上这个廉价又方便的下载器。 在做之前希望各位将下载的文件中的PDF通读一遍以免有些 ...…

查看全部问答>

怎么下载DDS IP核啊?希望各位帮帮忙啊!

最近在弄毕业设计,要做一个DDS,要求用IP核做,我用的是QII,但是我在网上找不到DDS的IP核,希望哪位大哥帮帮忙,如果有ISE的IP的话也可以,谢谢啦1…

查看全部问答>

看看人家做的调频发射机

用双面板做的,采用单片机+锁相环控制+C2053功率管输出,频率非常稳定。 可以用按键调节发射频率,电子音量等 频率设置步进0.1MHZ 或1MHZ可以工作在88-108MHZ 调频段,升级软件可以在70-120MHZ工作 输出能驱动小灯泡发光,功率最大1W 该 ...…

查看全部问答>

Quartus2的FPGACPLD设计

为什么不能正常下载?…

查看全部问答>

无法进入系统和BIOS

开机之后,我无法进入系统和BIOS,一直停留在最开始的界面。按DEL无反应。 我最开始出现这种情况,是我把自家的显卡取下来给邻居家的电脑装上,第二次出现这种情况,是我不小心动了网线,然后系统就一直显示,本地连接首先,本地无连接。然后我就 ...…

查看全部问答>

CL6017S收音机芯片的跳台问题

-------------------------------------------------------------------------------- 我用的收音机芯片是CL6017S,它的01h制度寄存器的低10位显示的是频率,它的值和04h低10位的值相等,频率单元是10KHz,初始化后的电台频道是0x15E;我的家台程 ...…

查看全部问答>

Windows Mobile 安装包用VS.NET2005 C#怎么制作?

Windows Mobile 安装包用VS.NET2005 C#怎么制作? 我试了好多次了。还是有些问题。各位帮一下忙。多谢了。…

查看全部问答>

protel dxp指导教程pdf

 protel dxp指导教程pdf…

查看全部问答>

CCS2.2编译出现了错误,不知怎么解决

CCS2.2出现如附件中的提示, 我卸载掉CCS2.2然后重新安装,还是出现相同的编译错误提示, Buffer overrun detected! Program: c:\\\\ti\\\\cc\\\\bin\\\\cvrlh40.exe A buffer overrun has been detected which has vorrupted the program\\\'s ...…

查看全部问答>