一、LPC82X 的中断系统概述
1.1 LPC82x (Cortex-M0+ CPU)的中断系统概述
− 管理中断的进入、退出、嵌套,与优先级制度;自动保存与恢复现场
− 这一切都是全自动的,软件看不见——也就是说不用写汇编代码!
- M0+ 自带了一个嵌套中断控制器,简称 NVIC,支持 32 路中断输入
− 提供开关控制
− 反映和控制待决标志
− 设置优先级
− 反映 ISR 是否已进入
− 如果发生嵌套,可以出现两个以上中断的 ISR 已进入的情况
- 除了来自 NVIC 的中断,M0+ 自身还定义了其它的特殊中断类型
− 不可屏蔽中断(NMI), 硬故障(Hard_Fault),系统服务(SVCall, PendSV),系统节拍定时器(Systick)
1.2 中断编号
− 你可以把它当作是中断源的身份证号
− 中断号用于寻找 ISR 的入口地址
− M0+ 内核以变换后的中断号记录当前的中断
− NVIC 就是以中断号管理各路中断源的
- 在 NVIC 的 API 函数中也是以中断号作为参数
− 这个和我们写程序时关系最密切了
− 在 cmsis.h 中的“IRQn_Type”定义了 LPC82x 的中断编号分配
NVIC 管理的中断从 0 号开始
M0+ 直接管理的特殊中断,中断号为负
1.3 中断开关控制示意图
1.4 在 LPC82x 上使用中断
1.5 LPC82x 的 CPU 内核(M0+)支持的其它中断
- 除了来自 NVIC 的中断,M0+ 还支持其它的特殊中断类型,用于任何以 M0+ 为内核的 MCU:
− NMI: 不可屏蔽中断。LPC82x 可以任选一个中断源连接到 NMI
常用的是把看门狗中断或者欠压警告中断连接到 NMI,它们的后果很严重。
− Hard_fault: 软件或总线错误导致程序不能执行下去时,就会产生 hardfault(硬故障),并且走中断响应的流程进入 Hard_Fault 中断
例如,软件存取了一个不存在的地址,或者地址没有对齐。
又如,CPU 读取到的指令译码失败(如果 M3 的 bin 文件烧到了 M0 的器件上)。
− SVCall 和 PendSV: 给操作系统选用的系统服务中断(一般不用)
在严格划分特权级别以保护系统的 RTOS上 可能会用到 SVCall。
PendSV 专用于上下文切换。
− SysTick: 这是 M0+ 自带的一个简单的节拍定时器所对应的中断源。
在一般情况下可以作为系统的时基,有超低功耗要求时不能用。
1.6 中断向量表
- M0+ 要求程序在 CPU 看到的 0 地址存储一张表,称为“中断向 量表”
− 每个表目存储一个 ISR 的入口地址(有些位置没有用到)
− 每个中断源在表中有自己的位置
NVIC 管理的中断,就是中断号 +16。
M0+ 自己管理的中断,另有编号机制。
− 0 号表目有些特殊,它存储复位后的栈指针,会被装载到 SP 寄存器
− 默认的中断向量表存储在启动代码中
- LPC82x 提供“0 地址重映射”功能,把 0 地址开始的 512 字节范围映射到 Flash, RAM, 或者 Boot ROM
− 用户程序执行时默认把 Flash 映射到 0
− 用户程序可以今后再手工把向量表放到 Flash 或 RAM
− 映射成 RAM 后,Flash 的前 512 字节内容无法被访问到!
- 启动后,M0+ 允许把向量表放到别处 (NVIC 的 VTOR 寄存器)
1.7 LPC82x 启动文件(keil_startup_lpc82x.s)中的中断向量表
IRQ 编号
|
| 中断向量
| 向量地址
|
| DCD
| __initial_sp ;
| Top of Stack
| 0x0000_0000
|
| DCD
| Reset_Handler ;
| Reset Handler
| 0x0000_0004
| -14
| DCD
| NMI_Handler ;
| NMI Handler
| 0x0000_0008
| -13
| DCD
| HardFault_Handler ;
| HardFault Handler
| 0x0000_000C
| -12
| DCD
| 0
| Reserved interrupt
| 0x0000_0010
| -11
| DCD
| 0
| Reserved interrupt
| 0x0000_0014
| -10
| DCD
| 0
| Reserved interrupt
| 0x0000_0018
| -9
| DCD
| 0
| Checksum of the first 7 words
| 0x0000_001C
| -8
| DCD
| 0
| Reserved interrupt
| 0x0000_0020
| -7
| DCD
| 0
| Reserved interrupt
| 0x0000_0024
| -6
| DCD
| 0
| Reserved interrupt
| 0x0000_0028
| -5
| DCD
| SVC_Handler ;
| SVCall Handler
| 0x0000_002C
| -4
| DCD
| 0
| Reserved interrupt
| 0x0000_0030
| -3
| DCD
| 0
| Reserved interrupt
| 0x0000_0034
| -2
| DCD
| PendSV_Handler ;
| PendSV Handler
| 0x0000_0038
| -1
| DCD
| SysTick_Handler ;
| SysTick Handler
| 0x0000_003C
|
|
| ; External Interrupts
|
| 0
| DCD
| SPI0_IRQHandler ;
| SPI0 interrupt
| 0x0000_0040
| 1
| DCD
| SPI1_IRQHandler ;
| SPI1 interrupt
| 0x0000_0044
| 2
| DCD
| 0 ;
| Reserved interrupt
| 0x0000_0048
| 3
| DCD
| USART0_IRQHandler ;
| USART0 interrupt
| 0x0000_004C
| 4
| DCD
| USART1_IRQHandler ;
| USART1 interrupt
| 0x0000_0050
| 5
| DCD
| USART2_IRQHandler ;
| USART2 interrupt
| 0x0000_0054
| 6
| DCD
| 0 ;
| Reserved interrupt
| 0x0000_0058
| 7
| DCD
| I2C1_IRQHandler ;
| I2C1 interrupt
| 0x0000_005C
| 8
| DCD
| I2C0_IRQHandler ;
| I2C0 interrupt
| 0x0000_0060
| 9
| DCD
| SCT0_IRQHandler ;
| State configurable timer interrupt
| 0x0000_0064
| 10
| DCD
| MRT0_IRQHandler ;
| Multi-rate timer interrupt
| 0x0000_0068
| 11
| DCD
| CMP_IRQHandler ;
| Analog comparator interrupt
| 0x0000_006C
| 12
| DCD
| WDT_IRQHandler ;
| Windowed watchdog timer interrupt
| 0x0000_0070
| 13
| DCD
| BOD_IRQHandler ;
| BOD interrupts
| 0x0000_0074
| 14
| DCD
| FLASH_IRQHandler ;
| flash interrupt
| 0x0000_0078
| 15
| DCD
| WKT_IRQHandler ;
| Self-wake-up timer interrupt
| 0x0000_007C
| 16
| DCD
| ADC0_SEQA_IRQHandler ;
| ADC0 sequence A completion.
| 0x0000_0080
| 17
| DCD
| ADC0_SEQB_IRQHandler ;
| ADC0 sequence B completion.
| 0x0000_0084
| 18
| DCD
| ADC0_THCMP_IRQHandler ;
| ADC0 threshold compare and error.
| 0x0000_0088
| 19
| DCD
| ADC0_OVR_IRQHandler ;
| ADC0 overrun
| 0x0000_008C
| 20
| DCD
| DMA0_IRQHandler ;
| DMA0 interrupt
| 0x0000_0090
| 21
| DCD
| I2C2_IRQHandler ;
| I2C2 interrupt
| 0x0000_0094
| 22
| DCD
| I2C3_IRQHandler ;
| I2C3 interrupt
| 0x0000_0098
| 23
| DCD
| 0 ;
| Reserved interrupt
| 0x0000_009C
| 24
| DCD
| PIN_INT0_IRQHandler ;
| Pin interrupt 0 or pattern match engine slice 0 interrupt
| 0x0000_00A0
| 25
| DCD
| PIN_INT1_IRQHandler ;
| Pin interrupt 1 or pattern match engine slice 1 interrupt
| 0x0000_00A4
| 26
| DCD
| PIN_INT2_IRQHandler ;
| Pin interrupt 2 or pattern match engine slice 2 interrupt
| 0x0000_00A8
| 27
| DCD
| PIN_INT3_IRQHandler ;
| Pin interrupt 3 or pattern match engine slice 3 interrupt
| 0x0000_00AC
| 28
| DCD
| PIN_INT4_IRQHandler ;
| Pin interrupt 4 or pattern match engine slice 4 interrupt
| 0x0000_00B0
| 29
| DCD
| PIN_INT5_IRQHandler ;
| Pin interrupt 5 or pattern match engine slice 5 interrupt
| 0x0000_00B4
| 30
| DCD
| PIN_INT6_IRQHandler ;
| Pin interrupt 6 or pattern match engine slice 6 interrupt
| 0x0000_00B8
| 31
| DCD
| PIN_INT7_IRQHandler ;
| Pin interrupt 7 or pattern match engine slice 7 interrupt
| 0x0000_00BC
|
- 前 16 个是 M0+ 内核自己管理的特殊向量,有两个比较特殊:
− 0 号:栈指针初值
− 1 号:复位后第 1 条指令的地址
另外,7 号是前面 7 个向量之和的 checksum 补码,由开发工具自动计算。
- 后面 32 个是 NVIC 管理的最多 32 路中断未用到的填 0
二、 LPC82x 的 SysTick & GPIO 中断实验
- 目的:通过本实验,理解和掌握 SysTick 和 GPIO 中断配置的全过程
- 软/硬件环境搭建:
− 硬件:LPC824Lite-V1.0(评估板)
− 软件:SDK 从 NXP 官网下载(https://mcuxpresso.nxp.com/en/select);
工程位置:......driver_examplesgpioled_outputmdkgpio_led_output.uvprojx
- 本实验把 S1 键所在的 PIO0_12 配置为下降沿触发中断,另有 SysTick 定时器产生周期性中断(1Hz)。
- 循环读取整个端口的输入值,当对应位为 0 时,即 S1 键被按下,反转 LED2 的输出电平;同时 SysTick 一直计数,如果触发中断就反转 LED0 的电平。
按下 S1 时 LED2 熄灭或点亮,松开时一般不闪烁(但偶尔会“抖动”闪烁)
LED0 以 1Hz 频率闪烁
/**
* @brief Handle interrupt from SysTick timer: reverses current output logic of the GPIO pins.
* @return Nothing
*/
void SysTick_Handler(void)
{
GPIO_PortToggle(GPIO, APP_BOARD_TEST_LED_PORT, 1u << 7); // LED0
}
/*!
* @brief Main function
*/
int main(void)
{
uint32_t port_state = 0;
/* Define the init structure for the output LED pin*/
// LED 灯的初始化结构体
gpio_pin_config_t led_config =
{
kGPIO_DigitalOutput,// 设置为数字电平输出
0, // 输出为低电平
};
……
/* Init output LED GPIO. */
/*
初始化 PIO0 端口;
初始化 PIO0_16 和 PIO0_7 引脚为输出模式,输出高电平
*/
GPIO_PortInit(GPIO, APP_BOARD_TEST_LED_PORT);
GPIO_PortInit(GPIO, APP_SW_PORT);
GPIO_PinInit(GPIO, APP_BOARD_TEST_LED_PORT, APP_BOARD_TEST_LED_PIN, &led_config);
GPIO_PinWrite(GPIO, APP_BOARD_TEST_LED_PORT, APP_BOARD_TEST_LED_PIN, 1);
GPIO_PinInit(GPIO, APP_BOARD_TEST_LED_PORT, 7, &led_config);
GPIO_PinWrite(GPIO, APP_BOARD_TEST_LED_PORT, 7, 1);
……
//初始化 SysTick 及其中断,并启动系统计时
SysTick_Config(SystemCoreClock / TICKRATE_HZ); /* SystemCoreClock / TICKRATE_HZ 得出 SysTick
两次中断之间的节拍数 */
while (1)
{
port_state = GPIO_PortRead(GPIO, APP_SW_PORT); // 读取整个 GPIO 端口当前的输入值
if (!(port_state & (1 << APP_SW_PIN))) // 判断按键 S1 对应引脚位是否被置零
{
PRINTF("rn Port state: %xrn", port_state);
GPIO_PortToggle(GPIO, APP_BOARD_TEST_LED_PORT, 1u << APP_BOARD_TEST_LED_PIN);
}
delay();
}
}
|