||
SysTick
写在前面:
有好几天没有更新自己的笔记了,今天又来总结一下这几天的学习过程。前几天定下的安排是看一下有关TFT的资料,现在对TFT也有了一些了解,基本能控制其刷屏,显示字符了。我不是一个性格开朗的人,更是一个不太会说话的人,我有时候在质疑我做的事情是否是对的,我很害怕别人笑话我,面对自己一无所有,现在我的希望寄托在嵌入式里面了,我害怕希望越大失望也越大,不过我现在觉得那没什么,我想坚持走单片机这条路,哪怕最后自己就是一个小小的程序员,但我想我在这个电子世界里找到的是一种自我,一种快乐,我不再羡慕那些有快乐的人,因为我的快乐已经融进了汗滴,随秋雨而去。我要大胆继续下去。
在总结TFT的学习之前我想对其它的一些东西进行梳理一下。也许我做的笔记很差劲,也有许多错误,不过没关系,只要是一点一点也打补丁,一点一点地修正,终有一天它会变得稳定。况且我还是一个学生,我的路还有很长,但我愿意选择这条路。
我记得有一句话:可以把一本书读簿也可以打一本书读厚。所以我不觉得麻烦了,对一份资料要真正地把它理解。可能我写的这个日记有点冗长,很多没有必要的东西,不过当一件事简单到了极点就会出现极难解释的东西,就像一天的晚上12点到另一天0点的转换,可能我们只看到这其中时间的变换,不过在这时间点的变换背后又有多少东西在转变呢,这五行是也。在简单的东西中也会发现它的价值。
参考资料:
《Cortex-M3权威指南》
《STM32F10xx固件函数库》
《STM32F10x微控制器参考手册》
有一部分资料转载来自网络。
Ø 首先我们要明白什么是SysTick定时器?
Sys 系统 ,tick 滴答声 ,系统滴答滴答很形象地表示了它是一个系统节拍器。SysTick 是一个24 位的倒计数定时器,当计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息。
Ø 为什么要设置SysTick定时器?
(1)产生操作系统的时钟节拍
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。SysTick的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要这种“滴答”来推动任务和时间的管理。
(2)便于不同处理器之间程序移植。
Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的。
(3)作为一个闹铃测量时间。
SysTick定时器还可以用作闹钟,作为启动一个特定任务的时间依据。它作为一个闹铃,用于测量时间。要注意的是,当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂停运作。
Ø 再来看看SysTick的用法
(1)我们对一个系统编程,老说编程编程什么的,到底我们在编什么程?当然这个问题要探讨起来可能有点远了。我来说说对SysTick的编程,对单片机的编程不过就是对单片机里面的寄存器进行控制,使整个软硬件系统处于一种在你的掌控之下的状态。这就是了嘛,现在我是头,我对我的手下下达一些指令,让它们去做一些事情。所以我们想搞清楚怎样控制SysTick我们还得看我们能对它的哪些部分可以控制。那些部分就是寄存器。
SysTick有4个寄存器 :
寄存器 描述
CTRL SysTick 控制和状态寄存器
LOAD SysTick 重装载值寄存器
VAL SysTick 当前值寄存器
CALIB SysTick 校准值寄存器
对应地在固件函数库中定义了这个东西
typedef struct
{
vu32 CTRL;
vu32 LOAD;
vu32 VAL;
vuc32 CALIB;
} SysTick_TypeDef;
在这背后,已经对定义的寄存器进行了一个地址映射。当我们操控我们定义的寄存器时实际上已是通过那种映射关系操控了芯片内部的值。其实在STM32中对寄存器的操作都是通过这种方式进行的。
具体的映射过程如下,我们可以看一下:
#define SCS_BASE ((u32)0xE000E000)
#define SysTick_BASE (SCS_BASE + 0x0010)
#ifndef DEBUG
...
#ifdef _SysTick
#define SysTick ((SysTick_TypeDef *) SysTick_BASE)
#endif /*_SysTick */
...
#else /* DEBUG */
...
#ifdef _SysTick
EXT SysTick_TypeDef *SysTick;
#endif /*_SysTick */
...
#endif
#ifdef _SysTick
SysTick = (SysTick_TypeDef *) SysTick_BASE;
#endif /*_SysTick */
为了访问SysTick寄存器,, _SysTick必须在文件“stm32f10x_conf.h”中定义如下:
#define _SysTick
映射过程就不作讨论了。总这这样映射的结果是我们能直接使用SysTick。那就来看一下有关寄存器的设置。
(2)SysTick里的寄存器我也简单地把它理解为是一个32位数。
这里有一张图:
在最新的STM32固件库中的core_cm3.c中提供了这样一个函数来供我们配置SysTick,当我们须要用到SysTick时调用它就可以了:
static __INLINE uint32_t SysTick_Config(uint32_t ticks)//ticks 是要重装载的值
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);
/* Reload value impossible 检查重装值是否可用 */
/*
这里引起了我的思考,在很多程序里的第一步都是检查输入值是否可用,也许我应该形成一种条件反射,一看到有输入值就应该判断是否应该对其检查是否可用,这样的好处还是很多的,经过检查我们就不必输入值合法不,不合适时程序会提示我们的
*/
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
/* set reload register */
/*
往LOAD寄存器中写入重装值,前面提到了我们可以直接使用SysTick,这里就可以看出来,我们要使用SysTick结构中的LOAD寄存器,于是我们就直接用到SysTick->LOAD
在core_cm3.c中也可以找到SysTick_LOAD_RELOAD_Msk的值为0xFFFFFFFFul,即unsigned long int 0xFFFFFFFF,请注意这样一句话:SysTick 是一个24 位的倒计数定时器,当计到0 时,将从RELOAD 寄存器中自动重装载定时初值。假设是你单独思考你会想到重装值是多少吗。为什么是ticks要减1,而不是ticks次。我们可以发现它是倒数到0的,也可以理解成从0计数到设定值的,所以它是ticks-1次。
*/
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 */
/*
对CTRL进行配置,在core_cm3.c中有如下定义:
#define SysTick_CTRL_CLKSOURCE_Pos 2
#define SysTick_CTRL_CLKSOURCE_Msk (1ul<<SysTick_CTRL_CLKSOURCE_Pos)
#define SysTick_CTRL_TICKINT_Pos 1
#define SysTick_CTRL_TICKINT_Msk (1ul << SysTick_CTRL_TICKINT_Pos)
#define SysTick_CTRL_ENABLE_Pos 0
#define SysTick_CTRL_ENABLE_Msk (1ul <<SysTick_CTRL_ENABLE_Pos)
即有:SysTick_CTRL_CLKSOURCE_Msk = 0x00000004 clksource使用内部FCLK
SysTick_CTRL_TICKINT_Pos = 0x00000002 响应SysTick中断
SysTick_CTRL_ENABLE_Pos = 0x00000001 使能SysTick
*/
return (0); /* successful */ /* 初始化成功返回值为0 */
}
总结:在配置过程对CTRL//LOAD/VAL三个寄存器进行了配置,初始化了SysTick使用的时钟,清除系统当前值,装入重装值,使能SysTick,使SysTick能响应中断,说了半天其实就这一句话。在主程序中调用SysTick_Configuration( uint32_t ticks ),输入重装值就配置完成了。
(3)SysTick 的中断处理函数在stm32f10x_it.c
函数原型为void SysTick_Handler(void)
{
// user code
}
用户只要把须要处理的程序填入这里就完成啦。
Ø 例子:
正如上面叙述,SysTick的使用为:
(1)配置SysTick
(2)写中断函数
我们产生1ms的廷时:
在我们自己编写的main.c中有:
//前面的省略 ……
Volatile unsigned int TimingDelay ; //定义一个全局变量,用于计数计时值
//中间部分省略……
void Delay_Ms( uint32_t nTime ) //我们须要的廷时函数
{
TimingDelay = nTime ; //把廷时值赋值给TimingDelay;
while( TimingDelay != 0 ); //等待计时时间到,在SysTick的中断函数
//中每1ms对TimingDelay减1
}
int main(void)
{
//配置电源
//配置GPIO
//配置NVIC 等等
while( SysTick_Configuration( 72000 ) != 0 ) ; //配置SysTick,装入倒数值,我
//们假设系统时钟为72MHz,则
//要定时1ms,输入的倒数值为
//72000
while(1)
{
//user code
}
}
在stm32f10x_it.c中:
//前面省略 ……
extern volatile unsigned int TimingDelay;
void SysTick_Handler(void)
{
// user code
TimingDelay--;
}