STM32学习及应用笔记一:SysTick定时器学习及应用

ARM 212浏览


这几年一直使用STM32MCU,对ARM内核的SysTick计时器也经常使用,但几乎没有仔细了解过。最近正好要在移植一个新的操作系统时接触到了这块,据比较深入的了解了一下。

1SysTick究竟是什么?

关于SysTickSTM32的资料中并没有详细的介绍,这可能由于SysTickARM内核的东西。在《STM32F10xxx参考手册》、《STM32F4xx参考手册》以及《STM32F7xx参考手册》中,介绍时钟的时候仅仅是在使用树上简单的画出了HCLK时钟经过8分频后送到了Cortex系统时钟。对这个时钟的描述也非常的简单。在《STM32F10xxx参考手册》中仅仅说:“RCC通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。同样在《STM32F4xx参考手册》和《STM32F7xx参考手册》中,也只是说:“RCC
Cortex
系统定时器 (SysTick)
馈送 8
分频的 AHB
时钟 (HCLK)

另外,STM32在中断部分对SysTick也有一句话的描述。如在《STM32F10xxx参考手册》中的“9.1.1
系统嘀嗒(SysTick)校准值寄存器中提到:系统嘀嗒校准值固定为9000,当系统嘀嗒时钟设定为9MHz(HCLK/8的最大值),产生1ms时间基准。而在《STM32F4xx参考手册》的“10.1.2SysTick
校准值寄存器一节和《STM32F7xx参考手册》“10.1.1SysTick
校准值寄存器一节中也都有:“SysTick
校准值设置为 18750。当
SysTick
时钟设置为 18.75 MHzHCLK/8HCLK
设为150 MHz),会产生
1 ms
时间基准。

仅看这些让我们觉得SysTick似乎都是定好的,但事实上并非如此,因为在库函数中有相关寄存器的操作函数。为了搞清楚这一点,我们必须查看Cortex-M3M4的手册。在《Cortex-M3权威指南》的8
NVIC
与中断控制中有比较详细的描述。首先很明确SysTick 就是一个定时器;其次SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常,主要适用于操作系统的心跳节律。关于SysTick的时钟来源最终还是由芯片厂商决定。

SysTick定时器能产生中断,并且是一个单独的异常类型,并且在向量表中有它的一席之地。并有四个寄存器来控制SysTick,在《Cortex-M3权威指南》中对他们的描述如下:

1)、STK_CSR控制寄存器:寄存器内有4个位具有意义

 

2)、STK_LOAD 重载寄存器

Systick 是一个递减的定时器,当定时器递减至时,重载寄存器中的值就 会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24 位的寄存器最大计数0xFFFFFF

3)、STK_VAL当前值寄存器

     也是个24 位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick 控制及状态寄存器中的COUNTFLAG 标志。

4)、STK_CALRB 校准值寄存器 

2STM32中的SysTick

作为一用于系统级的24位递减计时器,在STM32中又是处理的呢?ST在手册里介绍的比较简单,但在库函数中却有比较清楚的定义。不论是标准库还是HAL库都有较为清楚的定义,如在标准库中就定义SysTick寄存器结构体机器操作函数,misc.C文件中的SysTick_CLKSourceConfig函数,它是一个时钟源配置函数,其定义如下:

/*@功能:配置SysTick时钟源

*@输入参数: 
SysTick_CLKSource: 
指定 SysTick
时钟源.

* 
该参数可以是以下其中一个值:

* @ SysTick_CLKSource_HCLK_Div8: AHB
时钟 8
分频作为SysTick时钟源

* @ SysTick_CLKSource_HCLK: AHB
时钟作为 SysTick
时钟源. */ 

void SysTick_CLKSourceConfig(uint32_t Sy sTick_CLKSource)

{

assert_param(IS_SYSTICK_CLK_SOURCE(SysTi ck_CLKSource));

     
if(SysTick_CLKSource==  SysTick_CLKSource_HCLK)

 
   {

SysTick->CTRL|= SysTi ck_CLKSource_HCLK;

} 

else

{

SysTick->CTRL&= SysTick_CLKSource_HCLK_Div8;

}

}

由此可见,在STM32中时钟可以设置为HCLK或者HCLK8分频,根据使用的需求而定。默认不配置的话时钟就是HCLK8分频。同样在HAL苦衷的定义也是如此。

core_cm3.h文件中有寄存器定义:

typedefstruct

{

__IOuint32_t
CTRL;

__IOuint32_t
LOAD;

__IOuint32_t
VAL;

__Iuint32_t
CALIB;

}SysTick_Type;

还定义了一个SysTick_Config
函数

#if (!defined(__Vendor_Sy sTickConfig )) ||(__Vendor_SysTickConfig 
== 0)

/**

* @功能 
初始化并开启 Sy sTick
计数器及其中断

*@输入参数   
ticks   
两次中断间的 ticks
数值

*@返回值 
1 = 
失败, 0=成功

* 
初始化系统滴答定时器及其中断并开启系统滴答定时器在自由运行模式下以产生周期中断*/

static __INLINE uint32_t SysTick_Config(uint32_t ticks)

{

if(ticks >SysTick_LOAD_RELOAD_Msk) 
return(1);/*
重装值超过了 24
位,是不可能的。返回失败值 0 */

SysTick->LOAD 
=(ticks & SysTick_LOAD_RELOAD_Msk) - 1;/* 
设置重装载寄存器 
*/

NVIC_SetPriority(SysTick_IRQn,( 1<<__NVIC_PRIO_BITS) - 1); 
/*
设置优先级for Cortex-M0系统中断*/

SysTick->VAL 
= 0; /*
装载计数器值(当前计数值清 0 
*/

SysTick->CTRL =SysTick_CTRL_CLKSOURCE_Msk |

SysTick_CTRL_TICKINT_Msk| SysTick_CTRL_ENABLE_Msk;/*
使能 Sy sTick
中断请求和 Sy stick
定时*/

return(0);         
/*
成功,返回 0 */

 }

 #endif

经过以上分析,我们我们需要,可以很方便的在自己的软件中操作SysTick来实现一些功能。

3STM32SysTick应用举例

既然SysTick我们已经清楚了他的原理及操作,也明白了STM32库中如何来操作它,那么我们能用他来做什么呢?首先我们可以在STM32中使用嵌入式操作系统的时候使用它来,并更具实际应用来设置。其实在不考虑操作系统是我们还可以使用SysTick来实现延时计时器。

一个实现的例子如下:

//定义延时计数的变量

__IO uint32_t TimingDelay;

const uint16_t delayAdjustments=12;

 

//函数名:ms延时函数

//
述:参数1即为1ms1000即为1s;只有几us的误差;

void Delayms(__IO uint32_t nTime)

{

 while(SysTick_Config(SystemCoreClock/1000));

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

 
TimingDelay = nTime;

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

 
while(TimingDelay != 0);

 
SysTick->CTRL=0x00; //
关闭计数器

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

}

 

//函数名:us延时函数

//
述:参数1即为1us1000即为1ms;只有几us的误差;

void Delayus(__IO uint32_t nTime)

{

 
while(SysTick_Config(SystemCoreClock/1000000));

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

 
TimingDelay = nTime;

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

 
while(TimingDelay != 0);

 
SysTick->CTRL=0x00; //
关闭计数器

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

}

 

//函数名:延时递减计数函数

//
述:由systick的中断函数调用,实现延时数值的递减计数

void TimingDelay_Decrement(void)

{

 
if (TimingDelay != 0x00)

 
{

   
TimingDelay--;

 
}

}

 

//函数名:延时调整形式的delaynus函数

//
述:参数1即为1us1000即为1ms,根据时钟频率的不同时间不同

void delay_nus(uint16_t n)

{

 
uint16_t j;

 
while(n--)

 
{

   
j=delayAdjustments;//
根据不同时钟频率的指令周期调整数值

   
while(j--);

 
}

}

 

//函数名:延时调整形式的delaynms函数

//
述:参数1即为1ms1000即为1s,根据时钟频率的不同时间不同

void delay_nms(uint16_t n)

{

 
while(n--)

 
{

   
delay_nus(1000);

 
}

}

4、参考文献

我们参考了一些经典的手册以及网络论坛上的一些讨论,无法一一列出,暂列出部分参考文献:

1)、《Cortex-M3权威指南》

2)、《STM32F10x参考手册》

3)、《STM32F4参考手册》

4)、《STM32F7参考手册》

5)、《STM32F10x库函数说明》

6)、《STM32F1
HAL
库说明》

7)、《STM32F4
HAL
库说明》

8)、《STM32F7
Hal
库说明》