XuSenfeng

个人站

复读了,更新随缘,有的文件不全或者图片缺失具体看我的笔记库(https://github.com/XuSenfeng/note)


中断

目录

中断

中断有优先级, 数字越小优先级越高

  • 系统异常: 内核水平
  • 外部中断: 外设水平

有10个系统异常, 60个外设中断

image-20230628180451294

image-20230628180512907

控制

NVIC: 嵌套向量中断控制器, 属于内核外设

在文件STM32F10xxxx Cortex-M3编程手册中

内核的寄存器定义在core_cm3.h文件

typedef struct
{
  __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt 使能寄存器           */
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt 清除         */
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt 使能悬起          */
       uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt 清除悬起        */
       uint32_t RESERVED3[24];                                   
  __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt 有效位寄存器           */
       uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt 中断优先级寄存器,8位有效 */
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                         /*!< Offset: 0xE00  软件触发中断寄存器     */
}  NVIC_Type;   

经过裁剪之后STM公司把它放在misc.h文件中

优先级设定

实际操作的是NVIC->IPRx寄存器, 一个寄存器8位

使用STM只使用高四位共64种, 又将它分为主优先级, 子优先级, 数字越小优先级越高, 都相等比较硬件中断编号

优先级还会进行分组, 设置为五个组, 控制寄存器表示的方法(主优先级和子优先级分别由几位进行控制), 使用寄存器SCB->AIRCR:PRIGROUP[10:8](系统控制块(System Control Block))

image-20230628183435658

编程顺序

  • 请求使能中断

外设使能, 控制外设相关寄存器 + 总开关NVIC中断使能寄存器

  • 配置优先级分组 有专门的函数进行配置

  • 配置NVIC寄存器

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) 配置要使能的中断*/

  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref 抢占优先级												   NVIC_Priority_Table 主优先级*/

  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref 															   NVIC_Priority_Table 次优先级*/

  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE 内核控制器												 是否同意产生中断*/   
} NVIC_InitTypeDef;
  • 编写中断服务函数

在中断向量表里面找到对应的函数名称

中断函数写错了会执行启动文件中的, 陷入循环, 函数全部在说stm32F10x_it.h文件中重新定义为空函数

实现GPIO中断(外部中断)

EXTI(外部中断/事件控制器)控制, 对应的是上面的中断函数中的EXTI触发的中断

AFIO: 复用功能I/O和调试配置

  • 每个中断都有专属的触发和屏蔽
  • 每个中断都有专属的状态位

image-20230628193325234

image-20230628193616469

中断为上面的一路, 最终的信号通入NVIC控制器

事件为下面的一路

①是输入线, EXTI有十九个中断/事件输入线, 输入线通过寄存器设置为任意一个GPIO, 也可以是一些外设的时间, 一般是存在电平变化的信号

②是一个检测电路, 根据上升沿或下降沿选择寄存器, 对应的位设置控制信号触发, 测到有边沿跳变就左侧输出有效信号 1 给编号 3 电路,否则输出无效信号 0。

③软件中断事件控制器EXTI_SWIER, 设置为1则与门设置为1, 之后会控制EXTI_PR寄存器对应的位为1设置为挂起, 之后会输出1

④EXIT_IMR中断屏蔽寄存器控制位, 设置为1的时候开放请求

⑤发送到内核产生中断

⑥EXTI_EMR事件屏蔽寄存器控制, 写1使能, 来到脉冲发生器, 产生脉冲, 之后会触发ADC产生信号, 或触发定时器等

EXTI可以控制20根外部的终端线, 每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另 外七根用于特定的外设事件,见表 EXTI 中断 _ 事件线 。有十六根外部中断线连接到EXTI的一根线上

image-20230628200901595

image-20230628194547668

第19根这个版本没有

image-20230628195224413

AFIO_EXTICR1等寄存器控制实际的对应的I/O

  • EXTI_RTSR上升沿触发选择

image-20230628195445820

  • EXTI_FTSR下降沿触发

image-20230628195511517

固件库

使用EXIT_InitTypeDef函数进行实现

typedef struct
{
  uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled.
                                         This parameter can be any combination of @ref EXTI_Lines 设置产生中断的线实										际上设置的是中断屏蔽寄存器*/
   
  EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef设置为事件还是中断 */

  EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef 设置出发的条件*/

  FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines.
                                         This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;

这里控制的是上面的框图里面的寄存器

之后使用void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);函数进行设置实际使用的引脚

思路总理

首先设置GPIO的触发中断的引脚使用函数GPIO_EXTILineConfig, 之后产生的信号会进入EXTI, 由EXTI初始化函数确定中断的进一步选择是触发中断还是产生事件, 最后进入NVIC在配置优先级之后会触发中断

  1. 初始化引脚为输入, 打开时钟
  2. 选择引脚复用, 由于属于引脚复用打开AFIO时钟(对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO 的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。)
  3. 初始化EXTI
  4. 初始化NVIC
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;
}

在初始化NVIC的通道的时候前四个EXTI有单独的通道, 后面的会使用这种定义

image-20230630154013370

区分不同的中断源需要进行相应的判断,通过取读EXTI->PR寄存器来判

  1. 中断服务函数

首先检查中断标志位, 之后处理, 最后清除标志位

//声明为静态函数, 只能在本文件中使用, 其他文件可以有同名字的函数, 如果声明的是变量之后不可以使用extern在其他文件调用
static void EXTI_NVIC_Config(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}


void EXTI_Key_config(void)
{


	//开启时钟
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLOCK, ENABLE);
	//初始化引脚
	GPIO_InitTypeDef GPIO_InitStructure;
	//初始化NVIC
	EXTI_NVIC_Config();
	
	
	GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
		
	GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
	//复用引脚,选择输入线
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	//初始化引脚
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;	//设置为EXTI0
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;	//使能
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;	//设置为中断
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
	EXTI_Init(&EXTI_InitStruct);
}

连接多个引脚相同的时候可以检测IDR寄存器