LPC824-系统定时器SysTick

发布时间:2023-05-24  

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


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

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

上表中,SYST_CSR是系统定时器的控制寄存器,负责SysTick的启动、中断使能、输入时钟选择、溢出标志读取等操作;SYST_RVR是系统定时器的初始值重载寄存器,负责SysTick的24位初值载入;SYST_CVR是系统定时器的当前值寄存器,负责获取SysTick的24位当前计数值,当对该寄存器进行写操作时,该寄存器的数值将会被清零;SYST_CALIB是系统定时器的校准值寄存器,负责SysTick的校准。
下面给出的是上表中控制寄存器SYST_CSR的全部位结构,其字节地址为0xE000E010。(1)第0位(ENABLE)是使能SysTick位,置1启动计数,置0关闭计数。
(2)第1位(TICKINT)是SysTick的中断使能位,置1使能中断,置0禁止中断。
(3)第2位(CLKSOURCE)是输入时钟的选择位,置1时选择系统时钟做为计数脉冲,置0时选择半系统时钟做为计数脉冲的参考时钟。
(4)第3到15位为保留位,不能对它们写1。
(5)第16位(COUNTFLAG)是溢出标志位,当计数的值递减到0时,该位被置1,在读取该值后自动清零。
(6)第17到31位为保留位,不能对它们写1。SYST_RVR是系统定时器的初始值重载寄存器,负责SysTick的24位初值载入。下表给出了它的全部位结构,其字节地址为0xE000E014。

(1)第0到23位(RELOAD)为重载的初值,即当计数器计数到0后重载到计数器中的值,一共24位。
(2)第24到31位为保留位,不能对它们写1。
SYST_CVR是系统定时器的当前值寄存器,负责获取SysTick的24位当前计数值,当对该寄存器进行写操作时,该寄存器的数值将会被清零。下表给出了它的全部位结构,其字节地址为0xE000E018。

(1)第0到23位(CURRENT)为SysTick的当前值,一共24位。
(2)第24到31位为保留位,不能对它们写1。
SYST_CALIB是系统定时器的校准值寄存器,负责SysTick的校准。下表给出了它的全部位结构,其字节地址为0xE000E01C。

 

(1)第31位(NOREF)指示处理器是否有基准时钟,由芯片厂家出厂时决定。该位读出值为1,指明无独立的基准时钟。
(2)第30位(SKEW)指示TENMS值是否精确。由于TENMS不可知,因此10ms不精确计时的校准值不能确定,这会影响SysTick作为软件实时时钟的适用性。该位读出值为1,指明未提供TENMS值。
(3)第24到29位为保留位。
(4)第0到23位(TENMS)在发生系统时钟扭曲错误时,在10ms时序下的校正值。该位读出值为0,指明校准值未知。

下面给出系统时钟SysTick的结构体定义。
typedef struct
{
  __IOM uint32_t CTRL;
  __IOM uint32_t LOAD;
  __IOM uint32_t VAL;
  __IM    uint32_t CALIB;
} SysTick_Type;

注意,在上述定义中,结构体成员CTRL对应SYST_CSR寄存器,LOAD对应SYST_RVR寄存器,VAL对应SYST_CVR寄存器,CALIB对应SYST_CALIB寄存器。结构体中并没有全部采用SysTick中的寄存器名称。

SysTick定时器组的基址为0xE000E000,所以要将基址指针强制转换为上述结构体,还要加上下面的定义。
#define SCS_BASE            (0xE000E000UL)
#define SysTick_BASE        (SCS_BASE +  0x0010UL)
#define SysTick             ((SysTick_Type   *)     SysTick_BASE  )
对于系统定时器SysTick产生的中断,也有特定的入口函数,形式如下所示。
 void SysTick_Handler(void)
{
       系统定时中断服务程序部分
}由上述可见,其实系统定时器本身就是一个普通的定时器,在实际应用时可按以下顺序进行操作。
(1)给SYST_RVR寄存器写入定时器的初始值。
(2)写SYST_CVR寄存器对计数值进行清零。
(3)设置SYST_CSR寄存器选择相应的时钟源,启动定时并使能中断。
SysTick定时的时长由系统时钟频率、系统时钟选择和载入的初始值共同决定。假设系统时钟为24MHz,默认情况下SysTick的CLKSOURCE值为0,即选择半系统时钟频率。这样,输入给定时器计数的时钟就是24÷2=12MHz,计数周期为1/(12MHz)=1/12us,则计12次就是1us的定时,但实际上SysTick采用的是倒数计数方式,即从最大值依次递减计数直到0产生溢出信号。所以12次计数实际上是0~11次,即最大值要减1,即12-1=11。同时要注意,由于计数位宽是24位,所以最大计数值不能超过2的24次方(即16777216),由此,可得出微秒级的初始值计算公式,如下:
LOAD=((12*us)-1),其中us取值范围1~1398000
同理可得出毫秒级的初始值计算公式:
LOAD=((12000*ms)-1),其中ms取值范围1~1398
上升到一般情况,定时初始值可用下面的公式来计算:上式中,系统时钟SysClk的单位是MHz,CLKSOURCE的值是0或1,得到的是微秒级别的定时,要注意LOAD的值不能大于16777216。下面来看一个使用SysClk实现的毫秒级延时函数,代码如下:
void delay_ms(uint32_t ms)
{
    SysTick->LOAD = (((12000)*ms)-1);         //载入初始值
    SysTick->VAL = 0;                                   //写当前值寄存器使其清零
    SysTick->CTRL |= (1<<0);                        //启动定时器,选择半系统时钟
    while(!(SysTick->CTRL & 0x10000));        //循环查询,等待定时时间到
    SysTick->CTRL &= ~(1<<0);                    //关闭定时器
}
在程序中,通过“while(!(SysTick ->CTRL & 0x10000));”这句来等待定时器溢出,其实就是循环查询控制寄存器SYST_CSR中的COUNTFLAG位(第16位)是否被置1,若为0则循环等待直到1为止。注意,本函数延时的最大时长为1398ms,即传递给它的参数不能超过1398,否则不能实现延时功能。下面再来看一个流水灯的例子,要求实现一个8位的流水灯,时间间隔为100ms。电路延用第一个演示示例中的电路原理图,参考代码如下:#include

//************************端口初始化***********************************
void Port_init(void)
{
LPC_GPIO_PORT->DIR0 = 0x1FFFFFFF; //设置端口为输出方向
LPC_GPIO_PORT->PIN0 = 0x1FFFFFFF; //关闭所有LED
LPC_GPIO_PORT->CLR0 = (1<<7); //点亮第1个LED
}
//************************定时器初始化*********************************
void SysTick_init(void)
{
SysTick->LOAD = (((12000)*100)-1);
SysTick->VAL = 0;
SysTick->CTRL |= ((1<<1)|(1<<0));
}
//***************************主函数************************************
int main(void)
{
Port_init(); //调用端口初始化
SysTick_init(); //调用定时器初始化
while(1)
{
;
}
}
//************************定时器中断***********************************
void SysTick_Handler(void)
{
static uint8_t i=0;
switch(i++)
{
case 0:
LPC_GPIO_PORT->SET0 = (1<<7); //熄灭第1个LED
LPC_GPIO_PORT->CLR0 = (1<<13); //点亮第2个LED
break;
case 1:
LPC_GPIO_PORT->SET0 = (1<<13); //熄灭第2个LED
LPC_GPIO_PORT->CLR0 = (1<<16); //点亮第3个LED
break;
case 2:
LPC_GPIO_PORT->SET0 = (1<<16); //熄灭第3个LED
LPC_GPIO_PORT->CLR0 = (1<<17); //点亮第4个LED
break;
case 3:
LPC_GPIO_PORT->SET0 = (1<<17); //熄灭第4个LED
LPC_GPIO_PORT->CLR0 = (1<<19); //点亮第5个LED
break;
case 4:
LPC_GPIO_PORT->SET0 = (1<<19); //熄灭第5个LED
LPC_GPIO_PORT->CLR0 = (1<<27); //点亮第6个LED
break;
case 5:
LPC_GPIO_PORT->SET0 = (1<<27); //熄灭第6个LED
LPC_GPIO_PORT->CLR0 = (1<<28); //点亮第7个LED
break;
case 6:
LPC_GPIO_PORT->SET0 = (1<<28); //熄灭第7个LED
LPC_GPIO_PORT->CLR0 = (1<<18); //点亮第8个LED
break;
case 7:
LPC_GPIO_PORT->SET0 = (1<<18); //熄灭第8个LED
LPC_GPIO_PORT->CLR0 = (1<<7); //点亮第1个LED
i = 0; //计数清零
break;
}
}


在上述程序中,由于8个LED并没有接到LPC824相邻的引脚,因此使用了针对引脚的位操作方式来实现流水灯效果。把程序编译后下载到LPC824中,给系统上电,可看到接到端口上的8个LED在闪烁流动。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>