再上一篇:12.4 性能评估
上一篇:12.5 当处理器被锁定(Lockup)时
主页
下一篇:13.2 电源管理
再下一篇:13.3 多处理机通信
文章列表

第13章

《Cortex-M3 权威指南》,嵌入式处理器开发教程。

Cortex-M3的其它特性

 SysTick定时器

 电源管理

 多处理机通信

 自复位控制


到了这里,我们已经学完了 CM3的绝大多数重要和基础的特性,再加一把劲儿,这章不难,过
了以后就到了一个里程碑了。

13.1 SysTick 定时器

回顾第 8章讲述 NVIC时,曾走马观花地带过了 SysTick定时器。复习一下:SysTick是一个

24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要不把它在 SysTick控制及状态寄存器中的使能位清除,就永不停息。图 13.1中小结了 SysTick的相关寄存 器。


图 13.1 SysTick 相关寄存器的定义

CM3允许为 SysTick提供两个时钟源以供选择。第一个是内核的“自由运行时钟”FCLK。“自 由”表现在它不来自系统时钟 HCLK,因此在系统时钟停止时 FCLK也继续运行。第二个是一个外部 的参考时钟。但是使用外部时钟时,因为它在内部是通过 FCLK 来采样的,因此其周期必须至少是 FCLK的两倍(采样定理)。很多情况下芯片厂商都会忽略此外部参考时钟,因此通常不可用。通过 检查校准寄存器的位[31](NOREF),可以判定是否有可用的外部时钟源,而芯片厂商则必须把该引 线连接至正确的电平。

当 SysTick定时器从 1计到 0时,它将把 COUNTFLAG位置位;而下述方法可以清零之: 读取 SysTick控制及状态寄存器(STCSR)
往 SysTick当前值寄存器(STCVR)中写任何数据

SysTick的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要这种“滴答”来

推动任务和时间的管理。如欲使能 SysTick异常,则把 STCSR.TICKINT置位。另外,如果把向量 表重定位到了 SRAM中,还需要为 SysTick异常建立向量,提供其服务例程的入口地址,如下段代 码所演示:

; 建立SysTick异常服务例程

MOV

R0,

#0xF

; 异常号:15

LDR

R1,

=systick_handler

; 加载服务例程的入口地址

LDR

R2,

=0xE000ED08

; 加载向量表偏移量寄存器的地址

LDR

R2,

[R2]

; 读取向量表的首地址

STR

R1,

[R2, R0, LSL #2]

; 写入向量

下面的代码演示启用 SysTick的基本程序

; 使能SysTick定时器,并且使能SysTick异常

LDR MOV

R0, R1,

=0xE000E010

#0

; 加载STCSR的地址

STR LDR

STR STR MOV STR

R1, R1,

R1, R1, R1, R1,

[R0]

=0x3FF

[R0,#4] [R0,#8]

#0x7 [R0]

; 先停止SysTick,以防意外产生异常请求

; 让SysTick每1024周期计完一次。因为是从1023数到

; 0,总共数了1024个周期,所以加载值为0x3FF

; 写入重装载的值

; 往STCVR中写任意的数,以确保清除COUNTFLAG标志

; 选择FCLK作为时钟源,并使能SysTick及其异常请求

; 写入数值,开启定时器

除此之外,SysTick定时器还提供了走完 10ms所需要的格数(TENMS位段),作为时间校准的
参考信息。在 CM3处理器的顶层有一个 24位的输入,芯片厂商可以写入一个 10ms的加载值,写程 序时就可以读取 STCR寄存器中的 TENMS位段来获取此信息。不一定每个芯片都实现了此功能,因 此在使用时还需查阅芯片的数据手册。

SysTick定时器还可以用作闹钟,作为启动一个特定任务的时间依据。例如,如果需要在 300 周期后执行一段代码,就可以在 SysTick 异常服务例程中设置执行那段代码的软件标志。使用 SysTick时,清零 CURRENT再编程 RELOAD寄存器,以使它在 300周期后产生异常,如下述代码所 演示:

LDR

LDR

r0,

r1,

=15

=SysTickAlarm ; SysTick异常服务例程为SetupExcpHanler

BL SetupExcpHandler ; 调用前面章节讲到的子程来建立向量

LDR MOV

STR

R0, R1,

R1,

=0xE000E010

#0 [R0]

; SysTick寄存器组的基地址

; 编程前先除能SysTick

STR LDR STR LDR MOV

STR

R1, R1, R1, R4, R5,

R5,

[R0,#0x8]

=(300-12) [R0,#0x4]

=SysTickFired

#0 [R4]

; 清零CURRENT

; 设置装载值。减去12是为了补偿中延迟

; 写入RELOAD

; 在RAM中的一个变量,指示是计时到期

; 初始为0

MOV STR LDR

R1, R1, R4,

#0x7 [R0]

=SysTickFired

; 使用FCLK,使能SysTick,使能SysTick异常

; 启动计时

WaitLoop

LDR R5, [R4] ; 循环查询软件标志

CMP R5, #0

BEQ WaitLoop

... ; SysTickFired在服务例程中被置位,主程序可以继续执行

本例中使用以前讲到的 SetupExcpHandler来建立向量表,但注意:必须重定位向量表到 RAM
中才行。

SetupExcpHandler

; 入口条件:R0 = 异常号

; 入口条件:R1 = 异常服务例程

PUSH

{R0,

R2, LR}

LDR

R2,

=NVIC_VECTTBL

LDR

STR.W

R2,

R1,

[R2]

[R2, R0, LSL #2]

; 读取向量表的地址

; 表中[R2+R0<<2]的位置就是为该向量的

POP

{R0,

R2, PC}

; 快速返回

因为计数器是从 0开始计数的,所以它会立即把 300-12加载入 CURRENT。12是中断响应的最
短延时,因此减去它用以补偿。但是如果有更高优先级的异常抢占或者阻塞了它,则中断延迟还是 会有的。
另外要注意的,减去 12只适用于一次性(oneshot)的闹钟操作,在这种情况下必须在 SysTick 服务例程中按停这个 SysTick。进一步地,如果其它异常把它延迟得太久,就有可能会使 SysTick 异常被悬起两次。因此,对于单次处理时,还需要其它一些步骤来消灭二次触发:

SysTickAlarm ; SYSTICK exception handler

PUSH

LDR

{LR}

R0,

=0xE000E010

; SYSTICK寄存器组的基地址

MOV

R1,

#0

STR

R1,

[R0]

; 除能SysTick,因为只使用一次

LDR

R0,

=0xE000ED04

LDR

R1,

=0x02000000

; 手工清除NVIC中的SysTick悬起位

STR R1, [R0]

...

LDR

R2,

=SysTickFired

; 执行所需的处理工作

LDR

R1,

[R2]

ORR

R1,

#1

STR POP

R1, [R2]

{PC}

; 设置软件标志,与主程序同步,以执行任务

; 异常返回

在服务例程的末尾处,通过设置 SysTickFired标志,通知主程序定时已经到期,可以结束循
环等待了。