STM32学习笔记(1)中断

STM32学习笔记(1)中断

六月 25, 2021

本文章适用于STM32F103RBT6,其他型号可能有所不同

开发模板见我的GitHub仓库skyone-wzw@STM32F103RBT6-template

如果你觉得这篇文章有帮助,请star仓库或评论回复吧~

如果文章有任何错误(包括错别字)可以在这里提交Issue或邮件联系我

设置NVIC中断分组

用到的函数

NVIC_PriorityGroupConfig()

设置中断分组

可选:

  • NVIC_PriorityGroup_0

    抢占优先级0位,响应优先级4位

  • NVIC_PriorityGroup_1

    抢占优先级1位,响应优先级3位

  • NVIC_PriorityGroup_2

    抢占优先级2位,响应优先级2位

  • NVIC_PriorityGroup_3

    抢占优先级3位,响应优先级1位

  • NVIC_PriorityGroup_4

    抢占优先级4位,响应优先级0位

抢占优先级与响应优先级

这两个优先级都是数值越小,优先级越高

  • 当A中断抢占优先级高于B中断,不论响应优先级如何,如果中断同时发生,A中断先执行,B中断后执行;如果B中断正在执行,A中断发生,A中断可以打断B中断的执行,先执行A中断,再继续执行B中断。
  • 当A中断抢占优先级与B中断相等,如果A中断响应优先级高于B中断,如果中断同时发生,A中断先执行,B中断后执行;如果B中断正在执行,A中断发生,A中断不可以打断B中断的执行!

中断线和中断初始化

相关函数

GPIO_EXTILineConfig(GPIO_PortSource, GPIO_PinSource)

IO口与中断线连接

例如:

1
2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
// GPIOA_1 与中断线1连接

EXTI_Init(&EXTI_InitTypeDef)

定义中断类型,即当什么时候触发中断

对于结构体EXTI_InitTypeDef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct {
// 中断线:0~15
uint32_t EXTI_Line;
/* 选择模式:
* 产生中断(EXTI_Mode_Interrupt)
* 产生事件(EXTI_Mode_Event)
*/
EXTIMode_TypeDef EXTI_Mode;
/* 选择触发方式:
下降沿触发(EXTI_Trigger_Falling)
上升沿触发(EXTI_Trigger_Rising)
上升沿和下降沿都触发(EXTI_Trigger_Rising_Falling)
*/
EXTITrigger_TypeDef EXTI_Trigger;
// 使能中断线
FunctionalState EXTI_LineCmd;
} EXTI_InitTypeDef;

例如:

1
2
3
4
5
6
7
8
9
10
EXTI_InitTypeDef exti;
// 选择要设置的中断线
exti.EXTI_Line = EXTI_Line3;
// 选择模式:产生中断
exti.EXTI_Mode = EXTI_Mode_Interrupt;
// 选择触发方式为下降沿触发
exti.EXTI_Trigger = EXTI_Trigger_Falling;
// 使能
exit.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti);

中断线

中断线有16个:0~15

每组GPIOx都有16个IO,GPIOA_0GPIOB_0等分别对应中断线0,以此类推。

设置中断优先级

相关函数

NVIC_Init(&NVIC_InitTypeDef)

设置中断优先级

对应结构体NVIC_InitTypeDef

1
2
3
4
5
6
7
8
9
10
typedef struct {
// 设置中断通道
uint8_t NVIC_IRQChannel;
// 设置抢占优先级
uint8_t NVIC_IRQChannelPreemptionPriority;
// 设置响应优先级
uint8_t NVIC_IRQChannelSubPriority;
// 使能
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;

例如:

1
2
3
4
5
6
7
8
9
10
NVIC_InitTypeDef nvic;
// 设置中断通道为:第0通道
nvic.NVIC_IRQChannel = EXTI0_IRQn;
// 设置抢占优先级为:2
nvic.NVIC_IRQChannelPreemptionPriority = 0x02;
// 设置响应优先级为:3
nvic.NVIC_IRQChannelSubPriority = 0x03;
// 使能
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);

中断通道

中断通道一共7个:

  • EXTI0_IRQn
  • EXTI1_IRQn
  • EXTI2_IRQn
  • EXTI3_IRQn
  • EXTI4_IRQn
  • EXTI9_5_IRQn
  • EXTI15_10_IRQn

中断处理函数

中断处理函数与中断通道一一对应一共7个

  • void EXTI0_IRQHandler()
  • void EXTI1_IRQHandler()
  • void EXTI2_IRQHandler()
  • void EXTI3_IRQHandler()
  • void EXTI4_IRQHandler()
  • void EXTI9_5_IRQHandler()
  • void EXTI15_10_IRQHandler()

常用API:

  • ITStatus EXTI_GetITStatus(EXTI_Line)

    判断指定中断线上,是否发生中断,常用于EXTI9_5EXTI15_10上判断具体是哪一个中断线上来的中断

  • void EXTI_ClearITPendingBit(EXTI_Line)

    消除指定中断线上的中断标志,中断处理操作完成后必须调用!

中断、中断线、中断通道、中断处理函数的关系

以西瓜汁生产线类比:

GPIO引脚触发中断相当于果园生产西瓜

中断线相当于西瓜运输线

中断通道相当于运输线上的桥,例如运输线1(中断线1)必须经过桥1(中断通道1),运输线5-9(中断线5到中断线9)必须经过桥9_5(EXTI9_5_IRQn)

中断处理函数相当于西瓜汁生产厂,处理运来的西瓜

所有流程

该流程不包括GPIO的配置

  1. 设置中断分组

    1
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  2. 初始化中断线

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 连接中断线3
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource3);

    EXTI_InitTypeDef exti;
    // 中断线3、产生中断、下降沿触发 使能
    exti.EXTI_Line = EXTI_Line3;
    exti.EXTI_Mode = EXTI_Mode_Interrupt;
    exti.EXTI_Trigger = EXTI_Trigger_Falling;
    exti.EXTI_LineCmd = EANBLE;
    EXTI_Init(&exti);
  3. 设置中断优先级

    1
    2
    3
    4
    5
    6
    7
    NVIC_InitTypeDef nvic;
    // 中断通道3(对应中断线3) 抢占优先级2 响应优先级3 使能
    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
  4. 定义中断处理函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void EXTI0_IRQHandler(void) {
    delay_ms(10); // 消抖
    if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_3)) {
    if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1)) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_1);
    } else {
    GPIO_SetBits(GPIOA, GPIO_Pin_1);
    }
    }
    EXTI_ClearITPendingBit(EXTI_Line3);
    }