×
单片机 > 单片机程序设计 > 详情

STM32之SysTick(系统定时器)

发布时间:2020-06-09 发布时间:
|

SysTick定时器是被捆绑在NVIC中的,用于产生SysTick异常(异常号是15)。(同样,玩过51单片机的都知道定时器的作用了)

在STM32在内核部分是包含了一个简单的定时器–SysTick timer。因为在所有的Cortex-M3芯片上都有这个定时器,所以软件在不同芯片生产厂商的Cortex-M3器件间的一只工作就得以化简。 
该定时器的时钟源可以是内部时钟( FCLK, CM3 上的自由运行时钟),或者是外部时钟(CM3 处理器上的 STCLK 信号)。不过, STCLK 的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可 
能会大不相同。因此,需要阅读芯片的使用手册来确定选择什么作为时钟源。 
*(在 STM32 中 SysTick 以 HCLK(AHB 时钟)或 HCLK/8 作为运行时钟。) 
SysTick定时器能产生中断,Cortex-M3专门为它开出一个异常类型,并且在向量表中存在。所以他能使操作系统和其他系统软件在Cortex-M3器件间的移植变的更简单了,因为在所有Cortex-M3产品之间,SysTick的处理方式都是相同的。

SysTick timer工作分析:

SysTick 是一个 24 位的定时器,即一次最多可以计数 224 个时钟脉冲,这个脉冲计数值被保存到 当前计数值寄存器 STK_VAL (SysTick current valueregister) 中,只能向下计数,每接收到一个时钟脉冲 STK_VAL 的值就向下减1,直至 0,当 STK_VAL 的值被减至 0 时,由硬件自动把重载寄存器 
STK_LOAD( SysTick reload value register) 中保存的数据加载到 STK_VAL,重新向下计数。当 STK_VAL 的值被计数至 0 时,触发异常,就可以在中断服务函数中处理定时事件了。 
(要使 SysTick 进行工作必须要进行SysTick配置。它的控制配置很简单,只有三个控制位和一个标志位,都位于寄存器 STK_CTRL) 

Bit0: ENABLE: 
为 SysTick timer 的使能位,此位为 1 的时候使能 SysTick timer,此位为 0的时候关闭 SysTick timer。

Bit1: TICKINT: 
为异常触发使能位,此位为 1 的时候并且 STK_VAL 计数至 0 时会触发SysTick 异常,此位被配置为 0 的时候不触发异常

Bit2: CLKSOURCE: 
为 SysTick 的时钟选择位,此位为 1 的时候 SysTick 的时钟为 AHB 时钟,此位为 0 的时候 SysTick 时钟为 AHB/8( AHB 的八分频)。

Bit16: COUNTFLAG: 
为计数为 0 标志位,若 STK_VAL 计数至 0,此标志位会被置 1。 
(STK_CALIB 寄存器是用于校准的,不常用。可以在STM32数据手册中找到所有SysTick相关寄存器)

实例分析SysTick(延时流水灯)

主函数功能: 
int main() 

LED_GPIO_Config(); //LED GPIO初始化

SysTick_Init();    //配置SysTick




while(1)           //一个死循环的流水灯

{

    LED1(0);

    Delay_us(50000);

    LED1(1);


    LED2(0);

    Delay_us(50000);

    LED2(1);


    LED3(0);

    Delay_us(50000);

    LED3(1);

}


return 0;


}


SysTick_Init()分析


void SysTick_Init(void) 

{


/* SystemFrequency / 1000 1ms 中断一次 

SystemFrequency / 100000 10us 中断一次 

SystemFrequency / 1000000 1us 中断一次 */


// if (SysTick_Config(SystemFrequency / 100000)) // ST3.0.0 库版本 

if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5.0 库版本 

while (1); 

}


SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;//关闭定时器


其实函数里面就调用了一个函数SysTick_Config(),这个函数是属于内核层的Cortex-M3的通用函数,位于Core_cm3.h中。函数工作很简单,就是若调用SysTick_Config()不成功,则进入死循环,当初始化SysTick成功后,先关闭定时器,需要用的时候再开启。(我们可以在keil环境下跟踪这个函数查看函数的定义) 

/* 

* @brief Initialize and start the SysTick counter and its interrupt. 

* @param ticks number of ticks between two interrupts 

* @return 1 = failed, 0 = successful 

* Initialise the system tick timer and its interrupt and start the 

* system tick timer / counter in free running mode to generate 

* periodical interrupts. 

*/ 

static __INLINE uint32_t SysTick_Config(uint32_t ticks) 

/* Reload value impossible */ 

if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); // 

/* set reload register */ 

SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 

/* set Priority for Cortex-M0 System Interrupts */ 

NVIC_SetPriority (SysTick_IRQn, (1<<__nvic_prio_bits>

/* Load the SysTick Counter Value */ 

SysTick->VAL = 0;


SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | 

SysTick_CTRL_TICKINT_Msk | 

SysTick_CTRL_ENABLE_Msk; /* Enab 

le SysTick IRQ and SysTick Timer */ 

return (0); /* Func 

tion successful */ 

}


这个函数的主要功能:启动了SysTimer,并且把它配置为计数至0引起中断,输入的参数ticks为两次中断之间的脉冲数(既相隔ticks个时钟周期会引起一次中断),配置SysTick成功时返回0,出错返回1


以下为详细分析: 

if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); 

因为ticks这个参数是脉冲计数值,要被保存到重载寄存器 STK_LOAD 寄存器中,再由硬件把STK_LOAD值加载到当前计数值寄存器STK_VAL使用的,因为STK_LOAD和STK_VAL都是24位,所以在输入ticks大于可存储的最大值时,由这行代码检查出错误返回。


SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 

当ticks的值没有错误后,就把ticks-1赋值到STK_LOAD寄存器中(注意是减1),当STK_VAL从ticks-1向下计数到0,则就经过了ticks个脉冲。 

(这里面用到的SysTick_LOAD_RELOAD_Msk宏(在keil环境下跟踪查看定义),这个宏是用来指示寄存器的“特定位置”或者是用来“位屏蔽”,所以叫位指示宏或位屏蔽宏,这个在操作寄存器代码中经常用到,通常都是用作控制寄存器某些位(“或”和“与”操作))


NVIC_SetPriority (SysTick_IRQn, (1<<__nvic_prio_bits>

调用了NVIC_SetPriority()函数配置了SysTick中断,所以我们就不需要再去手动配置。配置完后把STK_VAL寄存器重新赋值为0。


SysTick->CTRL配置 

向STK_CTRL写入SysTick timer的控制参数,配置为时钟,使能为计数至0引起中断,使能SysTick,接着SysTick就开始运行,做脉冲计数。 

(如果想使用AHB/8作为时钟,可以调用库函数SysTick_CLKSourceConfig()或者修改SysTick->CTRL的使能配置)


最后在调用完SysTick_Config()后,SysTick定时器被打开了,但是我们并不是需要这样,所以在调用完之后关闭定时器 

// 使能定时器 

SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; 

// 关闭定时器 

SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk; 

(这里又用到位屏蔽宏,可见用的是十分广泛)


定时时间的计算: 

再调用完SysTick_Config()时,输入ticks为SystemCoreClock / 100000 SystemCoreClock 为定义了系统时钟(SYSCLK)频率的宏,即等于AHB的时钟频率,历程用到AHB都是72MHz,所以SystemCoreClock这个宏展开就是7200 0000. 

根据前面对 SysTick_Config()函数的介绍,它的输入参数为SysTick将要计的脉冲数,经过 ticks 个脉冲(经过 ticks 个时钟周期)后将触发中断,触发中断后又重新开始计数。


由此我们可以算出定时的时间,下面为计算公式: 

T=ticks*(1/f) 

T 为要定时的总时间。 

ticks 为 SysTick_Config()的输入参数。 

1/ f 即为 SysTick timer 使用的时钟源的时钟周期,f为该时钟源的时钟频率,当时钟源确定后为常数。 

例如本实例使用时钟源为 AHB 时钟,其频率被配置为 72MHz。调用函数时,把ticks赋值为 ticks=SystemFrequency / 10000 =720,表示720个时钟周期中断一次;( 1/f)是时钟周期的时间,此时(1/f =1/72us ),所以最终定时总时间 T=720*(1/72),为 720 个时钟周期,正好是 10us。 

SysTick 定时器的定时时间(配置为触发中断,即为中断周期),由ticks参数决定,最大定时周期不能超过 224个。以下是几种常用的中断周期配置,就是根据上面的公式计算出来的。


SystemFrequency / 1000 1ms 中断一次

SystemFrequency / 100000 10us 中断一次

SystemFrequency / 1000000 1us 中断一次 


中断服务函数: 

在main中,我们让LED工作在无限循环中,在开关之间调用了Delay_us(),一旦调用了之后,SysTick就会被开启,按照设定好的定时周期递减计数,当SysTick的计数值减到0时,就进入到中断,中断执行完后又重新计数


void Delay_us(__IO u32 nTime) 

TimingDelay = nTime;


SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //使能定时器

while(TimingDelay != 0);


使能了SysTick之后,就使用 while(TimingDelay != 0)语句等待TimingDelay变量变为 0,这个变量是在中断服务函数中被修改的。


/* 

* @brief This function handles SysTick Handler. 

* @param None 

* @retval : None 

*/ 

void SysTick_Handler(void) 

if (TimingDelay != 0x00) 

TimingDelay–; 

}


SysTick中断属于系统异常向量,在stm32f10x_it.c文件中已经默认有了它的中断服务函数SysTick_Handler(),但内容为空。所以我们要自己编写。 

每次进入 SysTick 中断就把全局变量TimingDelay自减一次。用户函数 Delay_us ()在TimingDelay被减至等于 0 时,才退出延时循环,即我们对 TimingDelay 赋的值为要中断的次数。


所以总的延时时间 T延时= T 中断周期 * TimingDelay 。


ps: 

定时器我们还可以用作计算程序执行时间: 

当我们开启 SysTick 定时器后,定时器开始工作,我们可以定义一个变量a来对中断次数进行记录,在定时器进入中断时,这个变量就a++,当我们关闭定时器后,将变量的数值乘与定时器的中断周期 就等于测量时间。特别是涉及到算法的程序,这对于优化算法是有非常大的帮助。假如你的算法的是 us 级别的,那么 SysTick 就应该设定为us 级中断,如果是 ms 级别的,就将 SysTick 设定为 ms 级中断。


关键字:STM32  SysTick  系统定时器 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机的函数发生器的设计