给初学者的STM32(Cortex-M3)中断原理及编程方法介绍 [原创www.cnblogs.com/helesheng]

本人编著的《基于STM32的嵌入式系统原理及应用》(ISBN:9787030697974)刚刚在科学出版社出版。这本书花费了半年以上的时间,凝聚了笔者作为高校教师和嵌入式工程师的一些经验,希望对大学生、嵌入式初学者和有一定经验的工程师都有参考和借鉴作用。在写作中尤其注意了不做芯片手册的“搬运工”和“翻译者”,试图从开发者和工程师的角度理清知识点之间的逻辑关系,给读者一条清晰的学习路径。写作中尽量做到用生活中的实例来阐述抽象的概念;用工程实例来帮助大家提高嵌入式开发的实践能力。另外为帮助读者理解某些难点,我们在Bilibili上陆续发布免费的讲解的视频,欢迎大家关注我的B站账号:“何乐生0”(本文讲解点这里)。

下面将该书中关于STM32的Cortex-M3中断控制器NVIC的一小节发出来供大家拍砖,欢迎大家批评指正。以下内容欢迎转载,但请注明出处: https://www.cnblogs.com/helesheng

一、中断机制概述

中断是计算机系统的一种处理异步事件的重要方法。它的作用是在计算机的CPU运行软件的同时,监测系统内外有没有发生需要CPU处理的“紧急事件”:当需要处理的事件发生时,中断控制器会打断CPU正在处理的常规事务,转而插入一段处理该紧急事件的代码;而该事务处理完成之后,CPU又能正确地返回刚才被打断的地方,以继续运行原来的代码。中断可以分为“中断响应”、“中断处理”和“中断返回”三个阶段。

中断处理事件的异步性是指,紧急事件在什么时候发生与CPU正在运行的程序完全没有关系,是无法预测的。既然无法预测,只能随时查看这些“紧急事件”是否发生,而中断机制最重要的作用,是将CPU从不断监测紧急事件是否发生这类繁重工作中解放出来,将这项“相对简单”的繁重工作交给“中断控制器”这个硬件来完成。中断机制的第二个重要作用是判断哪个或哪些中断请求更紧急,应该优先被响应和处理,并且寻找不同中断请求所对应的中断处理代码所在的位置。中断机制的第三个作用是帮助CPU在运行完处理紧急事务的代码后,正确地返回之前运行被打断的地方。根据上述中断处理的过程及其作用,读者会发现中断机制既提高了CPU正常运行常规程序的效率,又提高了响应中断的速度,是几乎所有现代计算机都配备的一种重要机制。

嵌入式系统是嵌入宿主对象中,帮助宿主对象完成特定任务的计算机系统,其主要工作就是和真实世界打交道。能够快速、高效地处理来自真实世界的异步事件成为嵌入式系统的重要标志,因此中断对于嵌入式系统而言显得尤其重要,是学习嵌入式系统的难点和重点。

1.1 中断的基本概念

这里通过一个生活实例来解释中断机制中涉及的各种概念:某人在家中读书学习,假设可能有两件打断他学习的事情分别是:有人敲门来访和接听电话。安静地读书学习相当于嵌入式处理器的常规程序,而“有人敲门来访”和“接听电话”两件事就是可能引发中断的“中断源”,中断之前他读书所达到的页码称为“断点”。如图1所示,中断源提出要求响应中断称为“中断请求”;而CPU打断原来的程序运行,转而处理开门或接电话等事情称为“中断响应”;为响应事件而运行的程序称为“中断服务程序”;处理完异步事件,返回的过程称为“中断返回”。中断响应过程又可以分为自动保存当前寄存器值的“现场保护”和定位并跳转到中断服务程序地址两个小步骤。中断返回也相应地分为恢复寄存器值的“恢复现场”和恢复原来主程序运行的位置的“返回断点”两个小步骤。当然,也可以选择不理睬这些打断读书的事情,例如,将手机设置为静音状态,此时就称为“中断屏蔽”。

给初学者的STM32(Cortex-M3)中断原理及编程方法介绍 [原创www.cnblogs.com/helesheng]

图1 中断示意图

如图2所示,当电话铃声和敲门声同时响起时,只能根据事情的重要性,先响应一件事,而决定孰先孰后的分级机制称为“中断优先级”。显然图2中开门迎客的优先级高于接听电话。

给初学者的STM32(Cortex-M3)中断原理及编程方法介绍 [原创www.cnblogs.com/helesheng]

图2 中断优先级示意图

优先级高可以有两种体现,一种如图2所示,两个中断同时到来时先响应优先级较高的那个中断请求,再响应优先级低的那个。优先级高还可以体现为,即使优先级低的中断服务程序在运行中,优先级高的中断也可以打断优先级低的中断服务程序,而优先级低的中断服务程序只有等到优先级高的中断服务程序运行完成后才能继续运行。如图3所示,开门任务能够打断接听电话任务,形成类似“菊花”的嵌套结构,就称为“中断嵌套”。STM32中将能够被打断,实现中断嵌套的优先级称为“抢占优先级”,而不能够被打断,只能优先响应的中断优先级称为“子优先级”。

给初学者的STM32(Cortex-M3)中断原理及编程方法介绍 [原创www.cnblogs.com/helesheng]

图3 中断嵌套示意图

中断机制中还有一个重要概念称为“中断向量表”,中断向量表是一张由中断服务程序的入口地址构成的表格,一般占据代码空间的起始地址。处理器设计者会将所有可能响应的中断源所对应的中断服务程序入口地址按照固定的顺序排列在该表格中。当某个中断源被响应时,处理器会自动跳转到该中断源的中断服务程序入口所在的表格地址,并由该表格位置进一步跳转到中断服务程序。

1.2 ARM Cortex-M3的中断控制器——NVIC

ARM V7架构将处理器分为应用处理器A型、实时处理器R型和微控制器M型。M型中最重要的ARM Cortex-M3充分考虑了嵌入式应用中对异步事件处理的实时性要求。因而与之前的ARM架构不同,直接在Cortex-M3内核中集成了功能强劲的中断控制器,并命名为NVIC。在内核中集成中断控制器不但防止了不同芯片生产厂商间的不兼容,还大大提高了中断控制器和Cortex-M3内核的结合紧密度,降低了中断响应时间,提高了实时性。

NVIC直译为嵌套向量中断控制器,顾名思义,指ARM Cortex-M3的中断控制器支持中断嵌套和中断向量表的自动跳转功能。典型的NVIC可支持256个中断,其中包括16个由内核产生的异常中断和240个外设中断。其中,内核异常中断指由Cortex-M3内核产生的复位、硬件错误、SysTick定时器中断等中断,而外设中断则是由管脚电平变化、UART或DMA等外设变化引起的中断。NVIC还能实现Cortex-M3内核响应中断请求后的自动现场保护(自动保存处理器状态寄存器)和中断返回时的自动现场恢复(自动恢复处理器状态寄存器)。另外还有一种称为“中断尾链(末尾连锁)”的技术,能够在从高优先级的中断服务程序返回时避免多余的自动现场恢复,即自动进入低优先级的中断服务程序,从而避免了一轮多余的自动现场恢复和自动现场保护操作。

二、 STM32的NVIC

STM32的NVIC也只是标准NVIC的一部分,但主要功能都已经包含在其中:STM32的NVIC可支持16个内核异常中断和68个外设中断(其中STM32F103系列60个,STM32F107系列68个)。同时,每个中断源可配置4位优先级控制字PRI_n(ARM Cortex-M3内核定义了8位,STM32微控制器只使用了其中的4位),具有16级可编程中断优先级。

表1是STM32F103系列的中断向量表,供读者查询。其中灰色部分是内核异常中断。

表1 STM32F103系列的中断向量表

位置

优先级

优先级类型

名称

说明

地址

保留

0x0000_0000

3

固定

Reset

复位

0x0000_0004

2

固定

NMI

不可屏蔽

RCC时钟安全系统(CSS)连接到NMI向量

0x0000_0008

1

固定

硬件失效(HardFault)

所有类型的失效

0x0000_000C

0

可设置

存储管理(MemManage)

存储器管理

0x0000_0010

1

可设置

总线错误(BusFault)

预取指失败,存储器访问失败

0x0000_0014

2

可设置

错误应用(UsageFault)

未定义的指令或非法状态

0x0000_0018

保留

0x0000_001C

~0x0000_002B

3

可设置

SVCall

通过SWI指令的系统服务调用

0x0000_002C

4

可设置

调试监控(DebugMonitor)

调试监控器

0x0000_0030

保留

0x0000_0034

5

可设置

PendSV

可挂起的系统服务

0x0000_0038

6

可设置

SysTick

系统定时器

0x0000_003C

0

7

可设置

WWDG

窗口定时器中断

0x0000_0040

1

8

可设置

PVD

连接到EXTI的电源检测(PVD)中断

0x0000_0044

2

9

可设置

TAMPER

侵入检测中断

0x0000_0048

3

10

可设置

RTC

实时时钟(RTC)全局中断

0x0000_004C

4

11

可设置

FLASH

闪存全局中断

0x0000_0050

5

12

可设置

RCC

复位和时钟控制(RCC)中断

0x0000_0054

6

13

可设置

EXTI0

EXTI线0中断

0x0000_0058

7

14

可设置

EXTI1

EXTI线1中断

0x0000_005C

8

15

可设置

EXTI2

EXTI线2中断

0x0000_0060

9

16

可设置

EXTI3

EXTI线3中断

0x0000_0064

10

17

可设置

EXTI4

EXTI线4中断

0x0000_0068

位置

优先级

优先级类型

名称

说明

地址

11

18

可设置

DMA1通道1

DMA1通道1全局中断

0x0000_006C

12

19

可设置

DMA1通道2

DMA1通道2全局中断

0x0000_0070

13

20

可设置

DMA1通道3

DMA1通道3全局中断

0x0000_0074

14

21

可设置

DMA1通道4

DMA1通道4全局中断

0x0000_0078

15

22

可设置

DMA1通道5

DMA1通道5全局中断

0x0000_007C

16

23

可设置

DMA1通道6

DMA1通道6全局中断

0x0000_0080

17

24

可设置

DMA1通道7

DMA1通道7全局中断

0x0000_0084

18

25

可设置

ADC1_2

ADC1和ADC2的全局中断

0x0000_0088

19

26

可设置

USB_HP_CAN_TX

USB高优先级或CAN发送中断

0x0000_008C

20

27

可设置

USB_LP_CAN_RX0

USB低优先级或CAN接收0中断

0x0000_0090

21

28

可设置

CAN_RX1

CAN接收1中断

0x0000_0094

22

29

可设置

CAN_SCE

CAN SCE中断

0x0000_0098

23

30

可设置

EXTI9_5

EXTI线[9:5]中断

0x0000_009C

24

31

可设置

TIM1_BRK

TIM1刹车中断

0x0000_00A0

25

32

可设置

TIM1_UP

TIM1更新中断

0x0000_00A4

26

33

可设置

TIM1_TRG_COM

TIM1触发和通信中断

0x0000_00A8

27

34

可设置

TIM1_CC

TIM1捕获比较中断

0x0000_00AC

28

35

可设置

TIM2

TIM2全局中断

0x0000_00B0

29

36

可设置

TIM3

TIM3全局中断

0x0000_00B4

30

37

可设置

TIM4

TIM4全局中断

0x0000_00B8

31

38

可设置

I2C1_EV

I2C1事件中断

0x0000_00BC

32

39

可设置

I2C1_ER

I2C1错误中断

0x0000_00C0

33

40

可设置

I2C1_EV

I2C2事件中断

0x0000_00C4

34

41

可设置

I2C1_ER

I2C2错误中断

0x0000_00C8

35

42

可设置

SPI1

SPI1全局中断

0x0000_00CC

36

43

可设置

SPI2

SPI2全局中断

0x0000_00D0

37

44

可设置

USART1

USART1全局中断

0x0000_00D4

38

45

可设置

USART2

USART2全局中断

0x0000_00D8

39

46

可设置

USART3

USART3全局中断

0x0000_00DC

40

47

可设置

EXTI15_10

EXTI线[15:10]中断

0x0000_00E0

41

48

可设置

RTCAlarm

连接到EXTI的RTC闹钟中断

0x0000_00E4

42

49

可设置

USB唤醒

连接到EXTI的从USB待机唤醒中断

0x0000_00E8

43

50

可设置

TIM8_BRK

TIM8刹车中断

0x0000_00EC

位置

优先级

优先级类型

名称

说明

地址

44

51

可设置

TIM8_UP

TIM8更新中断

0x0000_00F0

45

52

可设置

TIM8_TRG_COM

TIM8触发和通信中断

0x0000_00F4

46

53

可设置

TIM8_CC

TIM8捕获比较中断

0x0000_00F8

47

54

可设置

ADC3

ADC3全局中断

0x0000_00FC

48

55

可设置

FSMC

FSMC全局中断

0x0000_0100

49

56

可设置

SDIO

SDIO全局中断

0x0000_0104

50

57

可设置

TIM5

TIM5全局中断

0x0000_0108

51

58

可设置

SPI3

SPI3全局中断

0x0000_010C

52

59

可设置

UART4

UART4全局中断

0x0000_0110

53

60

可设置

UART5

UART5全局中断

0x0000_0114

54

61

可设置

TIM6

TIM6全局中断

0x0000_0118

55

62

可设置

TIM7

TIM7全局中断

0x0000_011C

56

63

可设置

DMA2通道1

DMA2通道1全局中断

0x0000_0120

57

64

可设置

DMA2通道2

DMA2通道2全局中断

0x0000_0124

58

65

可设置

DMA2通道3

DMA2通道3全局中断

0x0000_0128

59

66

可设置

DMA2通道4_5

DMA2通道4和DMA2通道5全局中断

0x0000_012C

NVIC通过中断优先级控制字PRI_n既支持嵌套中断又支持非嵌套中断,方法如表2所示,将每个中断的优先级控制字PRI_n(4位)分为两截:前半截用于定义本中断的抢占优先级,后半截用于定义子优先级。而分割的具体办法由优先级组别寄存器定义:若优先级组别定义为4,则前半截的抢占优先级占据全部4位(共可定义24=16中抢占优先级),而后半截的子优先级占据0位(无法定义子优先级);若优先级组别定义为3,则前半截的抢占优先级占据全部3位(共可定义23=8种抢占优先级),而后半截的子优先级占据1位(共可定义21=2种子优先级);其他优先级组别定义以此类推,表2所示是NVIC的中断优先级配置。

表2 NVIC的中断优先级配置

优先级组别

抢占优先级

子优先级

4

4位/16级

0位/0级

3

3位/8级

1位/2级

2

2位/4级

2位/4级

1

1位/2级

3位/8级

0

0位/0级

4位/16级

值得注意的是,每个中断源都拥有自己的优先级控制字PRI_n(n为中断源编号),但优先级组别寄存器只有一个。即一旦对NVIC定义了优先级控制字的分割方式,则对所有中断源的所有PRI_n,分割方式都是相同的,并且意法半导体官方不建议在程序中频繁修改优先级组别寄存器的内容。

与本节关于通用中断嵌套规则的描述相同,STM32的NVIC的优先级嵌套规则如下:

(1)抢占优先级高的中断可以打断抢占优先级低的中断服务,构成中断嵌套。

(2)当两个或多个同级别抢占优先级的中断出现时,它们不能构成中断嵌套,但STM32先响应子优先级高的中断请求。

(3)当两个或者多个同级别抢占优先级和同级别子优先级的中断同时出现时,STM32先响应在中断向量表中靠前的那个中断。

通过实际生活的例子类比上述NVIC响应顺序原则:在火车站购票时,先比较抢占优先级,抢占优先级高(军人)的中断优先响应;当抢占优先级相同时,比较子优先级,子优先级高(军衔)的中断优先响应;当上述两者都相同时,比较它们在中断向量表中的位置(年龄),位置低(年龄大)的中断优先响应。

再通过一个中断配置实例,说明STM32的NVIC配置和响应顺序:假定设置优先级组为2,然后进行以下设置。

(1)中断3(RTC中断)的抢占优先级为2,子优先级为1。

(2)中断6(外部中断0)的抢占优先级为3,子优先级为0。

(3)中断7(外部中断1)的抢占优先级为2,子优先级为0。

则中断优先级顺序为:中断7 >中断3 >中断6。其中,中断3和中断7的抢占优先级相同,所以中断3不能被中断7打断,但中断6可以被中断3或中断7打断。

三、NVIC的配置和使用

STM32的嵌套向量中断控制器需要和中断源配合使用,本书将在后续章节中采用标准外设库提供的库函数,并结合具体中断源外设来详细讲解。这里为了帮助读者理解NVIC的工作方式,仅给出基于标准外设库的NVIC通用配置流程。

(1)配置中断优先级分组,例如:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//将中断优先级组别配置为2

上面代码调用的函数“void NVIC_PriorityGroupConfig(uint32_t NVIC_Priority Group);”是意法半导体官方提供的标准外设库函数,其功能是对中断优先级组别进行配置。而其参数NVIC_PriorityGroup_2是由标准外设库事先定义的宏,代表将组别设为2,也就是2位抢占优先级,2位子优先级。

(2)针对具体需要的中断源,设置对应的抢占优先级和子优先级,初始化NVIC,例如:

NVIC_Init(&NVIC_InitStructure);   //用结构体NVIC_InitStructure中定义的参数初始化NVIC                                  //寄存器

函数“void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);”也是标准外设库中提供的函数。&NVIC_InitStructure是指向初始化结构体NVIC_InitStructure的指针。结构体NVIC_InitStructure的定义如下,其成员包含了NVIC的主要参数。

typedef struct
{
uint8_t NVIC_IRQChannel; //设置中断源是哪一个
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //子优先级
FunctionalState NVIC_IRQChannelCmd; //使能/禁能本中断源
} NVIC_InitTypeDef;

(3)编写对应的中断服务程序。官方提供的标准外设库已经在文件stm32f10 x_it.c中为STM32所包含的每一个外设编写了中断服务程序的框架。其命名规则为“void PPP_IRQHandler(void);”,其中PPP代表了具体中断源的缩写,如:

void WWDG_IRQHandler(void);   //窗口看门狗中断服务
void RTC_IRQHandler (void); //实时时钟中断服务
void EXTI0_IRQHandler (void); //外部中断0中断服务
void EXTI1_IRQHandler (void); //外部中断1中断服务
void USART1_IRQHandler(void); //串口1中断服务
void SPI1_IRQHandle(void); //SPI1中断服务
上一篇:asp.net Global.asax 方法的使用和说明


下一篇:jpush 延迟推送的栗子