前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SysTick定时器

SysTick定时器

作者头像
飞哥
发布2020-07-10 10:21:43
1K0
发布2020-07-10 10:21:43
举报

今天讲解的是stm32的系统定时器——SysTick定时器。

一、SysTick定时器简介

《Cortex-M3权威指南》中对SysTick的描述,SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。它是一个24位的递减定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。大多数操作系统需要一个硬件定时器来产生滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有提供各种定时功能,都与滴答定时器有关。因此,需要一个定时器产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统的“心跳”的节奏。该定时器的时钟源可以是内部时钟(FCLK),或者是外部时钟(CM3处理器上的STCLK信号)。SysTick定时器能产生中断,异常中断。使用内核的SysTick定时器来实现延时,可以不占用系统定时器,由于和MCU外设无关,所以代码的移植,在不同厂家的Cortex-M内核MCU之间,可以很方便的实现。

二、SysTick相关寄存器介绍

SysTick一共有4个寄存器,在core_cm3.h这个头文件里定义了以这4个寄存器为成员的结构体指针。

代码语言:javascript
复制
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;

各个寄存器功能如下

1、SysTick控制及状态寄存器

2、SysTick重装载数值寄存器

3、SysTick当前数值寄存器

4、SysTick校准数值寄存器

通常只需要用到前3个寄存器,第四个寄存器在定时实验中不需要用到,有关各个位的描述在英文文档里比较晦涩难懂,这个寄存器的用途有待研究。

了解了它的定时器作用之后,接下来是如何通过编程得到准确的延时。

三、SysTick定时器配置步骤

SysTick定时器的操作可以分为 4 步:

(1)设置SysTick定时器的时钟源。

(2)设置SysTick定时器的重装初始值(如果要使用中断的话,就将中断使能打开)。

(3)清零SysTick定时器当前计数器的值。

(4)打开SysTick定时器。

代码实现

代码语言:javascript
复制
void SysTick_Init(u8 SYSCLK)
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //9M
    fac_us=SYSCLK/8;                    
    fac_ms=(u16)fac_us*1000;                   
}                
代码语言:javascript
复制
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;                          //清空计数器     
}            
代码语言:javascript
复制
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)));       //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭计数器
    SysTick->VAL =0X00;                         //清空计数器         
}     

上面有几个地方需要注意

①SysTick的时钟来源可以是AHB时钟或者是AHB时钟除以8。具体可以参照stm32固件库说明。

②在访问结构体成员时,(.)和(->)的区别。访问结构成员的运算符有两种,一种是结构成员运算符“·”,也称为“圆点运算符”,另一种是结构指针运算符“->”,也称“箭头运算符”。

点是用于结构体变量访问成员,箭头是用于结构体指针访问成员。

补充说明:圆点运算符是比较古老的写法,不能访问结构体指针变量成员,现在都推荐使用箭头运算符,即(->)。

③计数值的计算

计数值=计数总时间/每次计数所需时间。

每次计数所需时间=1/SYSCLK。

所以

计数值=计数总时间*SYSCLK。

同时还要注意单位的统一性。比如假设需要计时的时间为500us,定时器时钟源为系统时钟72MHz,那么计数值为500*72。

当定时单位为ms时,与kHz相乘才能将单位消掉,因此,假设定时500ms,定时器时钟源为9MHz,那么计数值为500*9*1000。

这就是为什么上面定义fac_ms=fac_us*1000的原因。

编写好上面两个延时函数之后就可以方便的调用了。调用时要注意不能超过它的最大值。

比如调用void delay_us(u32 nus),假设时钟源选用72MHz,

那么nus的最大值为0xFFFFFF/72=233016us。

比如调用void delay_ms(u32 nms),假设时钟源选用9MHz,

那么mus的最大值为0xFFFFFF/9000=1864ms。

总结:

SysTick定时器主要是要知道如何通过它得到准确定时,并且编写延时函数,另外也可以在定时结束时产生的中断里面编写中断响应函数。配置上也比较简单,基本上就是配置时钟来源,装载计数值。

另外,在一些编译器里,有时不能调用库函数SysTick_SetReload(),大概是因为官方库函数在不断更新,需要下载更高版本的库函数。在上面的程序中,直接操作了结构体,所以没有这个问题。

有了精确延时函数,那么使用通用GPIO软件模拟一些通信协议,如IIC、SPI等串行协议,就可以驱动很多硬件设备了,如EEPROM、温湿度传感器、显示屏等等。

*部分资料来源于网络,如有侵权请联系删除

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 电子技术研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档