3、中断服务

中断服务历程

中断处理是计算机系统中的一种重要机制,用于处理异步事件或请求,如硬件中断、软件异常等。中断处理的基本原理是通过一个中断向量表来确定中断源,并执行相应的中断处理程序。在操作系统层面,这通常是通过中断服务例程(ISR)来实现的。

基本原理如下:

  1. 中断源:计算机系统中的各个硬件组件(如外设、时钟、网络适配器等)都可以产生中断信号。当某个硬件组件需要 CPU 处理时,它会发出一个中断请求。

  2. 中断控制器:中断信号通常由中断控制器收集和管理。中断控制器是一个硬件组件,负责协调各个中断源,并向 CPU 发送中断请求。

  3. 中断向量表:操作系统维护了一个中断向量表,其中包含了一系列中断向量(或中断号),每个中断向量对应一个中断处理程序的地址。

  4. 中断处理程序:每个中断向量都关联一个中断处理程序,也称为中断服务例程 (ISR)。中断处理程序是一段特定的代码,它处理特定类型的中断。当中断请求被接受后,CPU会根据中断向量找到对应的中                      断处理程序,并执行它。

  5. 中断服务例程注册:操作系统或应用程序可以注册中断服务例程,告诉系统在特定类型的中断发生时应该执行哪个处理程序。

  6. 中断处理过程:当中断请求到达 CPU 时,CPU会检查中断向量,并执行与之相关联的中断处理程序。中断处理程序负责处理中断,可能会采取一些操作,然后恢复正常的执行。

  7. 中断完成:一旦中断处理程序执行完毕,系统会继续执行之前的任务。在处理硬件中断时,通常还需要向中断控制器发送中断完成信号,以允许中断控制器继续处理其他中断请求。

通过上述机制,系统可以根据中断向量表中的信息来确定应该执行哪个中断处理程序,而不需要在每个中断请求到来时手动指定执行的代码。这使得中断处理更加高效、可扩展和可维护。

 

中断控制器的结构体定义

 

 

为什么我们在isr.c中写一个函数,处理器就能自动找到与其对应的中断类型呢?

在 STM32 处理器中,中断服务函数 (Interrupt Service Routine, ISR) 是用来处理特定中断事件的函数。这些函数是用户定义的,用于响应不同类型的中断请求,如外部硬件中断、定时器中断等。当中断事件发生时,处理器会自动跳转到相应的中断服务函数来执行相应的操作。为了让处理器知道哪个中断与哪个函数相关联,需要进行以下配置:

  1. 中断向量表 (Interrupt Vector Table): 在嵌入式系统中,有一个中断向量表,它是一个存储着中断服务函数地址的表格。不同的中断号(或中断优先级)会映射到不同的表项。当一个中断事件发生时,处理器会查找中断号对应的中断向量表项,然后跳转到相应的中断服务函数地址开始执行。

  2. 中断优先级和中断控制器 (NVIC, Nested Vectored Interrupt Controller): STM32 处理器使用 NVIC 控制器来管理中断。在 NVIC 中,你可以配置中断的优先级,使得一些中断比其他中断更具优先级。这是为了确保在多个中断同时发生时,处理器能够正确响应最紧急的中断。中断服务函数的执行顺序是由中断优先级决定的。

  3. 中断服务函数的命名和关联: 为了关联一个中断服务函数和特定的中断号或优先级,需要按照一定的规则给中断服务函数命名。在 STM32 HAL 库中,这些规则通常是固定的,比如命名为 void EXTI0_IRQHandler(void) 的函数,其中 EXTI0 表示外部中断线 0 的中断服务函数。处理器会根据这些规则找到正确的中断服务函数。

综合上述,当你在 isr.c 中编写一个中断服务函数,并按照正确的命名规则,同时在中断向量表中配置好对应的中断号和中断优先级,处理器就会自动关联中断服务函数和中断事件。当中断事件发生时,处理器会自动跳转到正确的中断服务函数执行相应的操作。这种机制使得处理中断变得更加方便和高效,同时也提高了代码的可维护性。

 

我们不能随意定义isr.c中的函数名称

在很多嵌入式系统和操作系统中,中断向量表是一个包含中断处理程序入口地址的数据结构。这些入口地址通常指向中断服务子程序(ISR)或中断处理函数,这些函数通常是按照特定的命名规则定义的。

当特定中断发生时,系统会查找中断向量表,找到对应的入口,然后跳转到该入口,以执行相应的中断处理程序。

因此,确保 ISR 函数的名称与中断向量表中的入口名称相匹配是关键的,这样系统知道应该执行哪个处理程序来处理特定的中断。如果 ISR 函数的名称不匹配,系统将无法正确地关联中断源和处理程序,从而导致中断处理错误或无法正常工作。

从c语言角度来理解这种模式,及中断向量表中定义了一个label,而函数名本身其实是一个地址,我们将这个label作为函数名就可完成只编写一个函数,系统自动处理的操作。

 

 

在STM32系列微控制器中使用中断的过程如下:

1. 配置NVIC:首先,需要配置NVIC(Nested Vector Interrupt Controller),使能中断。可以使用`NVIC_SetPriority()`函数设置中断优先级,使用`NVIC_EnableIRQ()`函数使能中断。

2. 配置外部中断线:如果使用的是外部中断(例如GPIO中断),就需要设置外部中断线的触发方式和使能中断。可以使用`EXTI_Init()`函数进行配置。

3. 编写中断处理函数:编写中断处理函数来处理中断事件。中断处理函数需要依据中断类型进行相应的处理。

4. 注册中断处理函数:使用`NVIC_SetVector()`函数将中断处理函数注册到相应的中断向量表中。

5. 启动中断:使用`NVIC_EnableIRQ()`函数启动中断。

6. 等待中断:等待中断事件的触发。

需要注意的是,使用中断时需要配置中断优先级。较高优先级的中断可以打断较低优先级的中断。还需要注意对共享资源的访问控制,以避免竞争条件的产生。

总结起来,使用中断的过程包括配置NVIC、配置外部中断线、编写中断处理函数、注册中断处理函数、启动中断和等待中断。具体的配置和编写会根据不同的中断类型和应用场景有所不同。详细的操作可以参考相应的STM32系列微控制器的参考手册和库文件的文档。

 

 

中断使能

table_irq_uart其实是一个数组,采用查表的方式去找中断号。

IRQn_Type table_irq_uart[3] = {USART1_IRQn, USART2_IRQn, USART3_IRQn};

 

 

 #define NVIC_EnableIRQ              __NVIC_EnableIRQ

 

参数IRQn是中断号(IRQn_Type类型),用于指定要使能的中断。

函数的作用是使能指定的中断。具体的实现如下:

  1. 首先,通过判断IRQn是否大于等于0来检查传入的中断号是否是有效的。

  2. 如果中断号有效,则计算中断号对应的寄存器索引:(((uint32_t)IRQn) >> 5UL)

    • >> 5UL表示右移5位,相当于除以32,用于确定该中断号在NVIC->ISER寄存器数组中的索引。

  3. 然后,设置中断使能寄存器对应位置的位,通过NVIC->ISER[索引] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));实现。

    • 1UL << (((uint32_t)IRQn) & 0x1FUL)表示将1左移对应的位数,通过按位与操作(& 0x1FUL)确保位数不超过31,用于设置中断使能位。

该函数的主要功能是根据传入的中断号,设置中断使能寄存器,从而使得指定的中断得以触发。在使用该函数前,需要先确认对应的中断号是否正确,并确保中断号对应的硬件资源和中断处理程序正确配置。

 

ISER(Interrupt Set Enable Register)寄存器是嵌入式系统中的一种特殊寄存器,用于控制中断的使能状态。

在ARM Cortex-M微控制器中,ISER寄存器是Nested Vector Interrupt Controller(NVIC)的一部分。NVIC是用于管理中断的模块,负责管理中断的优先级、中断向量表和中断使能等功能。

ISER寄存器是一个32位的寄存器,每一位对应一个中断号,用于表示对应中断是否被使能。当某个中断的对应位被设置为1时,表示该中断被使能。当对应位被清零时,表示该中断被禁止。

ISER寄存器按照中断号的范围进行分组,每组32个中断号,共有8个ISER寄存器。以STM32为例,ISER寄存器数组为`NVIC->ISER[8]`,索引从0到7,分别对应ISER0到ISER7寄存器。

通过写入1到ISER寄存器的对应位,可以使能特定的中断。使能中断后,当中断条件满足时,对应的中断处理程序将被触发执行。

 

两张图中的IRQn_Type类型是一个枚举类型,其实本质也是一个查表的方式寻找中断号。其中37-39是我们串口相关的中断。

/**
 * @brief STM32L4XX Interrupt Number Definition, according to the selected device
 *        in @ref Library_configuration_section
 */
typedef enum
{
/******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Cortex-M4 Non Maskable Interrupt                                */
  HardFault_IRQn              = -13,    /*!< 3 Cortex-M4 Hard Fault Interrupt                                  */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt                                   */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt                                 */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M4 SV Call Interrupt                                    */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt                              */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt                                    */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick Interrupt                                */
/******  STM32 specific Interrupt Numbers **********************************************************************/
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                                         */
  PVD_PVM_IRQn                = 1,      /*!< PVD/PVM3/PVM4 through EXTI Line detection Interrupts              */
  TAMP_STAMP_IRQn             = 2,      /*!< Tamper and TimeStamp interrupts through the EXTI line             */
  RTC_WKUP_IRQn               = 3,      /*!< RTC Wakeup interrupt through the EXTI line                        */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                                            */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                              */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                              */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                              */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                              */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                              */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                              */
  DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                                   */
  DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                                   */
  DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                                   */
  DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                                   */
  DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                                   */
  DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                                   */
  DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                                   */
  ADC1_IRQn                   = 18,     /*!< ADC1 global Interrupt                                             */
  CAN1_TX_IRQn                = 19,     /*!< CAN1 TX Interrupt                                                 */
  CAN1_RX0_IRQn               = 20,     /*!< CAN1 RX0 Interrupt                                                */
  CAN1_RX1_IRQn               = 21,     /*!< CAN1 RX1 Interrupt                                                */
  CAN1_SCE_IRQn               = 22,     /*!< CAN1 SCE Interrupt                                                */
  EXTI9_5_IRQn                = 23,     /*!< External Line[9:5] Interrupts                                     */
  TIM1_BRK_TIM15_IRQn         = 24,     /*!< TIM1 Break interrupt and TIM15 global interrupt                   */
  TIM1_UP_TIM16_IRQn          = 25,     /*!< TIM1 Update Interrupt and TIM16 global interrupt                  */
  TIM1_TRG_COM_IRQn           = 26,     /*!< TIM1 Trigger and Commutation Interrupt                            */
  TIM1_CC_IRQn                = 27,     /*!< TIM1 Capture Compare Interrupt                                    */
  TIM2_IRQn                   = 28,     /*!< TIM2 global Interrupt                                             */
  I2C1_EV_IRQn                = 31,     /*!< I2C1 Event Interrupt                                              */
  I2C1_ER_IRQn                = 32,     /*!< I2C1 Error Interrupt                                              */
  I2C2_EV_IRQn                = 33,     /*!< I2C2 Event Interrupt                                              */
  I2C2_ER_IRQn                = 34,     /*!< I2C2 Error Interrupt                                              */
  SPI1_IRQn                   = 35,     /*!< SPI1 global Interrupt                                             */
  SPI2_IRQn                   = 36,     /*!< SPI2 global Interrupt                                             */
  USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                                           */
  USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                                           */
  USART3_IRQn                 = 39,     /*!< USART3 global Interrupt                                           */
  EXTI15_10_IRQn              = 40,     /*!< External Line[15:10] Interrupts                                   */
  RTC_Alarm_IRQn              = 41,     /*!< RTC Alarm (A and B) through EXTI Line Interrupt                   */
  SDMMC1_IRQn                 = 49,     /*!< SDMMC1 global Interrupt                                           */
  SPI3_IRQn                   = 51,     /*!< SPI3 global Interrupt                                             */
  TIM6_DAC_IRQn               = 54,     /*!< TIM6 global and DAC1&2 underrun error  interrupts                 */
  TIM7_IRQn                   = 55,     /*!< TIM7 global interrupt                                             */
  DMA2_Channel1_IRQn          = 56,     /*!< DMA2 Channel 1 global Interrupt                                   */
  DMA2_Channel2_IRQn          = 57,     /*!< DMA2 Channel 2 global Interrupt                                   */
  DMA2_Channel3_IRQn          = 58,     /*!< DMA2 Channel 3 global Interrupt                                   */
  DMA2_Channel4_IRQn          = 59,     /*!< DMA2 Channel 4 global Interrupt                                   */
  DMA2_Channel5_IRQn          = 60,     /*!< DMA2 Channel 5 global Interrupt                                   */
  COMP_IRQn                   = 64,     /*!< COMP1 and COMP2 Interrupts                                        */
  LPTIM1_IRQn                 = 65,     /*!< LP TIM1 interrupt                                                 */
  LPTIM2_IRQn                 = 66,     /*!< LP TIM2 interrupt                                                 */
  DMA2_Channel6_IRQn          = 68,     /*!< DMA2 Channel 6 global interrupt                                   */
  DMA2_Channel7_IRQn          = 69,     /*!< DMA2 Channel 7 global interrupt                                   */
  LPUART1_IRQn                = 70,     /*!< LP UART1 interrupt                                                */
  QUADSPI_IRQn                = 71,     /*!< Quad SPI global interrupt                                         */
  I2C3_EV_IRQn                = 72,     /*!< I2C3 event interrupt                                              */
  I2C3_ER_IRQn                = 73,     /*!< I2C3 error interrupt                                              */
  SAI1_IRQn                   = 74,     /*!< Serial Audio Interface 1 global interrupt                         */
  SWPMI1_IRQn                 = 76,     /*!< Serial Wire Interface 1 global interrupt                          */
  TSC_IRQn                    = 77,     /*!< Touch Sense Controller global interrupt                           */
  RNG_IRQn                    = 80,     /*!< RNG global interrupt                                              */
  FPU_IRQn                    = 81,     /*!< FPU global interrupt                                              */
  CRS_IRQn                    = 82      /*!< CRS global interrupt                                              */
} IRQn_Type;

 

在代码中关闭总中断的操作(DISABLE_INTERRUPTS)通常是为了确保某个操作的原子性和可靠性。

当需要在特定的代码片段中执行一些关键操作时,有时需要禁用所有中断。这样可以防止在该关键代码执行期间发生中断处理程序的干扰,确保该关键操作的执行不被中断打断。

禁用总中断可以避免以下情况的发生:

1. 竞态条件(Race Condition):当多个中断和主循环代码同时访问共享资源时,可能会导致竞态条件,即数据被非预期地修改。通过禁用中断,可以避免中断处理程序和主循环代码同时对共享资源进行访问,从而消除竞态条件的风险。

2. 实时性要求:某些操作需要在严格的时间限制下完成,不能被其他中断或任务打断。通过禁用中断,可以确保关键操作不会被其他中断的触发或任务的调度打断,从而满足实时性的要求。

禁用总中断可能会带来一些不便,例如无法及时响应其他中断事件或任务的调度等。因此,在禁用总中断时,需要谨慎考虑代码的执行时间和对实时性的要求。一般来说,应尽量将关键操作的执行时间控制在较短的范围内,以减少对系统其他部分的影响。

综上所述,关闭总中断的操作常用于确保关键操作的原子性和可靠性,以避免竞态条件和满足实时性要求。

 

 

关于以下代码中的初始化时关总中断

在一般情况下,初始化操作并不需要保证原子性。初始化操作的目标是对系统的各个部分进行初始化,通常在系统启动阶段或特定的初始化函数中执行。

初始化操作一般在单线程环境中进行,并且不涉及对共享资源的访问。因此,不需要禁用总中断或使用其他机制来保证初始化操作的原子性。

但是,在某些特殊的情况下,如果初始化操作涉及多个线程或任务的并发执行,或者涉及对共享资源的初始化,那么可能需要考虑保证初始化操作的原子性。

在这种情况下,可以采用以下方法来保证初始化操作的原子性:

1. 使用互斥锁(Mutex):通过在初始化操作之前获取互斥锁,并在初始化操作完成后释放互斥锁,确保只有一个线程或任务可以执行初始化操作。

2. 使用原子操作:某些处理器提供原子操作指令,可以保证对特定内存位置的读写操作是原子的,从而避免竞态条件的发生。

3. 禁用中断:在某些特殊的场景下,如果初始化操作涉及对共享资源的访问,并且中断可能会干扰初始化操作的执行,可以考虑在初始化操作期间禁用中断,以确保初始化操作的原子性。

需要根据具体情况来决定是否需要保证初始化操作的原子性。一般来说,对于单线程环境下的简单初始化操作,不需要额外的措施来保证原子性。但是,在多线程、多任务环境或对共享资源的初始化操作中,需要谨慎考虑原子性的问题,并采取相应的措施来保证初始化操作的正确执行。

 

//【不变】关总中断
    DISABLE_INTERRUPTS;

// 用户外设模块初始化
    gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON);    //初始化蓝灯
    
    uart_init(UART_User,115200);     //初始化串口模块   
  
// 使能模块中断
     uart_enable_re_int(UART_User);  //使能UART_USER模块接收中断功能
//【不变】开总中断
    ENABLE_INTERRUPTS;

 

总结:

  当我们要使用某个特定中断的时候,首先要在main.c里面进行初始化,初始化内容包括初始化引脚和使能中断, 然后我们要按照规范和约定编写特定的中断处理函数。

  在程序运行时,当处理器收到了中断信号,首先会确定中断源,然后根据我们对中断控制器的配置和中断向量表执行中断处理程序(也就是我们根据特定的命名规则编写在isr.c中的程序)。中断处理结束后,程序返回main函数中继续执行。

posted @ 2023-10-21 10:53  深渊之巅  阅读(282)  评论(0编辑  收藏  举报