新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > LPC1114系统定时器(SysTick)

LPC1114系统定时器(SysTick)

作者: 时间:2016-11-13 来源:网络 收藏
LPC1114内部有一个特殊的定时器——系统定时器SysTick),它位于Cortex-M0内核里面,是ARM内核的一部分,主要用来给操作系统提供时间片轮转的定时,一般固定为10ms的定时,所以中文也称它为“嘀哒”定时器。在不跑操作系统时,可以把它当作普通定时器来用,一般用来进行程序延时。在前面的第一个演示示例中就用到过,下面就来讨论一下如何运用SysTick来提供延时。

系统定时器也位于“私有外设总线”(Private peripheral bus)内,其地址为0xE000E010~0xE000E01F。下面先来看一下SysTick的内部结构,如下图所示。

本文引用地址:https://www.eepw.com.cn/article/201611/316325.htm

从上图中可以看出,SysTick定时器的位长度是24位,即最长的计数次数为16777216次,且计数为倒数计数形式,递减到0时产生中断请求。计数的脉冲可直接取系统时钟,也可取半系统时钟。下表给出了和SysTick相关的寄存器。

上表中,SYST_CSR是系统定时器的控制寄存器,负责SysTick的启动、中断使能、输入时钟选择、溢出标志读取等操作;SYST_RVR是系统定时器的初始值重载寄存器,负责SysTick的24位初值载入;SYST_CVR是系统定时器的当前值寄存器,负责获取SysTick的24位当前计数值,当对该寄存器进行写操作时,该寄存器的数值将会被清零;SYST_CALIB是系统定时器的校准值寄存器,负责SysTick的校准。
下面给出的是上表中控制寄存器SYST_CSR的全部位结构。
从表中可以看出,第0位是使能SysTick,值为1启动计数,值为0关闭计数;第1位是SysTick的中断使能位,值为1使能中断,值为0禁能中断;第2位是输入时钟的选择位,值为1时选择系统时钟做为计数脉冲,值为0时选择半系统时钟做为计数脉冲的参考时钟;第16位是溢出标志位,当计数的值递减到0时,该位被置1,在读取该置后自动清零。
从上述可以看出,其实系统定时器就是一个普通的定时器,在使用时顺序进行如下操作即可:
1.给SYST_RVR寄存器写入初始值;
2.写SYST_CVR寄存器以对计数值进行清零;
3.设置SYST_CSR寄存器启动定时、使能中断以及选择时钟源。
下面给出系统时钟SysTick的结构体定义。
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;

SysTick定时器组的基址为0xE000E000,所以要将基址指针强制转换为上述结构体,还要加上下面的定义。

#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */
#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */
对于系统定时器SysTick产生的中断,也有特定的入口函数,形式如下所示。
void SysTick_Handler(void)

{

系统定时中断服务程序部分

}

接下来讨论一下系统定时器的初始值设置,因为定时的时长由系统时钟频率、系统时钟选择和载入的初始值共同决定。假设系统时钟为48MHz,默认情况下SysTick的CLKSOURCE值为0,即选择半系统时钟频率。这样,输入给定时器计数的时钟就是48/2=24MHz,计数周期为1/(24MHz)=1/24us,则计24次就是1us的定时,但实际上SysTick采用的是倒数计数方式,即从最大值依次递减计数直到0产生溢出信号。所以24次计数实际上是0~23次,即最大值要减1,即24-1=23。同时要注意,由于计数位宽是24位,所以最大计数值不能超过2的24次方(即16777216),由此,可得出微秒级的初始值计算公式,如下:

LOAD=((24*us)-1),其中us取值范围1~699000

同理可得出毫秒级的初始值计算公式:

LOAD=((24000*ms)-1),其中ms取值范围1~699

上升到一般情况,定时初始值可用下面的公式来计算:

上式中,系统时钟SysClk的单位是MHz,CLKSOURCE的值是0或1,得到的是微秒级别的定时,要注意LOAD的值不能大于16777216。

下面来看一下前面第一个演示示例中的延时部分,代码如下:

static volatile uint32_t TimeTick = 0; //设置全局变量

void SysTick_Handler(void)

{

TimeTick++; //系统定时中断中,全局变量加1

}

void delay_ms(uint32_t ms)

{

SysTick->LOAD = (((24000)*ms)-1); //载入初始值

SysTick->VAL = 0;//写当前值寄存器使其清零

SysTick->CTRL |= ((1<<1)|(1<<0)); //打开中断,启动定时器,选择关系统时钟

while(!TimeTick);

TimeTick = 0; //当定时时间到时,全局变量清零

SysTick->CTRL =0;//关闭定时器

}

可见上述定时是毫秒级别的,最长可定时699ms。

至此,第一个演示示例中的全部内容就都讨论完了,可见在LPC1114中要实现一个简单的十二个LED交替闪烁,涉及到的内容还是很多的。下面再来看一个流水灯的例子,要求实现一个12位的流水灯,时间间隔为100ms。假设LED采用共阳接法接在GPIO2端口,参考代码如下:

#include
//===================系统定时器中断服务程序============================
void SysTick_Handler(void)
{
uint32_t temp;//定义临时中间变量
temp = LPC_GPIO2->DATA;//读取当前端口2的值
temp = ~temp; //把中间变量进行取反
temp<<=1; //把中间变量进行左移一位
temp = ~temp; //再次把中间变量进行取反
LPC_GPIO2->DATA = temp; //把左移后的量赋给端口2
if(temp==0xFFF) //如果左移到头,则从头开始
{
LPC_GPIO2->DATA = 0xFFE;
}
}
//========================系统定时器初始化=============================
void SysTick_init(void)
{
SysTick->LOAD = (((24000)*100)-1);//设置100ms的定时
SysTick->VAL = 0; //计数清零
SysTick->CTRL |= ((1<<1)|(1<<0)); //允许中断,选择半系统时钟,启动定时器
}
//============================主函数==================================
int main(void)
{
LPC_GPIO2->DIR = 0xFFF; //设置端口2为输出方向
LPC_GPIO2->DATA = 0xFFE;//端口2最低位输出0,点亮最末一个LED
SysTick_init();//调用系统定时器
while(1)
{
;//空循环
}
}

把程序编译后下载到LPC1114中,给系统上电,可看到接到端口2上的12个LED在闪烁流动。



评论


技术专区

关闭