SysTick定时器适用所有的STM32开发板,这节课讲解SysTick定时器产生的 延时函数,STM32开发指南5.1小节有有关SysTick相关的 介绍,在程序中在delay文件夹中,SysTick定时器是内核级别的,这个定时器很简单,主要用来延时和用作实时系统里面的心跳时钟 可以节省单片机资源,SysTick定时器就是系统滴答定时器,是一个24位的倒计数定时器,当他计数到0时就会从RELOD寄存器(重装载寄存器)重新装载计数初值,由此循环。只要 不把他的使能 位清除,他就会永不停息的工作,即使在睡眠模式下也能正常工作。
SysTick定时器它是捆绑在NVIC中,可以产生SysTick异常,SysTick也可以产生中断 ,可以设置中断优先级,可编写中断服务函数
SysTick有四个寄存器,前面3个比较常用,LOAD放重装载值,VAL寄存器一个时钟周期会减一,一直减到0之后又会从这个LOAD寄存器重新加载到VAL寄存器, 这样去循环
1. SysTick控制和状态寄存器 CTRL 下面这个表在Cortex M3权威指南里有。用来配置systick相关的一些使能位和时钟。
位0 (ENABLE)SysTick定时器的使能位,
位1 (TICKINT) 当VAL寄存器计数到0时是否要产生中断
为1 产生中断 为0不产生中断
位2(CLKSOURCE)时钟源,为 0使用外部时钟源(STCLK) 为1 使用内核时钟(FCLK)
对于STM32而言 0.使用外部时钟源是HCLK(AHB总线时钟的1/8)
1.内核时钟是HCLK时钟
这里M3经过 Systeminit()后系统时钟是72M,那么SysTick时钟就是72/8=9M
M4经过 Systeminit()后系统时钟是168M 那么SysTick时钟就是168/8=21M
SysTick定时器的时钟源可以由SysTick_CLKSourceConfig()配置,由这个函数的入口参数确定使用哪一个作为时钟源。这里
对于M3\M4都是一样的,因为它是属于内核级别的,都是属于同一 SysTick定时器。
位16 (COUNTFLAG) 如果SysTick计数到0,该位为1,如果读取该位,该位将自动清零
第二寄存器叫做 重装载数值寄存器-LOAD,是一个24位寄存器 能装2的24次方的数
第三个寄存器是 SysTick当前值寄存器-VAL,一个周期减一
Systick相关库函数
SysTick_CLKSourceConfig(); 是配置CTL寄存器的,用于时钟源的选择
SysTick_Config(uint32_t ticks) 这个函数会开启SysTick 中断, ticks是一秒钟产生中断的次数
这里的中断函数 void SysTick_Handler(void); 对于M3/M4都是一样的
下面看代码 这个 函数有一个入口参数叫 Systick_CLKSource
实际上是配置CTRL寄存器位2(CLKSOURCE), 为0 就是配置为外部时钟 HCLK(AHB总线时钟)的1/8
为1就是配置为内核时钟,就是HCLK
还有一个函数在core_cm4.h/core_cm3.h里面的SysTick_Config(uint32_ticks)这个代码的作用;初始化SysTick相关的寄存器,并且开启SysTick相关的寄存器,入口参数ticks.,也就是说ticks个时钟周期产生一次中断。
ticks 代表 两个systick中断之间的时间长短,如果将 ticks设置为 1000,也是就1000个systick时钟周期,
代码的第一行对这个ticks的值进行一个有效性判断,因为这个ticks的值不能是无限大,因为ticks最终是要写到LOAD寄存器的,从LOAD寄存器被加载到这个VAL寄存器,ticks的值 不能大于
我们可以看到是6个F(就是2的24次方-1),也就是ticks不能大于2的24次方。
将 ticks-1赋值给这个load寄存器(也就是重装载寄存器的值设置为ticks-1),那么他就会加载到这个VAL寄存器当中,当VAL的值计数到0,LOAD寄存器又从新将初值装载到VAL当中。
接下来 是设置优先级,那么设置优先级在NVIC相关的视频会去讲解
接着把这个VAL设置为0, 为什么要设置为0呢?因为当我们这个VAL值为0的时候他会从新去加载计数初值,所以这里初始化的时候把它设置为0,后面如果开启了SysTick定时器,第一次他就会把初值加载到VAL当中。
上图代码就是就是开启这个SysTick定时器,1设置 时钟源 2开启中断 3使能SysTick定时器。
对于Systick库函数,有一个·SysTick_Config(uint32_ticks)利用中断实现的delay函数
上面这段代码作简单讲解
1.首先 在主函数里面他会调用一个SysTick_Config(uint32_ticks)这样的一个函数,入口参数是ticks,这里SysTick的时钟是HCLK是168M,因为要定时1ms,那么通过计算就是:168000000/1000,然后是 上面写的一个delay函数,在这个函数前面定义了一个全局变量,TimerDelay(168M时钟,1s计数168000 000次,那么1ms就计数计数168 000次,所以168M时钟/1000=1ms 延时)
上面的代码中入口参数是200,就等待他为0才能够结束。
函数功能:每1ms产生一次中断,进一次中断TimingDelay就-1,直到TimingDelay为0就延时了200ms。
正点原子的系统文件夹 的delay.c是是使用查询的方式实现延时的,在delay.h里面有三个函数
1,。delay_init(u8 SYSCLK) 这是对SysTick进行初始化,然后是两个 毫秒和微秒的延时。因为正点原子的delay.c里面支持ucos操作系统,所以有一部分代码是通过宏定义来支持的。
上图中 的两个变量在初始化函数当中我们会设置他的值,假如系统时钟是 168M,SysTick的时钟频率是系统时钟的8分频,也就是21M,那么 fac_us 含义就是我们要延时1us要用多少个SysTick时钟周期?
因为系统时钟是21M,所以就是21时钟周期是1us。
那么延时1ms要多少个时钟周期? 就是21000个时钟周期
上图入口参数, 选择HCLK的8分频作为SysTick的时钟频率,那么SysTick的时钟频率就是21M,
这里SYSCLK/8=168/8=21,所以fac_us=21,也就是说1us需要21个时钟周期。
上图这一段代码也是ucos相关的暂时不看。
到最后这个就是设置ms的因子。下面就开始写我们的ms、us的延时函数,有3个函数
delay_us这个函数他就有一个入口函数叫nus。就告诉我们调用这个函数输入100就是100us
怎么实现呢?
也就是让初值LOAD=nms*fac_ms
使能SysTick计数器,让LOAD寄存器的值加载到VAL寄存器,并开始计数,当计数到0时,CTRL寄存器就有一个位16标志位,计数到0就会置1.之后我们就关闭计数器。然后再清空计数器。
注意:延时时间是有一个范围的,怎么来确定这个范围呢?LOAD寄存器是一个24位的寄存器,也就是说他的最大值是2的24次方-1,所以nus*fac_us不能大于79815us这个值
这里将入口参数除以540是为什么呢?,目的是增加延时时间的范围,比如说我们要延时一个任意长度的延时时间,那么我们这里实际上就把他切成了540毫秒一份,比如说你要延时1000ms,他就先延时540ms再延时460ms这样就延时了1000ms。
这个函数和微秒 的配置是一样的。这里不在介绍。