7. 系统定时器与中断管理
在嵌入式系统中,定时器和中断管理是非常重要的功能。定时器用于精确控制时间相关的任务,而中断管理则用于处理实时事件,确保系统响应及时且高效。本节将详细介绍如何在 LPC1100 系列单片机中使用系统定时器和管理中断。
7.1 系统定时器概述
LPC1100 系列单片机内置了多个定时器,其中系统定时器(SysTick)是 ARM Cortex-M0 核心的一部分。SysTick 定时器主要用于操作系统的时间管理,但也广泛用于其他需要精确时间控制的应用。
7.1.1 SysTick 定时器的特点
- 24位递减计数器:SysTick 定时器使用一个 24 位的递减计数器。
- 可编程的时钟源:默认情况下,SysTick 定时器使用系统时钟(System Clock)作为时钟源,但也可以选择外部时钟源。
- 中断功能:当计数器递减到 0 时,可以触发一个中断。
- 自动重载:计数器递减到 0 后,可以自动重载预设值,实现周期性中断。
7.1.2 SysTick 定时器的寄存器
SysTick 定时器有以下几个主要寄存器:
- SysTick Control and Status Register (STCTRL):用于控制 SysTick 定时器的启用、中断使能和状态。
- SysTick Reload Value Register (ST_RELOAD):用于设置计数器的重装载值。
- SysTick Current Value Register (ST_CURRENT):用于读取当前计数器的值。
- SysTick Calibration Value Register (ST_CALIB):用于读取时钟校准值,通常用于确定硬件时钟的精度。
7.1.3 SysTick 定时器的配置
配置 SysTick 定时器通常需要以下几个步骤:
- 使能 SysTick 定时器:通过设置
STCTRL
寄存器的ENABLE
位。 - 设置重装载值:通过设置
ST_RELOAD
寄存器。 - 使能中断:通过设置
STCTRL
寄存器的TICKINT
位。 - 选择时钟源:通过设置
STCTRL
寄存器的CLKSOURCE
位。
7.1.4 SysTick 定时器的中断处理
SysTick 定时器的中断处理函数通常在启动文件中定义。在 startup_LPC11xx.s
文件中,可以找到 SysTick 中断向量的定义。
; SysTick_Handler
.section .text
.global SysTick_Handler
SysTick_Handler:
; 处理 SysTick 中断的代码
; 通常会调用一个 C 函数
LDR R0, =SysTickHandler
BX R0
在 main.c
文件中,可以定义 SysTickHandler
函数来处理 SysTick 中断。
7.1.5 SysTick 定时器的使用示例
以下是一个使用 SysTick 定时器实现每 1 秒触发一次中断的示例:
#include "LPC11xx.h"
// SysTick 中断处理函数
void SysTickHandler(void) {
// 中断处理代码
// 例如,每 1 秒切换一次 LED 状态
LPC_GPIO0->FIOSET = (1 << 18); // 设置 P0.18 引脚
LPC_GPIO0->FIOCLR = (1 << 18); // 清除 P0.18 引脚
}
// 配置 SysTick 定时器
void configureSysTick(void) {
// 计算重装载值
uint32_t reloadValue = (SystemCoreClock / 1000) - 1; // 1ms
// 设置重装载值
SysTick->LOAD = reloadValue;
// 清除当前值
SysTick->VAL = 0;
// 使能中断
SysTick->CTRL |= (1 << 1); // 设置 TICKINT 位
// 选择时钟源
SysTick->CTRL |= (1 << 2); // 设置 CLKSOURCE 位
// 使能 SysTick 定时器
SysTick->CTRL |= (1 << 0); // 设置 ENABLE 位
}
int main(void) {
// 初始化 GPIO
LPC_GPIO0->DIR |= (1 << 18); // 设置 P0.18 引脚为输出
// 配置 SysTick 定时器
configureSysTick();
while (1) {
// 主循环
}
}
7.1.6 SysTick 定时器的精度调整
SysTick 定时器的精度可以通过调整重装载值来实现。例如,如果需要实现更精确的时间控制,可以使用更高频率的时钟源或更小的重装载值。
7.1.7 SysTick 定时器的调试
调试 SysTick 定时器时,可以使用调试工具(如 JTAG 或 SWD)来查看 ST_CURRENT
寄存器的值,以确保计数器按预期递减。此外,可以通过在中断处理函数中添加调试信息来验证中断是否按时触发。
7.2 中断管理概述
LPC1100 系列单片机使用嵌套向量中断控制器(NVIC)来管理中断。NVIC 支持多个中断源,并可以配置每个中断的优先级和使能状态。
7.2.1 NVIC 的特点
- 多中断源:LPC1100 系列支持多个中断源,包括外部中断、定时器中断、UART 中断等。
- 可配置的优先级:NVIC 允许为每个中断源配置优先级,确保高优先级的中断优先处理。
- 中断使能和禁止:可以通过 NVIC 的寄存器来使能或禁止特定的中断源。
- 中断向量表:NVIC 使用一个中断向量表来管理中断向量,该表通常位于闪存或 RAM 中。
7.2.2 NVIC 的寄存器
NVIC 的主要寄存器包括:
- Interrupt Set-Enable Registers (ISER):用于使能中断。
- Interrupt Clear-Enable Registers (ICER):用于禁止中断。
- Interrupt Set-Pending Registers (ISPR):用于手动设置中断挂起状态。
- Interrupt Clear-Pending Registers (ICPR):用于清除中断挂起状态。
- Interrupt Priority Registers (IPR):用于设置中断的优先级。
- Interrupt Control and State Register (ICSR):用于控制和状态管理。
7.2.3 中断优先级配置
中断优先级可以通过 IPR
寄存器来配置。每个中断源的优先级由 3 位优先级字段表示,优先级数值越小,优先级越高。
7.2.4 中断使能和禁止
使能和禁止中断可以通过 ISER
和 ICER
寄存器来实现。例如,使能外部中断 0:
void enableExternalInterrupt0(void) {
NVIC->ISER[0] = (1 << 0); // 使能外部中断 0
}
禁止外部中断 0:
void disableExternalInterrupt0(void) {
NVIC->ICER[0] = (1 << 0); // 禁止外部中断 0
}
7.2.5 中断处理函数
中断处理函数通常在启动文件中定义,并在 main.c
文件中实现。例如,外部中断 0 的处理函数:
; EXTI0_IRQHandler
.section .text
.global EXTI0_IRQHandler
EXTI0_IRQHandler:
; 处理外部中断 0 的代码
; 通常会调用一个 C 函数
LDR R0, =EXTI0Handler
BX R0
在 main.c
文件中实现 EXTI0Handler
函数:
void EXTI0Handler(void) {
// 外部中断 0 的处理代码
// 例如,读取外部引脚的输入状态
uint32_t pinState = LPC_GPIO0->FIOPIN & (1 << 0); // 读取 P0.0 引脚状态
if (pinState) {
// 处理高电平
} else {
// 处理低电平
}
}
7.2.6 中断优先级配置示例
以下是一个配置外部中断 0 的优先级为最高优先级的示例:
void configureInterruptPriority(void) {
NVIC->IPR[0] = (0 << 5); // 设置外部中断 0 的优先级为 0(最高优先级)
}
7.2.7 中断调试
调试中断时,可以使用调试工具来查看中断状态寄存器 ICSR
的值,以确保中断按预期触发。此外,可以在中断处理函数中添加调试信息来验证中断处理逻辑。
7.3 定时器中断应用
定时器中断在嵌入式系统中非常常见,用于实现周期性任务、延时功能、时间管理等。以下是一个使用定时器 0 实现周期性中断的示例。
7.3.1 定时器 0 的配置
定时器 0 的配置包括设置时钟源、预分频值、匹配值和中断使能。
#include "LPC11xx.h"
// 定时器 0 中断处理函数
void TIMER0_IRQHandler(void) {
// 清除中断标志
LPC_TMR32B0->IR = (1 << 0); // 清除匹配中断标志
// 中断处理代码
// 例如,每 1 秒切换一次 LED 状态
LPC_GPIO0->FIOSET = (1 << 18); // 设置 P0.18 引脚
LPC_GPIO0->FIOCLR = (1 << 18); // 清除 P0.18 引脚