STM32

目录

#中断系统和EXTI外部中断#

中断系统(管理和执行中断的逻辑结构)

中断执行流程

STM32中断

内核中断:

外设中断:

NVIC基本结构(嵌套中断向量控制器)

NVIC优先级分组

EXTI简介(众多能产生中断的外设之一)

EXTI基本结构

AFIO复用IO口

EXTI框图

配合外部中断原理模块

旋转编码器简介

硬件电路

外部中断流程:

AFIO相关函数

EXIT相关函数

EXIT在c文件中代码举例

NVIC在c文件中代码举例

对射式红外传感器计数

[1.count Sensor.c](#1.count Sensor.c)

[count Sensor.h](#count Sensor.h)

main.c

旋转编码器计次

1.encounter.c

2.encounter.h

3.main.c


#中断系统和EXTI外部中断#

中断系统(管理和执行中断的逻辑结构)

  • 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源)(外部中断---引脚发生电平跳变,定时器---到达定时的时间,串口通信---接收数据),使得 CPU 暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
  • 中断优先级:当有多个中断源同时申请中断时, CPU 会根据中断源的 轻重缓急 进行裁决,优先响应更加紧急的中断源
  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断, CPU 再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

中断执行流程

STM32 中断

  • 68 个可屏蔽中断通道(中断源),包含 EXTI 外部中断、 TIM定时器 ADC 模数 转换器 USART 串口、 SPI 通信、 I2C 通信、 RTC 实时时钟等多个外设
  • 使用 NVIC (在 STM32 中管理中断分配优先级)统一管理中断, 每个中断通道都拥有 16 个可编程的优先等级 ,可对优先级进行分组,进一步 设置抢占优先级和响应优先级

内核中断:

①复位中断,当产生复位事件时,程序就会自动执行复位中断函数(复位后程序开始执行的位置)。

②NMI,不可屏蔽中断。

③硬件失效

④存储管理

⑤总线错误

⑥错误应用

......

外设中断:

①窗口看门狗,监测程序运行状态的中断(程序卡死没有及时喂狗,窗口看门狗就会申请中断)

PVD 电源电压监测,如果供电电压不足就会申请中断。

EXTI0~ EXTI 4 ,EXTI 9_5 EXTI15_10 外部中断对应的中断源

......

程序中的中断函数地址,由编译器分配,不固定,但是中段跳转由于硬件的限制,只能跳到固定的地址执行程序,所以为了能让硬件跳转到一个不固定的中段函数里,就需要在内存中定义一个地址的列表,在这个固定位置有编译器,再加上一条跳转到中断函数的代码,这样中断跳转跳到任意位置可行,中段地址的列表叫 中段向量表

NVIC 基本结构(嵌套中断向量控制器)

STM32 中,统一分配中断优先级和管理中断

NVIC 是一个内核外设,是 CPU 助手

NVIC 有许多输入口,可以接许多中断线路

NVIC 只有一个输出口,根据每个中断的优先级分配中断的先后顺序,通过一个输出口连接 CPU ,确定中断

NVIC 优先级分组

  • NVIC 的中断优先级由优先级寄存器的 4 位( 0 最高 ~15 最低)决定,这 4 位可以进行切分,分为高 n 位的 抢占优先级 和低 4-n 位的 响应优先级
  • 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队 ,抢占优先级和响应优先级均相同的按中断号排队(不存在先来后到排队方式)
  • 分组方式在程序中是我们自己来选择的,再分配优先级的时候,要注意抢占优先级和响应优先级的取值范围

|----------------|-------------|-------------|
| 分组方式(抢占等级) | 抢占优先级 | 响应优先级 |
| 分组0 | 0位,取值为0 | 4位,取值为0~15 |
| 分组1 | 1位,取值为0~1 | 3位,取值为0~7 |
| 分组2 | 2位,取值为0~3 | 2位,取值为0~3 |
| 分组3 | 3位,取值为0~7 | 1位,取值为0~1 |
| 分组4 | 4位,取值为0~15 | 0位,取值为0 |

EXTI 简介(众多能产生中断的外设之一)

  • EXTI(Extern Interrupt)外部中断
  • EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化 时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断 程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(PA0和PB0不能同时用)
  • 通道数20个:16个GPIO_Pin(0~15),(外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒)

(由于外部中断,可以从低功耗模式的停止模式下唤醒stm32,①pvd电源电压监测---电源从电压过低恢复时,PVD借助外部中断退出停止模式;②RTC闹钟---为了省电,RTC定闹钟后,stm32会进入停止模式,等到闹钟响的时候再唤醒,就需要借助外部中断;③USB唤醒和以太网唤醒都是类似作用)

  • 触发响应方式:中断响应/事件响应
  • (事件响应:Stm32对外部中断增加的一种额外功能,外部中断检测到引脚电平变化,正常流程是会选择触发中断,但也可以选择触发事件,那么外部中断的信号不会通向CPU,而是通向其他外设,触发其他外设的操作------触发ADC转换,触发DMA)
  • 中断响应是正常流程引脚电平变化触发中断,事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作。

EXTI 基本结构

AFIO 复用 IO

  • AFIO主要用于引脚复用功能的选择和重定义(选择器)
  • 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射(将默认复用功能引脚换到重定义模块)、中断引脚选择

EXTI 框图

配合外部中断原理模块

特征:想要获取信号是外部驱动的很快的突发信号(旋转编码器的输出信号 / 红外遥控接收头的输出)

旋转编码器简介

  • 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
  • 类型:机械触点式/霍尔传感器式/光栅式

硬件电路

未旋转→(触点未导通,上拉电阻) A 端口高电平

旋转→(内部触点导通) A 端口低电平

外部中断流程:

//初始化

//配置外部中断流程

//看EXTI基本结构图,将外设配置好,线路串通即可

//GPIO------AFIO------EXTI------NVIC

//配置RCC,打开时钟------使外设能够工作

//配置GPIO,选择端口为输入模式(查看手册GPIO配置)


AFIO相关函数

//配置AFIO,库函数与GPIO同一个文件,选择我们用的一路GPIO,连接到后面的EXIT

void GPIO_AFIODeInit(void);//复位AFIO外设,若调用,将清除所有AFIO外设配置

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

//锁定GPIO配置,参数为某一个引脚,调用引脚,这个引脚配置会被锁定,防止意外更改

void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

void GPIO_EventOutputCmd(FunctionalState NewState);

//俩个函数配置AFIO事件输出功能

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

///进行引脚重映射,第一个参数:选择重映射的方式;第二个参数:新的状态

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

///外部中断需要的函数,配置AFIO的数据选择器,选择我们想要的中断引脚

void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

//与以太网有关,芯片没有配置,用不到


EXIT相关函数

配置EXTI,选择边沿触发方式(上升沿/下降沿/双边沿),选择触发响应方式(中断响应/事件响应)

void EXTI_DeInit(void);

将EXTI配置清除,恢复上电默认状态

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

可根据结构体的参数配置EXTI外设,初始化EXTI 函数


cpp 复制代码
EXIT在c文件中代码举例
EXTI_InitTypeDef EXTI_InitStructure;

EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式

EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling //下降沿触发

EXTI_InitStructure.EXTI_Line=EXTI_Line14 ;//PB14

EXTI_InitStructure.EXTI_LineCmd=ENABLE;//开启中断

EXTI_Init(&EXTI_InitStructure); 

EXTI_InitStructure.EXTI_Trigger=

EXTI_Trigger_Rising// 上升沿触发

EXTI_Trigger_Falling //下降沿触发

EXTI_Trigger_Rising_Falling //双沿触发

void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

可以把参数传递的结构体变量赋一个默认值

void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);

用来软件触发外部中断,调用函数,需要参数给定一个中断线,就能软件触发一次外部中断

如果只需要外部引脚触发外部中断,就不需要使用此函数


在外设运行中,会产生一些状态标志位

比如外部中断,有一个挂起寄存器置一个标志位,串口收到数据,会置标志位

标志位都是存放在状态寄存器,当程序想要看到这些标志位,就会用到这四个函数读写状态寄存器

①FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);

获取指定标志位是否被置1

②void EXTI_ClearFlag(uint32_t EXTI_Line);

对置1的标志位进行清除

/想查看与中断有关的标志位和清除标志位下面俩个函数

③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

//获取中断标志位是否被置1

//set为置1,程序跳转到中断函数

④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

//清除中断挂起标志位

若不清除标志位,程序会一直申请中断,会死机

在主程序查看和清除标志位,用①②函数

在中断函数查看和清除标志位,用③④函数


//配置NVIC,给中断选择一个何使的优先级,通过NVIC,外部中断信号就能进入CPU,CPU接收到中断信号,跳转到中断函数执行中断程序

NVIC为内核外设,库函数在杂项

在配置中断之前,先置定中断分组

再使用 NVIC_ Init初始化NVIC

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

//中断分组,参数使中断分组的方式


cpp 复制代码
NVIC在c文件中代码举例
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;

NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;

NVIC_Init(&NVIC_InitStructure);

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

//根据结构体里里面置定的参数初始化NVIC

void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);

//设置中断向量表

void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

//系统低功耗配置

void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);


对射式红外传感器计数

1.count Sensor.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

uint16_t count;



//初始化
void CountSensor_Init(void)
{
//配置外部中断流程
//看EXTI基本结构图,将外设配置好,线路串通即可
//GPIO-AFIO------EXTI------NVIC
	
	
//1.配置RCC管理外设,打开时钟------使外设能够工作
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//PB12
	//配置AFIO时钟,也是APB2外设
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//PB12
		//EXTI和NVIC外设一直都是打开状态,不需要配置

	
//2.配置GPIO,选择端口为输入模式init初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	//选择模式,对于外部中断来说,上拉输入,下拉输入,浮空输入
	//不知道如何配置模式,在外设配置手册查看外设GPIO配置
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入,默认高电平输入方式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;//PB12
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//常规
	//********
	GPIO_Init(GPIOB,&GPIO_InitStructure);

//3.配置AFIO,库函数与GPIO同一个文件,选择我们用的一路GPIO,连接到后面的EXIT
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource12);//PB12


//4.配置EXTI,选择边沿触发方式初始化(上升沿/下降沿/双边沿),选择触发响应方式(中断响应/事件响应)
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发 
	EXTI_InitStructure.EXTI_Line=EXTI_Line12 ;//PB12
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;//开启中断
	//*******
	EXTI_Init(&EXTI_InitStructure);

//5.配置NVIC,给中断选择一个合适的优先级,通过NVIC,外部中断信号就能进入CPU,CPU接收到中断信号,跳转到中断函数执行中断程序
//	在配置中断之前,先置定中断分组
//  再使用NVIC_Init初始化NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组,整个工程执行一次
	//配置NVIC为分组2
	//即抢占优先级范围:0~3,响应优先级范围:0~3
	//此分组配置在整个工程中仅需调用一次
	//若有多个中断,可以把此代码放在main函数内,while循环之前
	//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;//选择配置NVIC的EXTI15_10线
//	  EXTI15_10_IRQn              = 40,     /*!< External Line[15:10] Interrupts                      */
//选择的是B12引脚,为外部中断12号线
	NVIC_Init(&NVIC_InitStructure);
}



//中断程序
void EXTI15_10_IRQHandler(void)
{
	//中断标志位判断,外部中断10~15都能进,判断是否是12
   if(EXTI_GetITStatus(EXTI_Line12)==SET)
	 {
		 //执行中断程序
		 count++;
		 //清除中断标志位函数,只有中断标志位置1,程序就会跳到中断函数
	   EXTI_ClearITPendingBit(EXTI_Line12);
	 }
}
注意:

NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;

F12跳转到stm32f10x.

根据芯片型号选择(芯片中8为64kflash)

|----|-------|----------|-------------------|
| MD | 中容量产品 | 64~128K | STM32F101/102/103 |

#ifdef STM32F10X_MD在这个模块里寻找相对应的 外部中断通道

EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */

根据选择的引脚选择外部中断线通道

我选的是PB12,为12号线,而12号线在根10号线通用同一个外部中断线


每个终端通道都有对应的外部中断函数,参考启动文件,以IRQhander结尾的就是中断函数的名字

EXTI15_10_IRQHandler ; EXTI Line 15..10

这是中断线10~15的外部中断函数名字


外部中断函数无返回值,无参数

首先进行中断标志位判断,外部中断10~15都能进,判断是否是12

后执行中断程序

最后注意清除中断标志位函数,只有中断标志位置1,程序就会跳到中断函数


数值跳太快,可以在count++前面加延时函数


2.count Sensor.h

cpp 复制代码
#ifndef __COUNTSENSOR_H_
#define __COUNTSENSOR_H_

void CountSensor_Init(void);
extern uint16_t count;

#endif

3.main.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "countSensor.h"

int main(void)
{	
	OLED_Init();
	CountSensor_Init();
	OLED_ShowString(1,1,"count");

	while(1)
	{	

		OLED_ShowNum(2,7,count,5);
	}

}

旋转编码器计次

1.encounter.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

int16_t count;				
//B相下降沿和A相低电平,判断为正转
//A相下降沿和B相低电平,判断为正转
void Encoder_Init(void)
{
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;//PB0和PB1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						
	
	//AFIO选择中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
	
	//EXTI初始化
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//选择配置外部中断的0号线和1号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	//NVIC中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
	//对俩个通道分别设置优先级														
	NVIC_InitTypeDef NVIC_InitStructure;						
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			
	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;			
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//优先级低一点	
	NVIC_Init(&NVIC_InitStructure);								
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = count;
	count = 0;
	return Temp;//返回count的变化值,用于外部加减变量
}

void EXTI0_IRQHandler(void)
{
	//检查中断标志位
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		
	{
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//反转
			//判断另一个引脚
		{
				count--;					
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断标志位
	}
}
//在中断不要执行耗时的代码
//不要在主函数和制度函数调用同一个函数或操作同一个硬件,会显示错误
void EXTI1_IRQHandler(void)
{
	//检查外部中断标志
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)//正转
			{
				count ++;					
			}
		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部标志位
	}
}

2.encounter.h

cpp 复制代码
#ifndef __ENCODER_H_
#define __ENCODER_H_
void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

3.main.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "encoder.h"
uint16_t number;
int main(void)
{	
	OLED_Init();
	OLED_ShowString(1,1,"number");
	Encoder_Init();
	while(1)
	{
		number +=Encoder_Get();
		OLED_ShowSignedNum(1,8,number,5);
	}

}

#up俩个方法都有误差,数字都不稳定,就去搜寻消抖方法#

#江协科技STM32------旋转编码器计次(软件消抖)_旋转编码器消抖-CSDN博客,之前提的在中断函数内延时,占用内存#

这个是一位博主写的方法,我引用分享:

增加判断正、反转的条件,读取一个周期内的电平变化再进行判断。首先将最小系统板的PB0引脚与A相连接,触发方式选择上升/下降沿触发,用A相的输出信号来触发中断,然后在A相下降沿触发第一次中断后读取B相电平,紧接着A相上升沿触发第二次中断后读取B相电平,结合两次读取到的电平来判断是正转还是反转,从A相的下降沿触发到上升沿触发期间,若B相电平发生了变化,则判定编码器转动,反之未转动,波形抖动时B相的电平保持不变,能够实现消抖。

全部代码就放在下面了,注意要改的几个点:
①只需要写EXTI0的中断函数

②AFIO选择中断引脚只需要PB0作为中断引脚

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);

//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚

③EXTI初始化,只需选择配置外部中断的0号线

EXTI_InitStructure.EXTI_Line = EXTI_Line0 ;

④选择上升下降沿触发

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;

⑤NVIC通道只需选择PB0-中断0通道

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //在启动项h文件中找到对应的

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int16_t count,B_level;				
//B相下降沿和A相低电平,判断为正转
//A相下降沿和B相低电平,判断为正转
void Encoder_Init(void)
{
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;//PB0和PB1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						
	
	//AFIO选择中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	
	//EXTI初始化
	EXTI_InitTypeDef EXTI_InitStructure;						
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 ;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;	
  EXTI_Init (&EXTI_InitStructure);

	//NVIC中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
	//对俩个通道分别设置优先级														
	NVIC_InitTypeDef NVIC_InitStructure;				
//定义结构体变量可以重复使用	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;		//在启动项h文件中找到对应的	
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
	
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		
	NVIC_Init(&NVIC_InitStructure);							

对俩个通道分别设置优先级	
	NVIC_Init(&NVIC_InitStructure);								
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = count;
	count = 0;
	return Temp;//返回count的变化值,用于外部加减变量
}


void EXTI0_IRQHandler(void)
{
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)//A相下降沿触发第一次中断
	{
		B_level=0;//读取B相电平,若为高电平则B_level置1,反之保持0
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)
		{
			B_level=1;
		}
	}
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1)//A相上升沿触发第二次中断
	{
		if(B_level==1 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) 
		{
			count++;//正转
		}
		if(B_level==0 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)
		{
			count--;//反转
		}	 
	}	
	EXTI_ClearITPendingBit(EXTI_Line0);	
}
                        

#所有现象均正确,之前的错误也全部解决,OLED显示屏没亮是RCC时钟初始化错误#

相关推荐
小宋同学在不断学习44 分钟前
stm32-掌握SPI原理(一)
stm32·单片机·spi
is08151 小时前
STM32的 syscalls.c 和 sysmem.c
c语言·stm32·嵌入式硬件
学不动CV了1 小时前
数据结构---链表结构体、指针深入理解(三)
c语言·arm开发·数据结构·stm32·单片机·链表
szxinmai主板定制专家3 小时前
【精密测量】基于ARM+FPGA的多路光栅信号采集方案
服务器·arm开发·人工智能·嵌入式硬件·fpga开发
工业互联网专业6 小时前
汇编与接口技术:8259中断实验
汇编·单片机·嵌入式硬件·8259中断实验
brave and determined6 小时前
国产MCU学习Day6——CW32F030C8T6: I2C功能详解与应用案例
单片机·eeprom·i2c·cw32f030c8t6·cw32·cw32f030·中断读取eeprom
梁山1号7 小时前
【ESP32】3.串口的发送与接受
单片机·物联网
KaiGer6667 小时前
AUTOSAR进阶图解==>AUTOSAR_SWS_V2XFacilities
单片机·汽车·嵌入式·autosar
desssq8 小时前
SPI通信协议
单片机·嵌入式硬件
星辰pid10 小时前
STM32实现四自由度机械臂(SG90舵机)多功能控制(软件篇freertos)
stm32·单片机·嵌入式硬件·机械臂