第十一章 STM32中断应用

目录

11.1 中断概述

11.1.1 中断的基本概念和原理

11.1.2 STM32中断系统结构和工作原理

11.1.3 中断向量表及存储位置

11.2 中断控制器

11.2.1 NVIC的功能和特点

11.2.2 中断优先级的设置方法和规则

11.2.3 中断使能和禁止的方法和注意事项

11.3 中断类型和应用

11.3.1 外部中断:外部触发引脚、按键等

11.3.2 定时器中断:定时器的基本原理和使用方法

11.3.3 DMA中断:DMA的基本原理和使用方法

11.3.4 其他中断的类型

11.4 中断处理函数

11.4.1 中断处理函数与主函数的关系和区别

11.5 中断优化和注意事项

中断响应时间和中断处理时间的优化方法

中断嵌套和中断优先级的注意事项和实践经验

11.6 EXTI中断控制实验

11.6.1 实验要求

11.6.2 硬件设计

11.6.3 软件设计

11.6.4 下载验证


11.1 中断概述

        STM32 中断非常强大,每个外设都可以产生中断。在STM32微控制器中,中断是一种重要的机制,允许处理器在执行主程序的同时响应外部事件。中断可以通过硬件或软件触发,并且可以优先于主程序执行。使用中断,可以实现异步事件的处理,提高系统的响应能力和并发性能。在STM32中,中断被广泛用于各种任务,如通信、定时器、ADC、DMA等。了解中断机制对于有效利用STM32的功能和性能至关重要。

11.1.1 中断的基本概念和原理

        F103 在内核水平上搭载了一个中断响应系统,支持为数众多的系统中断和外部中断。其中系统中断有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60 个。除了个别中断的优先级被定死外,其它中断的优先级都是可编程的。有关具体的系统中断和外部中断可在标准库文件 stm32f10x.h 这个头文件查询到,在 IRQn_Type 这个结构体里面包含了 F103 系列全部的中断声明。

F103系统中断清单

第十一章 STM32中断应用_第1张图片

 F103外部中断清单

第十一章 STM32中断应用_第2张图片

11.1.2 STM32中断系统结构和工作原理

        STM32中断系统的结构和工作原理如下:

        中断请求来源:STM32的中断请求可以来自外部和内部两个方面。外部中断是由GPIO口引脚的电平或边沿信号变化触发,而内部中断通常是由硬件模块(如定时器、ADC)或软件产生的。

        NVIC控制器:在STM32中,所有中断请求都由NVIC(Nested Vectored Interrupt Controller)控制器进行管理和调度。NVIC是一个基于向量表的中断控制器,通过优先级和向量表来实现对中断请求的管理。

        中断分组:STM32将中断分为多个组别,每个组别包含一组中断请求。不同组别的中断请求可以具有不同的优先级,并且可以使用优先级抢占和屏蔽机制来确保系统的实时性和可靠性。STM32中断分组方式可选为0~4个前缀,用于设定中断优先级组和亚组。

        中断服务程序:当中断事件发生后,CPU会暂停当前任务并跳转到相应的中断服务程序,处理该事件。中断服务程序通常包括以下几个步骤:

        保存CPU寄存器的值:包括堆栈指针、程序计数器等。

        处理中断请求:根据外部或内部中断的类型进行相应的处理,如清除标志位、读取数据等操作。

        执行用户自定义代码:根据实际需求执行用户自定义的代码段。

        恢复CPU寄存器的值:将保存在堆栈中的寄存器值恢复到其原始状态,以便CPU继续执行之前的任务。

        中断优先级:STM32中,所有中断请求都具有唯一的编号(IRQn),并且可以根据编号和中断分组方式确定其优先级。优先级高的中断可以打断正在执行的低优先级中断,从而确保系统的实时性和可靠性。如果多个中断请求的优先级相同,则可以使用优先级抢占机制来确定响应顺序。

        总之,STM32中断系统是一个重要的机制,在提高系统的实时性和并发性能方面具有重要作用。了解STM32中断系统的结构和工作原理,可以帮助我们更好地设计和实现STM32系统,并有效利用其各种功能和性能。

11.1.3 中断向量表及存储位置

        STM32中断向量表是一个存储器区域,用于保存处理器中断向量的入口地址。在STM32中,中断向量表通常存储在Flash或SRAM中,并且可以通过配置实现动态加载和切换。

        具体来说,STM32中断向量表的存储位置包括以下几种方式:

        Flash存储:大多数STM32芯片使用Flash作为中断向量表的存储介质。在这种情况下,中断向量表存储在Flash的起始地址处,即0x0800 0000。用户需要将中断向量表放置在相应的Flash扇区中,并在链接脚本文件中明确定义中断向量表存储地址。

        SRAM存储:少部分STM32芯片支持将中断向量表存储在SRAM中,这通常用于调试或系统启动时加载自定义的中断向量表。在这种情况下,中断向量表数据存储在SRAM的起始地址处,并在启动过程中将中断向量表复制到Flash的相应地址。

        BOOTLOADER存储:在使用BOOTLOADER进行应用程序升级时,应用程序的中断向量表可以被存储在BOOTLOADER的Flash区域中,以避免升级过程中中断服务程序丢失。这种方式需要在应用程序中设置中断向量表基地址(VTOR)为BOOTLOADER的Flash起始地址。

        无论是Flash存储还是SRAM存储,STM32的中断向量表结构都是固定的,包含每个中断的处理函数地址。在中断触发时,CPU会自动从中断向量表中读取对应中断的处理函数地址,并跳转到该地址执行中断服务程序。

        总之,了解STM32中断向量表的存储位置和结构可以帮助我们更好地设计和实现STM32系统,并有效利用其各种功能和性能。

11.2 中断控制器

11.2.1 NVIC的功能和特点

        STM32中的NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)是一个属于M3内核上的一个外设,用于处理和管理系统中断。以下是STM32 NVIC的主要功能和特点:

        中断优先级:NVIC支持多个中断源同时存在,并且可以为每个中断源分配不同的中断优先级。这使得开发人员可以根据应用程序的需求对中断进行优化。

        嵌套中断:NVIC支持嵌套中断,当一个中断正在处理时,如果有更高优先级的中断请求,则当前中断会被挂起并转入更高优先级中断的处理。在更高优先级中断完成后,上一个中断将从挂起状态恢复,并继续执行中断处理程序。

        中断控制:NVIC提供了对中断使能、禁止、清除、设置等的控制。通过这些控制,开发人员可以方便地管理中断并确保系统稳定性。

        中断向量表:NVIC使用中断向量表来确定中断源及其相应中断服务程序的地址,这些地址通常存储在Flash或RAM中。中断向量表通过一个特殊寄存器VTOR指向。

        低功耗模式:NVIC支持低功耗模式,以降低系统功耗并延长电池寿命。

        在固件库中, NVIC 的结构体定义可谓是颇有远虑,给每个寄存器都预留了很多位,恐怕为的是日后扩展功能。不过 STM32F103 可用不了这么多,只是用了部分而已,具体使用了多少可参考《Cortex-M3 内核编程手册》 -4.3.11:NVIC 寄存器映射。

typedef struct
{
	__IO uint32_t ISER[8];            // 中断使能寄存器              
		uint32_t RESERVED0[24];                                   
	__IO uint32_t ICER[8];            // 中断清除寄存器         
		uint32_t RSERVED1[24];                                    
	__IO uint32_t ISPR[8];            // 中断使能悬起寄存器
		uint32_t RESERVED2[24];                                   
 
	__IO uint32_t ICPR[8];            // 中断清除悬起寄存器          
		uint32_t RESERVED3[24];                                   
	__IO uint32_t IABR[8];            // 中断有效位寄存器         
		uint32_t RESERVED4[56];                                   
	__IO uint8_t  IP[240];            // 中断优先级寄存器 (8Bit wide)         
		uint32_t RESERVED5[644];                                  
	__O  uint32_t STIR;               // 软件触发中断寄存器         
}  NVIC_Type;

        固件库文件 core_cm3.h 的最后,还提供了 NVIC 的一些函数,这些函数遵循 CMSIS 规则,只要是 Cortex-M3 的处理器都可以使用,具体如下:

第十一章 STM32中断应用_第3张图片

         这些库函数我们在编程的时候用的都比较少,甚至基本都不用。在配置中断的时候我们还有更简洁的方法。

        总之,STM32的NVIC是一个非常强大和灵活的硬件块,它为开发人员提供了丰富的中断控制功能,可帮助实现高效、稳定和低功耗的系统设计。

11.2.2 中断优先级的设置方法和规则

        STM32中断优先级是通过向量表中的优先级字段实现的,该字段可以包含子组和主组两个部分。其中,主组用于确定不同中断源之间的优先级关系,子组用于确定相同主组内不同中断源之间的优先级关系。在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR宽度为8bit,原则上每个外部中断可配置的优先级为0~255数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在 F103 中,只使用了高 4bit,如下所示:

         用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

11.2.2.1 优先级分组

        优先级的分组由内核外设 SCB 的应用程序中断及复位控制寄存器 AIRCR 的 PRIGROUP[10:8] 位决定, F103 分为了 5 组,具体如下:主优先级 = 抢占优先级

第十一章 STM32中断应用_第4张图片

         设置优先级分组可调用库函数 NVIC_PriorityGroupConfig() 实现,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。

/**
  * @brief  Configures the priority grouping: pre-emption priority and subpriority.
  * @param  NVIC_PriorityGroup: specifies the priority grouping bits length. 
  *   This parameter can be one of the following values:
  *     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
  *                                4 bits for subpriority
  *     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
  *                                3 bits for subpriority
  *     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
  *                                2 bits for subpriority 

  *     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
  *                                1 bits for subpriority
  *     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
  *                                0 bits for subpriority
  * @retval None
  */
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

        在进行中断优先级设置之前,需要先选择合适的优先级分组方式。STM32支持4种优先级分组方式,分别为:

        分组0:子组优先级位数为0

        分组1:子组优先级位数为1

        分组2:子组优先级位数为2

        分组3:子组优先级位数为3

第十一章 STM32中断应用_第5张图片

         在选择优先级分组时,需要考虑系统的实际需求和性能要求。注意 如果优先级分组为 0,则抢占优先级就不存在,优先级就全部由子优先级控制。

11.2.2.2 优先级设置

        在STM32中,每个外设都有一个或多个与之相关联的中断线。当外设发生中断时,可以触发相应的中断服务程序(ISR)来处理它。

        中断优先级是用来确定当多个中断同时发生时,哪个中断会被优先处理的。在STM32中,中断优先级由两部分组成:抢占优先级和响应优先级。抢占优先级用于比较不同中断之间的优先级,而响应优先级则用于比较同一中断内部不同事件的优先级。

        在设置中断优先级之前,首先需要配置NVIC(Nested Vectored Interrupt Controller)寄存器,以使得CPU能够正确地响应中断请求。然后,可以使用__NVIC_SetPriority()函数来设置中断优先级。该函数有两个参数:中断向量号和优先级值。使用__NVIC_GetPriority()函数可以获得当前中断的优先级。

        建议在设置中断优先级时,将抢占优先级和响应优先级都设置为不同的值,以确保中断处理程序的正确性和可靠性。同时,在实际应用中,也需要根据具体的需求来选择合适的中断处理优先级,以充分利用系统资源,提高系统的稳定性和性能。

11.2.2.3 优先级规则

        优先级设置分为主优先级和子优先级两部分。

        主优先级具有高优先级的中断请求会立即被响应。主优先级使用8位或16位表示,由中断请求的NVIC IRQ通道号决定。当多个中断请求同时发生时,具有较高主优先级的请求将保证首先被响应。

        子优先级用于在具有相同主优先级的中断请求之间进行优先级划分。子优先级可以根据具体需求使用0~N位(其中N为分组方式中的子优先级位数)进行设置。当多个中断请求具有相同主优先级时,具有较高的子优先级的请求将被优先响应。

        在进行中断优先级设置时,需要遵循以下几个规则:

        中断抢占优先级越低,响应优先级越高。例如,抢占优先级为0的中断比抢占优先级为1的中断具有更高的优先级。响应优先级也是如此,例如,响应优先级为0的事件比响应优先级为1的事件具有更高的优先级。

        抢占优先级和响应优先级各自的取值范围由系统时钟频率决定。通常情况下,抢占优先级取值范围为0到15,响应优先级取值范围为0到3。在设置中断优先级时,需要根据具体的时钟频率和需求来选择合适的取值范围。

        在同一抢占优先级内,响应优先级越高的事件优先处理。例如,在抢占优先级为1的中断中,响应优先级为0的事件比响应优先级为1的事件先处理。

        如果多个中断或事件的优先级相同,则按照它们在NVIC向量表中的位置进行排队处理。排在前面的中断或事件先被处理。

        需要注意的是,在设置中断优先级时,必须遵循一定的规则和原则,以确保系统的正确性和可靠性。例如,必须避免设置过多的高优先级中断,否则可能会导致低优先级中断无法得到及时处理,从而影响系统的稳定性和性能。同时,也应该根据具体应用场景来选择适当的中断优先级设置方式,以满足实际需求。

11.2.3 中断使能和禁止的方法和注意事项

中断使能和禁止方法:

        使能中断:使用NVIC_EnableIRQ()函数,将对应中断的中断向量号作为参数传入。

        禁止中断:使用NVIC_DisableIRQ()函数,将对应中断的中断向量号作为参数传入。

注意事项:

        在中断处理函数中,不要直接使用任何其他中断。这可能导致意外结果或重复触发。

        不要在临界区内长时间禁用中断。否则,可能会导致其他中断被延迟。

        当多个中断同时请求处理时,优先级较高的中断将获得处理权。可以通过配置中断优先级来改变中断的优先级。

        在中断处理函数中,需要清除中断标志以避免重复触发该中断。可以使用NVIC_ClearPendingIRQ()函数来清除中断标志。

        在启用中断之前,必须确保已经初始化了相关的硬件、变量和数据结构。对于某些外设,需要在使用中断之前配置相关寄存器以启用中断。

        中断嵌套(即,在一个中断处理程序中调用另一个中断处理程序)需要特别小心,因为它可能导致无限递归和死锁。

11.3 中断类型和应用

        STM32支持多种不同类型的中断,如外部中断、定时器中断、ADC中断、UART中断和DMA中断等,每种中断都有其特定的应用场景和优点。例如,外部中断可以用于处理外部事件(如按键按下),定时器中断可以用于生成精确定时的周期性事件,UART中断可以用于异步串口通信等。在STM32应用开发中,合理使用中断机制可以提高系统的效率和灵活性,并可以满足各种不同的需求和应用场景。因此,深入理解STM32中断的原理、类型和应用非常重要,对于开发高质量的STM32应用具有重要意义。

11.3.1 外部中断:外部触发引脚、按键等

11.3.1.1 EXTI的简介

        EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

11.3.1.2 EXTI 功能框图

        EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体的把握,在编程时思路就非常清晰。 EXTI 功能框图见图 EXTI 功能框图。在图 EXTI 功能框图 可以看到很多在信号线上打一个斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有 20 个,这与 EXTI 总共有 20 个中断/事件线是吻合的。所以我们只要明白
其中一个的原理,那其他 19 个线路原理也就知道了。

第十一章 STM32中断应用_第6张图片

框图解读:

        ①输入线:EXTI有19个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设事件。

        ②边沿检测电路:它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿出发选择器(EXTI_FTSR)对应的设置来控制信号触发。

        边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1,给③电路,否则输出无效信号0,而EXTI_RTSR和EXTI_FTSR两个寄存器可以控制需要检测哪些类型的电平跳变过程,可以是只有上升、下降沿触发,或者上升沿和下降沿都触发。

第十一章 STM32中断应用_第7张图片

第十一章 STM32中断应用_第8张图片

         ③电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器 (EXTI_SWIER)。 EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1就可以输出 1 给④和⑥电路。

第十一章 STM32中断应用_第9张图片

          ④一个与门电路,它的两个输入分别是③电路的输出和中断屏蔽寄存器EXTI_IMR根据与门的逻辑特性“输入全为1时输出才为1”这就导致当EXTI_IMR.输出为0时不管③电路输出是什么④电路都输出0如果EXTI_IMR设置为1时,那么④电路的输出只由③电路的输出信号决定,这样我们就可以通过控制EXTI_IMR来实现是否产生中断的目的。

第十一章 STM32中断应用_第10张图片

         ⑤编号④电路输出的信号会被保存到挂起寄存器EXTI_PR内,如果确定④电路输出为1就全把EXTI_PR对立位置写1,⑤电路是将EXTI_PR寄存器内容输出到NVIC内从而实现系统中断事件控制。

第十一章 STM32中断应用_第11张图片

          ⑥一个与门两个输入分别来自③电路的输出另一个由事件屏蔽寄存器产生EXTI_EMR。如果EXTI_EMR设置为0.那么不管③电路输出是什么最终⑥与门结果都为0,如果EXTI_ENR设置为1时,⑥与门的输出信号与③电路输出信号有关,这样就可通过控制EXTI_EMR来实现是否产生事件的目的。

第十一章 STM32中断应用_第12张图片

        ⑦是一个脉冲发生器,当它的输入端即⑥的输出端,是一个有效信号1时就会产生一个脉冲,如果输入端是无效信号就不会输出脉冲。

        ⑧脉冲信号,产生事件线路的最终产物,这些脉冲可以给其他一些外设电路使用

11.3.1.3 中断/事件控制线

        EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另外七根用于特定的外设事件,见表 EXTI 中断 _ 事件线。4根特定外设中断/事件线由外设触发,具体用法参考《STM32F10X-中文参考手册》中对外设的具体说明。

第十一章 STM32中断应用_第13张图片

 EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。由表EXTI 中断 _ 事件线 可知, EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1) 的EXTI0[3:0] 位选择配置为 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、 PG0、 PH0 或者 PI0,见图 EXTI0 输入源选择。其他 EXTI 线 (EXTI 中断/事件线) 使用配置都是类似的。

第十一章 STM32中断应用_第14张图片

11.3.2 定时器中断:定时器的基本原理和使用方法

        STM32定时器中断是指在STM32微控制器的定时器计数器到达预设值时触发的中断。这种中断可以用来实现许多应用,如精确测量时间、调整PWM输出、生成周期性事件等。

        STM32微控制器通常配备了多个定时器,每个定时器都有自己的计数器和配置寄存器。要使用定时器中断,需要先通过配置寄存器设置定时器的预分频器、计数模式和计数器比较值等参数。然后启用定时器中断,并在中断服务程序中实现所需的操作。

        在中断服务程序中,可以读取或写入定时器寄存器来获取或修改计数器值,也可以执行其他操作,如切换GPIO引脚状态、修改变量值等。

        在使用定时器中断时,需要注意保持中断服务程序的实现简洁高效,避免占用过多CPU时间。此外,还需要根据具体应用场景选择合适的定时器配置参数和中断触发条件,以满足性能和功耗等要求。

        这里不过多参入定时器的内容,定时器在后面的内容中还会单独介绍。

11.3.3 DMA中断:DMA的基本原理和使用方法

        STM32 DMA中断是指在STM32微控制器的DMA传输完成时触发的中断。使用DMA(Direct Memory Access,直接内存访问)可以在不占用CPU时间的情况下,实现大量数据的高速传输。通过启用DMA中断,可以在DMA传输完成后及时地处理传输结果,以达到更加灵活和高效的数据传输方式。

        在STM32微控制器中,DMA是由DMA控制器单独管理的。它可以向外部设备或内部存储器执行数据传输操作,而无需CPU参与。对于每个DMA通道,都有自己的配置寄存器和状态寄存器。需要使用这些寄存器来配置DMA传输参数,如数据长度、传输方向、源地址和目的地址等,并启用DMA中断。

        当DMA传输完成后,触发DMA中断,在中断服务程序中可以读取或写入DMA寄存器来获取或修改DMA状态,也可以执行其他操作,如切换GPIO引脚状态、修改变量值等。需要注意,由于DMA操作涉及内存和外设的数据交换,因此需要保证数据传输的正确性和可靠性。

        在使用DMA中断时,需要根据具体应用场景选择合适的DMA通道和传输参数,并合理设置中断触发条件和优先级。同时,还需要优化中断服务程序的实现,避免占用过多CPU时间,以充分发挥DMA传输的高效性。

11.3.4 其他中断的类型

11.3.4.1 USART中断

        USART中断是指在STM32微控制器的USART模块接收或发送数据完成后触发的中断。USART是一种通用异步收发传输机制,可用于串口通信、调试输出等应用场景。启用USART中断可以实现对USART数据接收和发送的及时处理,提高应用程序的可靠性和灵活性。

        在STM32微控制器中,USART是由USART控制器单独管理的。它可以设置工作参数,如波特率、字符长度、停止位数等,并启用USART中断。在USART数据传输过程中,当有新的数据接收完成或者发送完成,就会触发相应的USART中断,在中断服务程序中可以读取或写入USART寄存器来获取或修改USART状态和数据,也可以执行其他操作,如切换GPIO引脚状态、修改变量值等。

        在使用USART中断时,需要注意保持中断服务程序的实现简洁高效,避免占用过多CPU时间。此外,还需要根据具体应用场景选择合适的USART工作参数和中断触发条件,以满足性能和功耗等要求。

        在USART数据传输过程中,需要考虑数据的正确性和可靠性。例如,如果不同设备之间的USART工作参数不匹配,则可能导致数据传输出错;如果USART缓冲区溢出,则可能丢失数据。因此,需要合理设置USART工作参数和缓冲区大小,并定期检查USART状态和错误标志,以确保数据传输的正确性和可靠性。

11.3.4.1 SPI中断

        SPI中断是指在STM32微控制器的SPI模块接收或发送数据完成后触发的中断。SPI(Serial Peripheral Interface,串行外设接口)是一种通用的串行通信协议,可用于微控制器与外部设备之间的通信。启用SPI中断可以实现对SPI数据接收和发送的及时处理,提高应用程序的可靠性和灵活性。

        在STM32微控制器中,SPI是由SPI控制器单独管理的。它可以设置工作参数,如数据传输格式、字节顺序、时钟速度等,并启用SPI中断。在SPI数据传输过程中,当有新的数据接收完成或者发送完成,就会触发相应的SPI中断,在中断服务程序中可以读取或写入SPI寄存器来获取或修改SPI状态和数据,也可以执行其他操作,如切换GPIO引脚状态、修改变量值等。

        在使用SPI中断时,需要注意保持中断服务程序的实现简洁高效,避免占用过多CPU时间。此外,还需要根据具体应用场景选择合适的SPI工作参数和中断触发条件,以满足性能和功耗等要求。

        在SPI数据传输过程中,需要考虑数据的正确性和可靠性。例如,如果不同设备之间的SPI工作参数不匹配,则可能导致数据传输出错;如果SPI缓冲区溢出,则可能丢失数据。因此,需要合理设置SPI工作参数和缓冲区大小,并定期检查SPI状态和错误标志,以确保数据传输的正确性和可靠性。

11.4 中断处理函数

        中断处理函数是一种特殊的函数,用于响应外部中断或异常事件。在STM32微控制器中,中断处理函数通常由硬件自动调用,在中断事件发生时立即执行。中断处理函数需要快速响应和处理中断请求,并确保系统正常运行。

        中断处理函数通常包括以下几个方面:

        中断服务程序入口:中断处理函数必须具有正确的函数声明和命名方式,以便编译器正确识别和生成相应的中断服务程序入口。在STM32微控制器中,中断处理函数通常采用以下格式:

void EXTI0_IRQHandler(void)
{
	//中断服务程序代码
}

其中,EXTI0_IRQHandler表示外部中断线0的中断服务程序入口。需要根据具体应用场景选择相应的中断服务程序入口。

        中断标志位清除:在进入中断处理函数之前,需要先清除相应的中断标志位,以避免重复中断和误判中断。在STM32微控制器中,可以通过以下方式清除中断标志位:

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line0);
		// 中断服务程序代码
	}
}

其中,EXTI_GetITStatus函数用于检查中断标志位是否已经被置位,EXTI_Line0表示外部中断线0,EXTI_ClearITPendingBit函数用于清除中断标志位并退出中断。

        中断服务程序代码:中断服务程序代码是实现中断响应和处理的核心部分。需要根据具体应用场景编写相应的中断服务程序代码,以确保系统正常运行。在编写中断服务程序代码时,需要注意以下几个方面:

        快速响应:中断响应时间必须要足够快,以避免延迟和数据丢失等问题。

        简单可靠:中断服务程序代码必须简单易懂,并尽量避免复杂的操作和处理流程,以提高中断处理效率和可靠性。

        避免阻塞:中断服务程序代码不应该使用阻塞的函数或方法,以避免影响其他任务的执行或导致系统死锁。

        注意同步:中断服务程序代码需要正确地管理共享资源,避免资源竞争和数据不一致等问题。

        中断服务程序退出:在中断服务程序执行完毕后,需要正确退出中断并返回到主程序或其他任务。在STM32微控制器中,可以通过以下方式退出中断:

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line0);
		// 中断服务程序代码
	}
	NVIC_ClearPendingIRQ(EXTI0_IRQn);
}

其中,NVIC_ClearPendingIRQ函数用于清除中断挂起位并退出中断。

        综上所述,中断处理函数是一种特殊的函数,用于响应外部中断或异常事件。需要根据具体应用场景编写相应的中断服务程序代码,并注意快速响应、简单可靠、避免阻塞和注意同步等要点。

11.4.1 中断处理函数与主函数的关系和区别

        中断处理函数和主函数是STM32系统中两个重要的部分,它们之间有以下几个关系和区别:

        触发方式:中断处理函数是响应外部中断或异常事件时自动触发的函数,而主函数是根据程序设计流程,在特定条件下由系统调用的函数。中断处理函数与外部事件相关联,而主函数则不一定。

        执行时间:中断处理函数需要快速响应和执行,以处理相应的事件。而主函数则不需要那么迅速地响应和执行。中断服务程序应该尽量简短、快速,避免在其中执行复杂的操作或长时间等待。

        上下文环境:中断处理函数和主函数的上下文环境不同。中断处理函数会打断当前的程序执行,切换到中断上下文环境,保存现场信息和状态,并执行相应的中断服务程序。而主函数则是在正常程序执行下运行,没有上下文切换的开销。

        调用方式:中断处理函数是由硬件自动调用的,不能人工干预。而主函数则可以在程序设计中人为调用,完成相应的操作。

        功能作用:中断处理函数通常用来处理外部中断事件,例如GPIO中断、定时器中断等,处理这些事件通常需要对某些寄存器进行读写。而主函数通常用于完成主要的业务逻辑,例如初始化系统、处理数据等。

        任务优先级:中断处理函数的优先级比主函数高。当一个中断发生时,中断处理函数会立即被执行,而主函数需要等到中断结束后才能被执行。

        综上所述,中断处理函数和主函数在STM32系统中具有不同的作用和特点。中断处理函数是响应外部中断或异常事件的函数,需要快速响应和执行。而主函数则是完成主要的业务逻辑的函数,可以根据程序设计流程人为调用。

11.5 中断优化和注意事项

中断响应时间和中断处理时间的优化方法

        中断响应时间和中断处理时间是影响STM32系统性能的重要因素。以下是优化中断响应时间和中断处理时间的一些方法:

        选择合适的中断优先级,在STM32微控制器中,中断按照优先级进行处理。对于高优先级中断,会打断正在执行的低优先级中断或主程序,并立即响应。因此,需要根据具体应用场景选择合适的中断优先级,以保证高优先级中断的及时响应与处理。

        减少中断嵌套层数,在STM32微控制器中,中断服务程序可能会嵌套调用其他中断服务程序。如果中断嵌套层数过多,会导致中断响应时间和中断处理时间增加,从而降低系统性能。因此,需要尽量减少中断嵌套层数,避免不必要的中断服务程序调用。

        简化中断服务程序代码,中断服务程序的执行时间应尽可能短,不能占用太多CPU时间。因此,需要简化中断服务程序代码,尽量避免复杂的操作和处理流程,同时减少对全局变量和堆栈的访问,以提高中断处理效率。

        使用DMA传输,如果需要处理大量数据传输,可以使用DMA(Direct Memory Access,直接内存访问)传输来减少CPU的占用时间。在使用DMA传输时,需要合理设置DMA通道和传输参数,并注意保证数据传输的正确性和可靠性。

        使用中断控制器优化中断处理时间,STM32微控制器中的中断控制器提供了一些优化中断处理时间的方法,如中断向量表重映射、多级中断管家、中断节流等。通过合理配置中断控制器,可以进一步提高系统性能和可靠性。

        综上所述,优化中断响应时间和中断处理时间是提高STM32系统性能的关键因素之一。需要根据具体应用场景选择合适的方法和技术,以满足性能和功耗等要求。

中断嵌套和中断优先级的注意事项和实践经验

        中断嵌套和中断优先级是影响STM32系统性能和稳定性的重要因素。以下是一些注意事项和实践经验:

        理解中断嵌套和中断优先级的工作原理

        在STM32微控制器中,中断按照优先级进行处理,高优先级的中断可以打断正在执行的低优先级中断或主程序,并立即响应。中断嵌套是指在一个中断服务程序中调用了其他中断服务程序,使得当前中断被打断并进入新的中断服务程序。需要理解这些特性,以便正确设置中断优先级并避免中断嵌套层数过多。

        合理设置中断优先级,需要根据具体应用场景选择合适的中断优先级,以保证高优先级中断的及时响应与处理。通常情况下,需要将系统关键模块的中断设置为高优先级,例如时钟、DMA传输等模块。

        避免中断嵌套层数过多,如果中断嵌套层数过多,会导致中断响应时间和中断处理时间增加,从而降低系统性能。因此,需要尽量减少中断嵌套层数,避免不必要的中断服务程序调用。可以通过合理设置中断优先级、使用DMA传输等方式来减少中断嵌套层数。

        避免死锁和资源竞争,在使用中断处理程序时,需要避免死锁和资源竞争等问题。例如,在使用共享资源时,需要正确设置访问权限,并加入相应的同步机制,如信号量、互斥锁等。

        适当延迟中断,在一些实时系统中,可能需要对中断进行适当的延迟,以确保系统稳定性和可靠性。例如,在执行关键操作时,可以暂时关闭中断或延迟中断响应,以提高系统的响应能力和可靠性。

        综上所述,中断嵌套和中断优先级是STM32系统设计中重要的考虑因素之一。需要根据具体应用场景选择合适的方法和技术,以满足性能和稳定性要求。

11.6 EXTI中断控制实验

11.6.1 实验要求

        开发板上电后开始执行流水灯程序,当按下KEY1按键时流水灯程序暂停,蜂鸣器叫一声后程序继续执行流水灯。

11.6.2 硬件设计

第十一章 STM32中断应用_第15张图片

11.6.3 软件设计

11.6.3.1 编程思路

        首先使能I/O口时钟,配置I/O口为输入模式,开启AFIO时钟设置I/O口与中断线的映射关系(将GPIO端口映射到EXTI中断线上),配置中断分组(NVIC)使能中断,初始化EXTI选择出发方式,编写中断服务函数,编写外部中断控制程序。

11.6.3.2 EXTI中断端口宏定义

#ifndef _BSP_EXTI_H 

#define _BSP_EXTI_H

#include "stm32f10x.h"

#define EXTI_GPIO_LINE			EXTI_Line0
#define EXTI_GPIO_CLK			RCC_APB2Periph_AFIO

#define EXTI_NVIC_IRChannl		EXTI0_IRQn

#define EXTI_GPIO_PORT			GPIO_PortSourceGPIOA
#define EXTI_GPIO_PIN			GPIO_PinSource0

void EXTI_GPIO_Config(void);

#endif /* _BSP_EXTI_H */

11.6.3.3 EXTI中断初始化函数

#include "bsp_exti.h"

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI_NVIC_IRChannl;
	
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; 
		NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStruct);
}

void EXTI_GPIO_Config(void)
{
	EXTI_InitTypeDef EXTI_InitStruct;
	
	RCC_APB2PeriphClockCmd(EXTI_GPIO_CLK,ENABLE);
	
	EXTI_NVIC_Config();
	
	GPIO_EXTILineConfig(EXTI_GPIO_PORT,EXTI_GPIO_PIN);
	
	EXTI_InitStruct.EXTI_Line = EXTI_GPIO_LINE;
	
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	
	EXTI_Init(&EXTI_InitStruct);
}

11.6.3.4 编写终端服务函数

#include "stm32f10x_it.h"
#include "bsp_buzzer.h"
#include "bsp_exti.h"
#include "bsp_led.h"void  
EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_GPIO_LINE) != RESET)
	{
		BUZZER_CRY(BUZZER_ON);
		delay(0XFFFFF);
		BUZZER_CRY(BUZZER_OFF);
		
		EXTI_ClearITPendingBit(EXTI_GPIO_LINE);
	}
}

11.6.3.5 主函数

#include "stm32f10x.h"
#include "bsp_buzzer.h"
#include "bsp_button.h"
#include "bsp_led.h"
#include "bsp_exti.h"

#define SOFT_DELAY	delay(0XFFFFF)

int main(void)
{
	//蜂鸣器外设初始化
	BUZZER_GPIO_Config();
	
	//按键外设初始化
	BUTTON_GPIO_Config();
	
	//LED灯初始化
	LED_GPIO_Config(); 
	//中断初始化
	EXTI_GPIO_Config();
	
	while(1)
	{
		//红灯
		LED_RED;
		SOFT_DELAY;
		
		//绿灯
		LED_GREEN;
		SOFT_DELAY;
		
		//蓝灯
		LED_BLUE;
		SOFT_DELAY;
	}
}

11.6.4 下载验证

        开发板上电后开始执行流水灯程序,当按下KEY1按键时流水灯程序暂停,蜂鸣器叫一声后程序继续执行流水灯。

        注:AFIO_EXTICEX低16位有用,高16位保留。低16位可通过软件读写用于选择外部中断输入源。

        static关键字用于函数时,会将该函数的作用域限制在当前源文件中,无法被其他源文件调用。这样可以避免函数名冲突和符号表污染等问题。此外,由于该函数不需要与其他文件共享,编译器可以进行更优化的代码生成。

谢谢阅读!

你可能感兴趣的:(STM32开发基础,stm32,单片机,嵌入式硬件)