程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

STM32F103嵌套向量中断控制器

一、STM32F103中断介绍

1.1 什么是中断

中断:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行;

举例:当你正在写作业时,做到一半又去吃饭,吃完饭后又回来接着原来的作业继续完成。

对于单片机来说,中断是指CPU正在处理某个事件A,发生了另一件事件B,请求CPU迅速去处理(中断发生);

CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);

CPU处理事件B完成后,再回到原来的事件A(断点)继续执行,这一过程称之为中断。

中断的作用和含义:

  • 实时控制:在确定的时间内对相应事件做出相应;例如:温度控制;
  • 故障处理:检测到故障,需要第一时间进行处理;
  • 数据传输:不确定数据何时会来,利用中断进行控制。

1.2 嵌套向量中断控制器(NVIC

NVIC即嵌套向量中断控制器,全称Nested vectored interrupt controller。属于是内核的器件,其作用是对STM32中的中断进行管理,因为Contex M3内核中的中断数量很多,当同时出现多个中断时,优先处理哪个中断?以及那些中断不处理等,都要靠NVIC进行控制。

Contrex M3内核都是支持256个中断,其中包含了16个内核异常和240个外部中断,并且具有256级的可编程中断设置。

但是对于ST公司来说,用不了Contrex M3内核中的所有中断以及中断优先级,进而对其进行了一定的裁剪。STM32F103中共有:

  • 10个内核异常;
  • 60个可屏蔽中断;(在STM32F107系列才有68个);

在中断的使用中有一个极其重要的一部分为中断服务函数(触发中断后,系统执行的部分,例如上文的吃饭过程)中断服务函数是中断的入口。

1.2.1 异常和中断向量表

定义一块固定的内存,以4字节对齐(32位),用于存放中断服务函数的首地址,系统已经将中断服务函数定义好了,放在异常和中断向量表中,我们只需要进行调用即可。

STM32f103异常和中断中断向量表如下:

其中前10个中断为内核异常,剩下的60个为可屏蔽中断,其编号为0~59,优先级从7~66

优先级号越小,优先级越高。当表中的某处异常或中断被触发,程序计数器指针(PC)将跳转到该异常或中断的地址处执行,该地址处存放这一条跳转指令,跳转到该异常或中断的服务函数处执行相应的功能。因此,异常和中断向量表只能用汇编语言编写。

1.3 工作原理

当中断被出发时,首先进入ICERISER寄存器,用于控制是否开对应的中断,打开的中断进入IPR寄存器,进行中断优先级的判断,IPR寄存器受AIRCR寄存器控制,最后按照中断优先级依次进入CPU被执行。

1.3.1 中断优先级

STM32中断优先级基本概念:

  • 抢占优先级(pre):高抢占优先级可以打断正在执行的低抢占优先级中断;即具有高抢占优先级的中断可以在具有低抢占优先级的中断处理过程中被响应,即中断嵌套;
  • 响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能相互打断;
  • 抢占优先级和响应优先级都相同的情况下,则按照他们在中断表中的排位顺序决定响应顺序;
  • 数值越小,表示优先级越高。
1.3.2 中断优先级分组

AIRCR寄存器位[10:8]用于控制优先级的分组,但是只取其中的五组优先级分组;

IPR寄存器位[7:4]用于控制中断的优先级,包括抢占优先级与响应优先级;

如下表所示:

优先级分组 AIRCR[10:8] IPR[7:4]分配 分配结果
0 111 None:[7:4] 抢占优先级(0位、0级)
响应优先级(4位、16级)
1 110 [7]:[6:4] 抢占优先级(1位、2级)
响应优先级(3位、8级)
2 101 [7:6]:[5:4] 抢占优先级(2位、4级)
响应优先级(2位、4级)
3 100 [7:5]:[4] 抢占优先级(3位、8级)
响应优先级(1位、2级)
4 011 [7:4]:None 抢占优先级(4位、16级)
响应优先级(0位、0级)

假定设置中断优先级分组为 2,每个中断的IPR寄存器的高四位中的最高2位是抢占优先级,低 2位是响应优先级。每个中断,你可以设置抢占优先级为0~3,响应优先级为0-3。假设我们设置:

  • 中断3的抢占优先级为 2,响应优先级为 1;
  • 中断6的抢占优先级为 3,响应优先级为0;
  • 中断7的抢占优先级为 2,响应优先级为 0;

那么这3个中断的优先级顺序为: 中断 7>中断 3>中断 6。 上面例子中的中断3和中断7都可以打断中断6的中断。而中断7和中断 3 却不可以相互打断!

二、中断相关寄存器

NVIC相关的寄存器有很多,但是重要的有以下几个,也是需要进行掌握的:

名称 位数 个数 作用
中断使能寄存器(ISER) 32 8 每一位控制一个中断
中断失能寄存器(ICER) 32 8 每一位控制一个中断
中断挂起控制寄存器组(ISPR) 32 8 每一位控制一个中断
中断解挂起控制寄存器组(ICPR) 32 8 每一位控制一个中断
应用程序中断及复位控制寄存器(AIRCR) 32 1 位[10:8]控制中断优先级分组
中断优先级寄存器(IPR) 8 240 8个位对应一个中断,而STM32只使用高4位

ISERICER寄存器共有32*8=256,用于控制240个中断的打开与关闭。

AIRCR寄存器,位1098三位用于控制优先级的分组,三位共2*2*2=8种,取其中的5组作为中断优先级的分组情况;

IPR寄存器,用于控制中断的优先级,包括抢占优先级与响应优先级,高4位控制,至于哪几位控制抢占,哪几位控制响应,由AIRCR寄存器说了算。

2.1 中断使能寄存器(ISER

0Cortex M3内核支持256个中断,这里用832位寄存器(即8ISER)来控制,每个位控制一个中断。

但是STM32的可屏蔽中断最多只有68个(互联型),所以就是三个(ISER[0~2]]),总共可以表示96个中断,而STM32只用了其中的前68位;

  • ISER[0]bit0~31分别对应中断0~31
  • ISER[1]bit0~32对应中断32~63
  • ISER[2]bit0~3对应中断64~67

这样总共68个中断就分别对应上了。 若使能某个中断,必须设置相应的ISER位为1 。具体某一位代表哪个中断,可以参前面介绍的异常和中断向量表。

2.2 中断失能寄存器(ICER

该寄存器组与ISER的作用恰好相反,是用来清除某个中断的使能的。若除能某个中断,必须设置相应的ICER位为1。

2.3 中断挂起控制寄存器组(ISPR

通过置1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。

2.4 中断解挂起控制寄存器组(ICPR

通过置 1,可以将挂起的中断解挂。

2.5 应用程序中断及复位控制寄存器(AIRCR

STM32将中断分为5个组,组0-4,该分组由AIRCR[10:8]决定,最终影响到IPR[7:4]位分配情况。具体参考前面介绍的中断优先级分组。

2.6 中断优先级寄存器(IPR

IPR是控制中断优先级的一个非常重要的寄存器,STM32的中断分组与这个寄存器组密切相关。IPR 寄存器组由2408bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示240个可屏蔽中断。

STM32只用到了其中的68个,IPR[67]~IP[0]分别对应中断 67~0。而每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高4 位。这4位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据AIRCR中的中断优先级分组设置来决定。

三、中断配置源码

3.1 系统启动

无论是是何种MCU,从简单的51MSP430,到ARM9ARM11A7都必须有启动文件,因为对于嵌入式开发,绝大部分情况都是使用C语言,而C语言一般都是从main函数开始,但是对于MCU来说,他是如何找到并执行main函数的,就需要用到“启动文件”。

STM32F103的启动文件为stm32f10x_vectors.s,启动文件是使用机器认识的汇编语言,经过一些必要的配置,最终能够调用main函数,使得用户程序能够在MCU上正常运行起来的必备文件。

本文以MDK环境下的stm32f10x_vectors.s为模板讲解,不同编译器下的启动文件不同,此外不同编译器的伪汇编指令是不一样的,由于需要和gcc编译器的伪汇编指令区分开来。

3.1.1 栈初始化

代码的开始,就是开辟栈空间,用于局部变量,函数调用,函数参数等;

Stack_Size       EQU     0x00000400
                 AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem        SPACE   Stack_Size
__initial_sp

汇编伪指令讲解:

  • EQU:宏定义的伪指令,相当于等于,类似于C语言中的宏定义define
  • AREA: 告诉汇编器汇编一个新的代码段或者数据段;
  • SPACE:用于分配一定大小的内存空间,单位字节;

因此上面的代码结合起来就是分配一个长度为1kb的栈空间,命名为STACK,并且不初始化,可读可写,按照2的3次方(8个字节)对齐开辟。

__initial_sp只是一个标号,标号主要用于表示一片内存空间的某个位置,等价于C语言中的地址概念。地址仅仅表示存储空间的位置。

__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。

3.1.2 堆初始化

接下来是开辟堆空间,主要用于动态内存分配,使用malloccalloc等函数分配的变量空间是在堆上的;

Heap_Size        EQU     0x00000400

                 AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem         SPACE   Heap_Size
__heap_limit

同样分配一个长度为1kb的堆空间,命名为HEAP,并且不初始化,可读可写,按照2的3次方(8个字节)对齐开辟。

标号_heap_base表示堆的起始地址,标号__heap_limit紧挨着SPACE语句放置,表示堆的结束地址,堆是由低向高生长的(与栈相反)。

3.1.3 导入异常和中断处理函数

接着是导入外部文件定义的异常和中断处理函数,为建立异常和中断向量表做准备;

                 THUMB
                 PRESERVE8

                 ; Import exceptions handlers
                 IMPORT  NMIException
                 IMPORT  HardFaultException
                 IMPORT  MemManageException
                 IMPORT  BusFaultException
                 IMPORT  UsageFaultException
                 ......
                 IMPORT  FLASH_IRQHandler
                 IMPORT  RCC_IRQHandler
                 IMPORT  EXTI0_IRQHandler
                 IMPORT  EXTI1_IRQHandler
                 IMPORT  EXTI2_IRQHandler
                 IMPORT  EXTI3_IRQHandler
                 IMPORT  EXTI4_IRQHandler
                 ......
                 IMPORT  TIM1_BRK_IRQHandler
                 IMPORT  TIM1_UP_IRQHandler
                 IMPORT  TIM1_TRG_COM_IRQHandler
                 IMPORT  TIM1_CC_IRQHandler
                 IMPORT  TIM2_IRQHandler
                 IMPORT  TIM3_IRQHandler
                 IMPORT  TIM4_IRQHandler
                 IMPORT  I2C1_EV_IRQHandler
                 IMPORT  I2C1_ER_IRQHandler
       			 ......
                 IMPORT  DMA2_Channel1_IRQHandler
                 IMPORT  DMA2_Channel2_IRQHandler
                 IMPORT  DMA2_Channel3_IRQHandler
                 IMPORT  DMA2_Channel4_5_IRQHandler
                 
;*******************************************************************************
; Fill-up the Vector Table entries with the exceptions ISR address
;*******************************************************************************
				 ; 定义一个数据段 命名为RESET,DATA表示数据, READONLY表示可读
                 AREA    RESET, DATA, READONLY
                 EXPORT  __Vectors

其中:

  • PRESERVE8表示指定当前文件的推栈按照8字节对齐;
  • THUMB表示后面指令位THUMB指令;
  • IMPORT用于声明外部符号,告诉汇编器这些符号来自其他模块或库,需要在链接时进行解析。这种机制使得汇编程序能够调用外部库中的函数或访问外部定义的全局变量,实现代码的模块化和复用;
  • EXPORT:声明一个标号具有全局属性,用于将符号暴露给当前源文件以外的其他文件。

比如我们在《STM32F103 GPIO和串口配置》中介绍的串口1接收中断处理函数USART1_IRQHandler,其实现位于usart.c文件中。

3.1.4 异常和中断向量表

Cortex M3内核规定起始地址必须存放栈顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。

Cortex-M3内核固定了异常和中断向量表的位置, 但是起始地址是可变化的。

__Vectors        DCD  __initial_sp              ; Top of Stack
                 DCD  Reset_Handler
                 DCD  NMIException
                 DCD  HardFaultException
                 DCD  MemManageException
                 DCD  BusFaultException
                 DCD  UsageFaultException
                 DCD  0                 ; Reserved
                 DCD  0                 ; Reserved
                 DCD  0                 ; Reserved
                 DCD  0                 ; Reserved
                 DCD  SVCHandler
                 DCD  DebugMonitor
                 DCD  0                 ; Reserved
                 DCD  PendSVC
                 DCD  SysTickHandler
                 DCD  WWDG_IRQHandler
                 DCD  PVD_IRQHandler
                 DCD  TAMPER_IRQHandler
                 DCD  RTC_IRQHandler
                 DCD  FLASH_IRQHandler
                 DCD  RCC_IRQHandler
                 DCD  EXTI0_IRQHandler
                 DCD  EXTI1_IRQHandler
                 DCD  EXTI2_IRQHandler
                 DCD  EXTI3_IRQHandler
                 DCD  EXTI4_IRQHandler
                 DCD  DMA1_Channel1_IRQHandler
                 DCD  DMA1_Channel2_IRQHandler
                 DCD  DMA1_Channel3_IRQHandler
                 DCD  DMA1_Channel4_IRQHandler
                 DCD  DMA1_Channel5_IRQHandler
                 DCD  DMA1_Channel6_IRQHandler
                 DCD  DMA1_Channel7_IRQHandler
                 DCD  ADC1_2_IRQHandler
                 DCD  USB_HP_CAN_TX_IRQHandler
                 DCD  USB_LP_CAN_RX0_IRQHandler
                 DCD  CAN_RX1_IRQHandler
                 DCD  CAN_SCE_IRQHandler
                 DCD  EXTI9_5_IRQHandler
                 DCD  TIM1_BRK_IRQHandler
                 DCD  TIM1_UP_IRQHandler
                 DCD  TIM1_TRG_COM_IRQHandler
                 DCD  TIM1_CC_IRQHandler
                 DCD  TIM2_IRQHandler
                 DCD  TIM3_IRQHandler
                 DCD  TIM4_IRQHandler
                 DCD  I2C1_EV_IRQHandler
                 DCD  I2C1_ER_IRQHandler
                 DCD  I2C2_EV_IRQHandler
                 DCD  I2C2_ER_IRQHandler
                 DCD  SPI1_IRQHandler
                 DCD  SPI2_IRQHandler
                 DCD  USART1_IRQHandler
                 DCD  USART2_IRQHandler
                 DCD  USART3_IRQHandler
                 DCD  EXTI15_10_IRQHandler
                 ......

以上代码定义了处理器复位后各个异常和中断对应的处理函数。异常和中断向量表的第一个元素存放的是__initial_sp栈顶的指针;第二个元素存放的是Reset_Handler函数入口地址。

3.1.5 Reset_Handler

系统上电或者复位后后,首先执行的代码就是Reset_Handler

; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler
				 
				         IF      DATA_IN_ExtSRAM == 1
; FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is 
; required, then adjust the Register Addresses


; Enable FSMC clock
				         LDR R0,= 0x00000114 
				         LDR R1,= 0x40021014
				         STR R0,[R1]	                 
                  
; Enable GPIOD, GPIOE, GPIOF and GPIOG clocks
				         LDR R0,= 0x000001E0
				         LDR R1,= 0x40021018
				         STR R0,[R1]	   

; SRAM Data lines, NOE and NWE configuration 
; SRAM Address lines configuration 
; NOE and NWE configuration   
; NE3 configuration 
; NBL0, NBL1 configuration 

				         LDR R0,= 0x44BB44BB 
				         LDR R1,= 0x40011400
				         STR R0,[R1]		
				 
				         LDR R0,= 0xBBBBBBBB 
				         LDR R1,= 0x40011404
				         STR R0,[R1]		
				 
				         LDR R0,= 0xB44444BB 
				         LDR R1,= 0x40011800
				         STR R0,[R1]		
				 
				         LDR R0,= 0xBBBBBBBB 
				         LDR R1,= 0x40011804
				         STR R0,[R1]		
				 
				         LDR R0,= 0x44BBBBBB 
				         LDR R1,= 0x40011C00
				         STR R0,[R1]	    

				         LDR R0,= 0xBBBB4444 
				         LDR R1,= 0x40011C04
				         STR R0,[R1]	    

				         LDR R0,= 0x44BBBBBB
				         LDR R1,= 0x40012000
				         STR R0,[R1]		

				         LDR R0,= 0x44444B44
				         LDR R1,= 0x40012004
				         STR R0,[R1]      
				         
; FSMC Configuration   
; Enable FSMC Bank1_SRAM Bank 

				        LDR R0,= 0x00001011
				        LDR R1,= 0xA0000010
				        STR R0,[R1]	

				        LDR R0,= 0x00000200 
				        LDR R1,= 0xA0000014
				        STR R0,[R1]	

				  		 
				        ENDIF
				        
				        
				 IMPORT  __main
                 LDR     R0, =__main
                 BX      R0
                 ENDP

PROCENDP这一对伪指令把程序分为若干个过程,使程序结构更加清晰。

_main标号表示C/C++标准实时库函数里的一个初始化子程序main的入口地址。该程序的一个主要作用是初始化堆栈(跳转_user_initial_stackheap标号进行初始化堆栈),并初始化映像文件,最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点,因为这是由C/C++标准实时库所规定的。

__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR
3.1.6 地址空间分布

我们打开我们的项目,查看Options for target

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(202)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示