STM32F103C8T6中断配置和实践

STM32F103C8T6中断配置

作为一个STM32新手,当初第一次接触中断配置的时候,真的是一头雾水。拿着STM32F103C8T6的最小系统板,想做个按键外部中断翻转LED的小实验,结果折腾了大半天,LED就是纹丝不动。后来一点点排查,才发现是自己漏了关键的步骤。今天就把STM32F103C8T6的中断配置经验分享出来,希望能帮到和我当初一样的新手。

一、STM32中断的分类

在STM32中,中断是指CPU在执行当前任务时,由于某种事件(外部或内部)发生,暂停当前任务,转而执行中断服务程序(ISR),完成后再返回原任务。STM32的中断主要分为以下几种类型:

硬中断 硬中断是由外部硬件设备触发的中断信号,例如GPIO外部中断、定时器中断等。硬中断可以进一步分为以下两类:

不可屏蔽中断(NMI):用于处理紧急情况,例如RAM奇偶校验错误。NMI无法被屏蔽,CPU必须立即响应。可屏蔽中断(INTR):用于一般外设的中断请求,例如网卡或串口通信。CPU可以选择响应或忽略。

软中断 软中断是由软件触发的中断,通常用于系统调用或访问受保护的资源。软中断的主要作用是将复杂的任务分为两部分:一部分在硬中断中快速处理,另一部分在软中断中完成,以提高系统实时性。

异常 异常是CPU在运行过程中由于错误或特殊指令触发的中断,例如:

Fault:如除零错误或无效操作码。

Trap:如调试断点或溢出。

Abort:如总线错误或内存访问错误。

外部中断(EXTI) 外部中断是由GPIO引脚的电平变化触发的中断。STM32支持多达23条外部中断线,包括GPIO中断、RTC闹钟事件、USB唤醒等。每条中断线可以独立配置触发方式(上升沿、下降沿或双边沿)。

系统中断 系统中断是由内核触发的特殊中断,例如SysTick定时器中断、PendSV中断等。这些中断通常用于操作系统的任务调度或系统管理。

中断优先级 STM32的中断优先级分为抢占优先级和响应优先级。抢占优先级决定中断是否可以打断其他中断,而响应优先级用于决定同级抢占优先级的中断处理顺序。

通过合理配置中断优先级和触发条件,可以实现高效的实时控制和资源管理。

二、先聊点基础:STM32中断的核心概念

在动手之前,先简单提两个核心玩意儿,不用死记硬背,知道是干啥的就行:

  • NVIC(嵌套向量中断控制器):相当于STM32的“中断管家”,负责管理所有中断的优先级、开启/关闭中断通道,所有外设的中断最终都要经过它的调度。
  • EXTI(外部中断/事件控制器):专门管外部引脚的中断,比如按键触发的中断,它能把GPIO引脚和NVIC连接起来,还能设置触发方式(上升沿、下降沿、双边沿)。

STM32F103C8T6的中断配置本质上就是:让外设(比如GPIO)产生中断请求,通过EXTI(外部中断)或外设自身的中断源,告诉NVIC,最后由NVIC触发对应的中断服务函数。

三、实战环境准备

先交代下我的实验环境,新手可以直接照搬:

  • 硬件:STM32F103C8T6最小系统板、LED灯(接PB0)、按键(接PA0)
  • 软件:Keil MDK5.36、STM32标准外设库3.5.0(F1系列,别用HAL库,新手先从标准库入手更易理解)

实验目标很简单:按下PA0的按键,触发外部中断,翻转PB0的LED灯状态

四、库函数配置外部中断

我推荐新手先用库函数,不用跟寄存器死磕,先把流程跑通再说。

步骤1:开启相关时钟(最容易漏的一步!)

STM32的外设要工作,必须先开时钟,这是铁律。这里需要开三个时钟:

  • GPIOA(PA1按键)、GPIOB(PB0 LED)的时钟(APB2总线)
  • AFIO(复用功能IO)的时钟(重点! 外部中断的引脚映射必须靠AFIO,不开这个时钟,中断绝对触发不了)

代码如下:

// 1. 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

步骤2:配置GPIO引脚模式

PA1作为按键输入,我选上拉输入(这样按键没按下时是高电平,按下是低电平);PB0作为LED输出,选推挽输出

代码:

// 2. 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
    
// PA1配置(根据实际电路选择模式)
// 如果按键接在PA0和GND之间,使用上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入   
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 输入模式可忽略Speed   
GPIO_Init(GPIOA, &GPIO_InitStructure);    
    
// PB0配置  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
GPIO_Init(GPIOB, &GPIO_InitStructure);   
GPIO_SetBits(GPIOB, GPIO_Pin_0);  // 初始高电平GPIO_InitTypeDef GPIO_InitStructure;

步骤3:配置AFIO的外部中断引脚映射

STM32的EXTI线和GPIO引脚是“一对多”的关系,比如EXTI0线可以对应GPIOA0、GPIOB0、GPIOC0等,但需要通过AFIO指定具体用哪个GPIO的引脚。

我们要把PA1和EXTI1线绑定,用库函数GPIO_EXTILineConfig就行:

// 3. 配置AFIO映射
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);

步骤4:配置EXTI中断参数

这里要设置中断的触发方式、开启中断线:

// 4. 配置EXTI

EXTI_InitTypeDef EXTI_InitStructure;    
EXTI_InitStructure.EXTI_Line = EXTI_Line1;    
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  // 下降沿触发   
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

步骤5:配置NVIC中断优先级

NVIC是“中断管家”,必须告诉它:哪个中断通道要开启?优先级是多少?

首先要配置优先级分组(整个程序只能调用一次!),我选分组2(2位抢占优先级,2位响应优先级),然后配置EXTI0的优先级:

// 5. 配置NVIC

NVIC_InitTypeDef NVIC_InitStructure;    
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;   
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;    
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;    
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

步骤6:编写中断服务函数(必须清标志位!)

中断服务函数的名字是固定的,要和启动文件(startup_stm32f10x_md.s)里的中断向量表一致,比如EXTI0的服务函数名就是EXTI0_IRQHandler

重点中的重点:执行完中断操作后,必须清除中断标志位,否则MCU会认为中断还在触发,一直进入服务函数,导致程序卡死!

// 中断服务函数定义
void EXTI1_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line1) != RESET)
    {
        // 简单延时消抖(可选,根据需求)
        // for(int i = 0; i < 10000; i++);
        
        // 读取按键状态
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
        {
            // 翻转LED状态
            if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0))
                GPIO_ResetBits(GPIOB, GPIO_Pin_0);
            else
                GPIO_SetBits(GPIOB, GPIO_Pin_0);
        }
        
        EXTI_ClearITPendingBit(EXTI_Line1);  // 必须清除中断标志
    }
}

步骤7:整合main函数

把上面的代码整合到main函数里,死循环里啥都不用做,等待中断即可:

#include "stm32f10x.h"                  // STM32标准外设库头文件

// 中断服务函数声明
void EXTI1_IRQHandler(void);

/**
  * @brief  主函数
  * @param  无
  * @retval int 程序返回值(通常不会返回)
  */
int main(void)
{
    // 系统初始化(配置系统时钟、Flash延迟等)
    SystemInit();
    
    /******************** 第一步:开启外设时钟 ********************/
    // 使能GPIOA、GPIOB和AFIO(复用功能IO)的时钟
    // 注意:STM32外设使用前必须开启对应的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    
    /******************** 第二步:配置GPIO引脚 ********************/
    GPIO_InitTypeDef GPIO_InitStructure;  // GPIO初始化结构体
    
    // 配置PA1为输入模式(连接按键)
    // 假设按键一端接PA1,另一端接GND,使用内部上拉电阻
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;      		// 选择引脚1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  		// 上拉输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  	// 输入模式下此参数可忽略
    GPIO_Init(GPIOA, &GPIO_InitStructure);         		// 应用配置到GPIOA
    
    // 配置PB0为输出模式(连接LED)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;     		 // 选择引脚0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  	 // 推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 	 // 输出速度50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure);        		 // 应用配置到GPIOB
    
    // 设置PB0初始状态为高电平(LED灭,假设低电平点亮LED)
    GPIO_SetBits(GPIOB, GPIO_Pin_0);
    
    /******************** 第三步:配置AFIO映射 ********************/
    // 将GPIOA的Pin1映射到外部中断线1
    // 注意:每个外部中断线可以映射到多个GPIO引脚,需通过AFIO选择具体引脚
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
    
    /******************** 第四步:配置外部中断(EXTI) ********************/
    EXTI_InitTypeDef EXTI_InitStructure;  // EXTI初始化结构体
    
    EXTI_InitStructure.EXTI_Line = EXTI_Line1;       		 // 选择外部中断线1
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 	 // 中断模式(非事件模式)
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  // 下降沿触发(按键按下时产生下降沿)
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;        		 // 使能该中断线
    EXTI_Init(&EXTI_InitStructure);                 	     // 应用配置
    
    /******************** 第五步:配置嵌套向量中断控制器(NVIC) ********************/
    NVIC_InitTypeDef NVIC_InitStructure;  // NVIC初始化结构体
    
    // 配置中断优先级分组(2位抢占优先级,2位响应优先级)
    // 注意:整个系统只能调用一次优先级分组函数
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    // 配置EXTI1中断通道
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;  		   // 中断通道:EXTI线1
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  // 抢占优先级为2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;         // 响应优先级为2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // 使能该中断通道
    NVIC_Init(&NVIC_InitStructure);                            // 应用配置
    
    /******************** 第六步:主循环 ********************/
    while(1)
    {
        // 主循环可以添加其他后台任务
        // 中断处理会在中断服务函数中进行,不会影响主循环运行
        // 可以在这里添加低功耗模式、系统状态监测等代码
    }
}

/**
  * @brief  EXTI线1中断服务函数
  * @param  无
  * @retval 无
  * @note   当PA1引脚检测到下降沿时,此函数会被自动调用
  */
void EXTI1_IRQHandler(void)
{
    // 检查是否发生了EXTI线1中断(中断标志位是否置位)
    if (EXTI_GetITStatus(EXTI_Line1) != RESET)
    {
        // ---------- 可选:按键消抖处理 ----------
        // 机械按键在按下/释放时会产生抖动,可能导致多次触发
        // 简单的软件消抖方法:延时后再次检测引脚状态
        // for(int i = 0; i < 10000; i++);  // 简单延时
        
        // ---------- 确认按键状态 ----------
        // 再次读取PA1引脚状态,确保是有效的按键按下
        // 上拉模式下,按键按下时引脚为低电平(0)
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
        {
            // ---------- 执行中断处理逻辑 ----------
            // 读取PB0当前输出状态并翻转
            if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0))
            {
                // 当前为高电平,设置为低电平(点亮LED)
                GPIO_ResetBits(GPIOB, GPIO_Pin_0);
            }
            else
            {
                // 当前为低电平,设置为高电平(熄灭LED)
                GPIO_SetBits(GPIOB, GPIO_Pin_0);
            }
            
            // 可选:添加按键释放等待,避免单次按下触发多次
            // while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0);
        }
        
        // ---------- 清除中断标志位 ----------
        // 非常重要!如果不清除标志位,会连续触发中断
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

五、容易踩的坑

  1. 忘记开启AFIO时钟:这是我当初最致命的坑,配置完所有步骤,按键按烂了LED都不亮,后来查手册才发现外部中断映射需要AFIO支持。
  2. 没清中断标志位:第一次成功触发中断后,LED闪了一下就卡住了,原因是没清标志位,MCU一直重复进入中断。
  3. GPIO模式配错:比如把PA0配成浮空输入,又没外接上拉电阻,导致引脚电平不稳定,中断乱触发。
  4. 优先级分组调用多次NVIC_PriorityGroupConfig只能调用一次,否则会导致优先级配置混乱。

六、总结:中断配置的核心逻辑

其实不管是外部中断、定时器中断还是串口中断,配置流程都大同小异:
开时钟 → 配外设 → 配中断源(EXTI/外设自身) → 配NVIC → 写服务函数(清标志位)

新手只要把这个流程刻在脑子里,再结合实际外设的特点稍作调整,就能搞定大部分中断配置了。

最后说句心里话:学习STM32,光看教程没用,一定要动手。遇到问题别慌,查手册、单步调试、逐行排查,那些踩过的坑,最后都会变成你的经验。

江协科技中断学习笔记

image-20251214201929799

image-20251214202131187

image-20251214202902901

image-20251214203046272

image-20251214203227939

image-20251214201821644

image-20251214203604593

image-20251214203913192

image-20251214203926184

image-20251214204125840

image-20251214204550680

对射式传感器的应用

5-1 对射式红外传感器计次

main.c 代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "OLED.h"
#include "CountSensor.h"

int main()
{
  OLED_Init();
	CountSensor_Init();
	
	OLED_ShowCHinese(0, 0, 0);					  	//计
	OLED_ShowCHinese(0, 16, 1); 						//次
	OLED_ShowString(1,5,":");

	while(1)
	{
		OLED_ShowNum(1,6,CountSensor_Get(),5);
	}
}

CountSensor.c 代码如下:

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	/*开启时钟*/
	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_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB14引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
    //将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
   
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;				//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;		    //选择配置外部中断的14号线
	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
																																									//即抢占优先级范围:0~3,响应优先级范围:0~3
																																									//此分组配置在整个工程中仅需调用一次
																																									//若有多个中断,可以把此代码放在main函数内,while循环之前
																																									//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
							
	/*NVIC配置*/						
	NVIC_InitTypeDef NVIC_InitStructure;							//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		    //选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;				//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);									//将结构体变量交给NVIC_Init,配置NVIC外设
	
}

/**
  * 函    数:获取计数传感器的计数值
  * 参    数:无
  * 返 回 值:计数值,范围:0~65535
  */

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line14) == SET)
	{
		
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)  //判断是否是外部中断14号线触发的中断
		{
			CountSensor_Count ++;				   //计数值自增一次
		}
		EXTI_ClearITPendingBit(EXTI_Line14);			//清除外部中断14号线的中断标志位
														//中断标志位必须清除
														//否则中断将连续不断地触发,导致主程序卡死
	}
}

CountSensor.h 代码如下:

#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

旋转编码器计数

5-2 旋转编码器计次

main.c 代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main()
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Encoder_Init();		//旋转编码器初始化
	
	OLED_ShowCHinese(0, 0, 0);					  	//计
	OLED_ShowCHinese(0, 16, 1); 						//次
	OLED_ShowString(1,5,":");

	while(1)
	{
		Num += Encoder_Get();
		OLED_ShowSignedNum(1,6,Num,5);	
	}
}

Encoder.c 代码如下:

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;

void Encoder_Init(void)
{
	/*开启时钟*/
	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;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB14引脚初始化为上拉输入
	
	/*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;					//选择配置外部中断的14号线
	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
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;		//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;		//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
	/*在这里,也可以直接返回Encoder_Count
	  但这样就不是获取增量值的操作方法了
	  也可以实现功能,只是思路不一样*/
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

/**
  * 函    数:EXTI0外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
            //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		
			{
				Encoder_Count --;					//此方向定义为反转,计数变量自减
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

/**
  * 函    数:EXTI1外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		//判断是否是外部中断1号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
            //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)		
			{
				Encoder_Count ++;					//此方向定义为正转,计数变量自增
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部中断1号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

Encoder.h 代码如下:

#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值