• stm32 - 中断


    概念

    stm32 支持的中断资源(都属于外设)

    • EXTI
    • TIM
    • ADC
    • USARt
    • SPI
    • I2C

    stm32支持的中断

    内核中断
    外设中断

    中断通道与优先级

    一个外设可能占用多个中断通道(一个EXTI外设模块,可以有多个中断通道)
    每个中断通道有16个优先级

    中断向量表

    自定义的中断服务函数,由编译器随机指定函数地址
    stm32的中断,由于硬件的限制,只能跳到固定的地址执行程序
    为了能让硬件跳转到一个不固定的中断函数中, 需要在内存中定义一个地址列表,这个列表的地址是固定的,中断发生后,先跳到这个固定位置,然后在这个固定位置,由编译器加上一条跳转到中断函数的代码,这样中断就可以跳转到任意位置了

    NVIC 嵌套中断向量控制器

    用于同一分配中断优先级和管理中断的,NVIC是一个内核外设

    一个外设可能占用多个中断通道,所以有n条线
    NVIC只有一个输出口,其根据每个中断的优先级分配中断的先后顺序
    然后通过仅有的一个输出口,通知CPU应该处理哪个中断

    在这里插入图片描述

    优先级

    • 响应优先级:“插队看病”
    • 抢占优先级:“中断嵌套”

    NVIC中断优先级由优先级寄存器的4位(0~15)决定,数值越小优先级越高
    高n位为抢占优先级,4-n位为响应优先级
    抢占优先级高的可以中断嵌套,响应优先高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

    中断

    EXTI

    检测指定GPIO的电平信号
    电平信号发生变化时,EXTI申请中断

    概念

    触发方式

    上升沿(低->高)触发中断
    下降沿(高->低)触发中断
    双边沿(上升沿和下降沿都可以触发中断)
    软件触发:引脚电平未发生变化,通过在软件中调用执行代码触发中断

    GPIO

    任意的GPIO口都可以当做外部中断的引脚,但是相同的pin引脚不能同时触发中断(PA0/PB0; PA1/PB1/PC1)

    通道数

    16个GPIO_pin,外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒

    触发响应方式

    中断响应:申请中断,让CPU执行中断函数
    事件响应: 当外部中断检测到引脚电平变化时,正常的流程是选择触发中断,也可以选择触发事件,那么外部中断的信号就不会通向CPU了,而是通向其他外设,用来触发其他外设的操作

    基本结构

    EXTI模块支持20个中断通道数,16个pin_脚+PVD+RTC+USB+ETH

    针对GPIO,利用AFIO进行中断选择(当使用GPIO实现外部中断时)

    PA0/PB0/PC0… 通过AFIO选择器选择一个作为pin_0通道
    PA1/PB1/PC1… 通过AFIO选择器选择一个作为pin_1通道
    PA2/PB2/PC2… 通过AFIO选择器选择一个作为pin_2通道

    每个通道有16可配置优先级,通过NVIC进行优先级配置
    NVIC可以进行优先级分组(抢占/响应),每个分组都有取值范围,不同的取值范围,在CPU进行中断服务的时机不同

    注意16个引脚输入通道,最终只有7个输入,其中9-5和15-10便成了路两路输出通道,因此需要中断标志位判断哪一个中断过来
    在这里插入图片描述
    在这里插入图片描述

    PB14引脚的电平信号就可以通过AFIO进入到EXTI电路中
    在这里插入图片描述

    例子- 对射式红外传感器计次

    main.c

    #include "stm32f10x.h"
    #include "OLED.h"
    #include "infrCountSensor.h"
    
    int main()
    {
    	OLED_Init();
    	OLED_ShowString(1,1,"helloworld");
    	OLED_ShowString(2,1,"count: ");
    	InfrCountSensor_Init(14);
    	while (1) 
    	{
    		OLED_ShowNum(2,8,InfrCountSensor_GetCount(),8);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    infrCountSensor.h

    #ifndef __INFRCOUNTSENSOR_H__
    #define __INFRCOUNTSENSOR_H__
    #include "stm32f10x.h"
    void InfrCountSensor_Init(unsigned char pin_num);
    unsigned int InfrCountSensor_GetCount();
    uint16_t GPIO_Pin_Num_Set(uint16_t pin_num);
    uint8_t GPIO_AFIO_Pin_Num_Select(uint16_t pin_num);
    uint32_t EXTI_Line_Set(unsigned char pin_num);
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    infrCountSensor.c

    #include "infrCountSensor.h"
    
    static unsigned int infrCountSensor_count;
    
    uint16_t GPIO_Pin_Num_Set(uint16_t pin_num)
    {
    	uint16_t GPIO_Pin_num=0;
    	switch (pin_num)
    	{
    		case 0: {return GPIO_Pin_0;break;}
    		case 1: {return GPIO_Pin_1;break;}
    		case 2: {return GPIO_Pin_2;break;}
    		case 3: {return GPIO_Pin_3;break;}
    		case 4: {return GPIO_Pin_4;break;}
    		case 5: {return GPIO_Pin_5;break;}
    		case 6: {return GPIO_Pin_6;break;}
    		case 7: {return GPIO_Pin_7;break;}
    		case 8: {return GPIO_Pin_8;break;}
    		case 9: {return GPIO_Pin_9;break;}
    		case 10: {return GPIO_Pin_10;break;}
    		case 11: {return GPIO_Pin_11;break;}
    		case 12: {return GPIO_Pin_12;break;}
    		case 13: {return GPIO_Pin_13;break;}
    		case 14: {return GPIO_Pin_14;break;}
    		case 15: {return GPIO_Pin_14;break;}
    		default: return GPIO_Pin_num;
    	}
    
    }
    
    uint8_t GPIO_AFIO_Pin_Num_Select(uint16_t pin_num)
    {
    	uint8_t AFIO_Pin_num=0;
    	switch (pin_num)
    	{
    		case 0: {return GPIO_PinSource0;break;}
    		case 1: {return GPIO_PinSource1;break;}
    		case 2: {return GPIO_PinSource2;break;}
    		case 3: {return GPIO_PinSource3;break;}
    		case 4: {return GPIO_PinSource4;break;}
    		case 5: {return GPIO_PinSource5;break;}
    		case 6: {return GPIO_PinSource6;break;}
    		case 7: {return GPIO_PinSource7;break;}
    		case 8: {return GPIO_PinSource8;break;}
    		case 9: {return GPIO_PinSource9;break;}
    		case 10: {return GPIO_PinSource10;break;}
    		case 11: {return GPIO_PinSource11;break;}
    		case 12: {return GPIO_PinSource12;break;}
    		case 13: {return GPIO_PinSource13;break;}
    		case 14: {return GPIO_PinSource14;break;}
    		case 15: {return GPIO_PinSource14;break;}
    		default: return AFIO_Pin_num;
    	}
    }
    
    uint32_t EXTI_Line_Set(unsigned char pin_num)
    {
    	uint32_t EXTI_Line_num=0;
    	switch (pin_num)
    	{
    		case 0: {return EXTI_Line0;break;}
    		case 1: {return EXTI_Line1;break;}
    		case 2: {return EXTI_Line2;break;}
    		case 3: {return EXTI_Line3;break;}
    		case 4: {return EXTI_Line4;break;}
    		case 5: {return EXTI_Line5;break;}
    		case 6: {return EXTI_Line6;break;}
    		case 7: {return EXTI_Line7;break;}
    		case 8: {return EXTI_Line8;break;}
    		case 9: {return EXTI_Line9;break;}
    		case 10: {return EXTI_Line10;break;}
    		case 11: {return EXTI_Line11;break;}
    		case 12: {return EXTI_Line12;break;}
    		case 13: {return EXTI_Line13;break;}
    		case 14: {return EXTI_Line14;break;}
    		case 15: {return EXTI_Line15;break;}
    		default: return EXTI_Line_num;
    	}
    }
    
    void InfrCountSensor_Init(unsigned char pin_num)
    {
    	// 打开GPIO/AFIO时钟
    	// GPIO和AFIO是APB2总线的外设,需要手动开启时钟,RCC用于配置内核外的外设时钟
    	// EXIT中断默认开始时钟,NVIC是内核外设(内核外设也无需开启时钟)
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    		 
    	// 配置GPIO 输入模式
    	uint16_t GPIO_Pin_num=0;
    	GPIO_Pin_num=GPIO_Pin_Num_Set(pin_num);
    	
    	GPIO_InitTypeDef GPIO_InitStructure; 
    	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;		// 上拉输入
    	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_num;
    	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    	
    	GPIO_Init(GPIOB,&GPIO_InitStructure);
    	
    	// 配置AFIO 选择器(中断引脚选择)
    	// GPIO_AFIODeInit(); 		// 清空配置
    	// GPIO_PinLockConfig(); 	// 锁定引脚,防止意外被更改
    	// GPIO_PinRemapConfig(); 		// 引脚重映射
    	// 选择指定的GPIO / pin 作为外部中断源
    	uint8_t AFIO_Pin_num=0;
    	AFIO_Pin_num=GPIO_AFIO_Pin_Num_Select(pin_num);
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,AFIO_Pin_num); // 配置数据选择器,
    															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器
    	
    	// 配置EXTI中断,触发方式,触发响应方式
    	// EXTI_DeInit(); 		// 清除配置
    	// EXTI_StructInit(); 	// 结构体赋值
    	// EXTI_GenerateSWinterrupt(); // 软件触发
    	// 外部中断的外部状态寄存器会设置对应的标志位
    	// 在主程序中读写外部中断标志位,在中断服务函数中读写外部中断标志位
    	uint32_t EXTI_Line_num=0;
    	EXTI_Line_num=EXTI_Line_Set(pin_num);
    	EXTI_InitTypeDef EXTI_InitStructure;
    	EXTI_InitStructure.EXTI_Line=EXTI_Line_num;				// 指定要配置的中断线,中断EXTI输入通道
    	EXTI_InitStructure.EXTI_LineCmd=ENABLE;					// 中断线的新状态,开启中断
    	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt ;		// 中断模式interrupt或事件模式event
    	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	//触发方式,下降沿
    	EXTI_Init(&EXTI_InitStructure); 						// 初始化
    	
    	// 配置NVIC,中断优先级,进入CPU再执行中断程序
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组,两位抢占,两位响应
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;		// 中断EXTI输出通道,15-10占用一个通道,在中断服务函数中需要判断中断标志位
    	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)
    	NVIC_Init(&NVIC_InitStructure);
    }
    
    void EXTI15_10_IRQHandler(void) // 中断函数
    {
    	// 中断标志位的判断
    	if (EXTI_GetITStatus(EXTI_Line14)==SET)
    	{
    		// 清楚中断标志位
    		EXTI_ClearITPendingBit(EXTI_Line14);
    		
    		infrCountSensor_count++;
    	}
    }
    
    
    unsigned int InfrCountSensor_GetCount()
    {
    	return infrCountSensor_count;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    例子 - 旋转编码器

    对于两个中断,AFIO、EXTI、NVIC的code方式

    main.c

    #include "stm32f10x.h"
    #include "Delay.h"
    #include "LED.h"
    #include "key.h"
    #include "Buzzer.h"
    #include "PhotoSensor.h"
    #include "OLED.h"
    #include "infrCountSensor.h"
    #include "encoder.h"
    
    int main()
    {
    	OLED_Init();
    	OLED_ShowString(1,1,"helloworld");
    	OLED_ShowString(2,1,"count: ");
    	Encoder_Init();
    	while (1) 
    	{
    		OLED_ShowSignedNum(2,8,Encoder_Get(),8);
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    encoder.h

    #ifndef __ENCODER_H__
    #define __ENCODER_H__
    
    #include "stm32f10x.h"
    void Encoder_Init();
    int16_t Encoder_Get();
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    encoder.c

    #include "encoder.h"
    
    int16_t encoder_rotate;
    
    void Encoder_Init()
    {
    	// 打开GPIO/AFIO时钟
    	// GPIO和AFIO是APB2总线的外设,需要手动开启时钟,RCC用于配置内核外的外设时钟
    	// EXIT中断默认开始时钟,NVIC是内核外设(内核外设也无需开启时钟)
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    		 
    	// 配置GPIO 输入模式
    	GPIO_InitTypeDef GPIO_InitStructure; 
    	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;		// 上拉输入
    	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    	
    	GPIO_Init(GPIOB,&GPIO_InitStructure);
    	
    	// 配置AFIO 选择器(中断引脚选择)
    	// 选择指定的GPIO / pin 作为外部中断源
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0); // 配置数据选择器,
    															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1); // 配置数据选择器,
    															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器
    
    
    	// 配置EXTI中断,触发方式,触发响应方式
    	// 外部中断的外部状态寄存器会设置对应的标志位
    	// 在主程序中读写外部中断标志位,在中断服务函数中读写外部中断标志位;
    	EXTI_InitTypeDef EXTI_InitStructure;
    	EXTI_InitStructure.EXTI_Line=EXTI_Line0 | EXTI_Line1;				// 指定要配置的中断线,中断EXTI输入通道
    	EXTI_InitStructure.EXTI_LineCmd=ENABLE;					// 中断线的新状态,开启中断
    	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt ;		// 中断模式interrupt或事件模式event
    	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	//触发方式,下降沿
    	EXTI_Init(&EXTI_InitStructure); 						// 初始化
    	
    	// 对两个通道分别设置优先级
    	// 配置NVIC,中断优先级,进入CPU再执行中断程序
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组,两位抢占,两位响应
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;		// 中断EXTI输出通道
    	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)
    	NVIC_Init(&NVIC_InitStructure);
    	
    	NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;		// 中断EXTI输出通道
    	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;		// 响应优先级 (在响应中断中)
    	NVIC_Init(&NVIC_InitStructure);
    }
    
    void EXTI0_IRQHandler()
    {
    	if (EXTI_GetITStatus(EXTI_Line0)==SET)
    	{
    		EXTI_ClearITPendingBit(EXTI_Line0);
    		
    		if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
    		{
    			encoder_rotate--;
    		} 
    	}
    }
    
    void EXTI1_IRQHandler()
    {
    	if (EXTI_GetITStatus(EXTI_Line1)==SET)
    	{
    		EXTI_ClearITPendingBit(EXTI_Line1);
    		
    		if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
    		{
    			encoder_rotate++;
    		} 
    	}
    }
    
    int16_t Encoder_Get()
    {
    	return encoder_rotate;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
  • 相关阅读:
    nginx(六十二)proxy模块(三)接收用户请求的包体
    Java高级面试题(二)-- JVM
    Buffer 与 拥塞控制
    Linux_用户管理
    CRM,下一程在哪?
    Python 和 Tableau:完整的数据分析训练营
    修改WPF程序集名称报错
    模拟实现string类
    工作电压范围宽的国产音频限幅器D2761用于蓝牙音箱,输出噪声最大仅-90dBV
    Spring Tool Suite(STS)初始化配置记录
  • 原文地址:https://blog.csdn.net/L_fengzifei/article/details/133466062