中断

允许 CPU 在执行程序的过程中,暂停当前任务,转而去处理更高优先级的任务(称为中断服务程序,ISR),处理完成后再返回原来的任务继续执行。

为什么需要中断?

中断的主要作用包括:

  1. 提高 CPU 效率:
    • 在没有中断的情况下,CPU 需要通过轮询(Polling)的方式不断检查设备状态,这会浪费大量的 CPU 资源。中断机制允许 CPU 在设备需要处理时才响应,从而提高效率。
  2. 实时响应:
    • 中断可以确保关键事件(如按键按下、定时器溢出等)得到及时处理,满足实时性要求。
  3. 多任务处理:
    • 中断机制使得 CPU 可以在多个任务之间快速切换,实现多任务处理。
  4. 硬件事件处理:
    • 硬件设备(如键盘、鼠标、定时器等)通过中断通知 CPU 有事件发生,CPU 可以及时处理这些事件。

如何实现中断?

中断的实现通常包括以下几个步骤:

(1) 硬件支持

  • 中断源:能够触发中断的事件或设备(如定时器、外部按键、串口接收数据等)。
  • 中断控制器:负责管理多个中断源,并根据优先级决定哪个中断先被处理(如 ARM Cortex-M 中的 NVIC)。
  • 中断向量表:存储中断服务程序(ISR)入口地址的表格,CPU 根据中断号查找对应的 ISR。

(2) 软件实现

  1. 初始化中断:
    • 配置中断源(如定时器、GPIO 等)。
    • 设置中断优先级。
    • 使能中断。
  2. 编写中断服务程序(ISR):
    • ISR 是处理中断事件的函数,通常需要快速执行并清除中断标志。
  3. 处理中断:
    • 当中断发生时,CPU 保存当前上下文(如寄存器值),跳转到 ISR 执行,执行完成后恢复上下文并继续原来的任务。

具体例子:STM32 定时器中断

以下是一个基于 STM32 的定时器中断示例,展示了如何实现中断。

(1) 硬件环境

  • STM32 微控制器(如 STM32F103)。
  • 定时器(如 TIM2)用于周期性触发中断。

(2) 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void TIM5_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能

//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中断,允许更新中断

//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5; //先占优先级5级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器

TIM_Cmd(TIM5, ENABLE); //使能TIM5
}

//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
printf("TIM3输出.......\r\n");
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}

main 函数中调用 TIM2_Int_Init(4999, 7199); 后,定时器会独立于 main 函数继续运行,并且当满足条件时(如定时器计数溢出),CPU 会自动跳转到中断服务函数(ISR)中处理中断事件。

1
2
3
4
5
6
7
8
9
10
11
12
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
/*从0开始计数到9999,系统时钟频率为 72 MHz,分频7200后计数频率为10000Hz,即1秒计数10000次*/
TIM3_Int_Init(10000-1,7200-1); //初始化定时器3,定时器周期1S
while(1){
//...
}
}