学习STM32第三天

STM32中断

一、中断系统

  • 中断概述
    在主程序运行过程中出发了特定中断条件,使得CPU暂停当前正在运行的程序转而去处理中断程序,处理完成后返回主程序暂停位置。每个外设都可以产生中断。
  • 中断优先级
    当有多个中断源同时申请中断时,CPU会根据优先级啊依次响应中断。
  • 中断嵌套
    在处理中断程序时,有更高优先级中断源申请中断,允许CPU暂停当前中断程序,转而响应新的中断,处理完成后依次返回暂停位置。
int main()
{
	while(1)
	{
		//主程序
		//触发中断
		//主程序
	}
}

void EXTI_FUN1()
{
	//中断程序
	//中断嵌套
	//中断程序
}

二、NVIC

NVIC是嵌套向量中断控制器,统一管理中断相关的功能,是内核里面的一个外设,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。这里给出江协科资料中NVIC基本结构图
EXTI基本结构

  • 中断优先级
    通过中断优先级寄存器NVIC_IPRx,配置外部中断优先级,宽8bit,但由于精简设计,F103只使用了高4bit(0~15)。这4bit又分为抢占优先级(高n位)和子优先级(低4-n位)。
  • 优先级分组
    抢占优先级高的可以中断嵌套,子优先级高的可以优先响应,优先级都相同的话,按硬件中断编号响应。编号越小,优先级越高。这里给出江协科资料中优先级分组图
    中断优先级分组
    设置中断优先级分组通过调用库函数NVIC_PriorityGroupConfig()实现,该函数定义如下
/**
* 配置中断优先级分组:抢占优先级和子优先级
* 形参如下:
* @arg NVIC_PriorityGroup_0: 0bit for抢占优先级
*                            4 bits for 子优先级
* @arg NVIC_PriorityGroup_1: 1 bit for抢占优先级
*                            3 bits for 子优先级
* @arg NVIC_PriorityGroup_2: 2 bit for抢占优先级
*                            2 bits for 子优先级
* @arg NVIC_PriorityGroup_3: 3 bit for抢占优先级
*                            1 bits for 子优先级
* @arg NVIC_PriorityGroup_4: 4 bit for抢占优先级
*                            0 bits for 子优先级
* @注意 如果优先级分组为0,则抢占优先级就不存在,优先级就全部由子优先级控制
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
    // 设置优先级分组
    SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

三、EXTI

EXTI外部中断/事件控制器,在APB2总线上,有20个中断/事件线,其中GPIO占用EXTI0至EXTI15。EXTI可以检测指定GPIO口的电平信号,当检测到特定电平信号时,EXTI将向NVIC发出中断申请,由NVIC根据优先级依次通知CPU响应中断。
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。通用I/O端口以外部中断通用I/O映像连接16个外部中断/事件线上,外部中断源通过AFIO的外部中断配置寄存器进行选择,这里给出参考手册中外部中断通用I/O映像图。
外部中断通用I/O映像图
这里给出江协科资料中EXTI基本结构图
EXTI基本结构图

四、中断编程

标准库编程精妙在于初始化结构体和初始化库函数。同配置输入输出编程,配置中断同样有3个步骤

  1. 使能外设中断
  2. 初始化NVIC_InitTypeDef结构体
typedef struct {
    uint8_t NVIC_IRQChannel;                    // 中断源
    uint8_t NVIC_IRQChannelPreemptionPriority;  // 抢占优先级
    uint8_t NVIC_IRQChannelSubPriority;         // 子优先级
    FunctionalState NVIC_IRQChannelCmd;         // 中断使能或者失能
} NVIC_InitTypeDef;
  • NVIC_IRQChannel
    用来设置中断源,不同中断的中断源不一样
  • NVIC_IRQChannelPreemptionPriority
    抢占优先级,具体的值根据优先级分组来确定
  • NVIC_IRQChannelSubPriority
    子优先级,具体值根据优先级分组来确定
  1. 编写中断服务函数
    实验案例,这里给出江协科实验案例
  • 红外对射式传感器计数实验
#include "stm32f10x.h"                  // Device header
uint16_t CountSensor_Count;
//初始化库函数
void CountSensor_Init(void)
{
	//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启PB引脚时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启复用IO时钟
	//配置GPIO输入口
	GPIO_InitTypeDef GPIO_InitStructure;//配置PB14引脚,EXTI输入线复用外部中断输入
	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);
	//配置AFIO的数据选择器,选择EXTI引脚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);
	//中断分组,这里选择第二种分组方式
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//设置中断源
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//设置抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//设置子优先级
	NVIC_Init(&NVIC_InitStructure);//初始化结构体
}

//中断服务函数
void EXTI15_10_IRQHandler(void)
{
	//通过EXTI_GetITStatus()函数获取是否触发中断
	if (EXTI_GetITStatus(EXTI_Line14) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;
		}
		//清除中断标志位
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

//返回计数值函数
uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

主函数代码如下

#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上
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);
	}
}
  • 旋转编码器计数
#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;//定义计数值

void Encoder_Init(void)
{
	//开启时钟
	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_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
	//配置外部中断
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//两个中断源,两条中断线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
	EXTI_Init(&EXTI_InitStructure);
	//选择第二种分组方式
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//为两个中断源配置NVIC,两个中断源抢占优先级相同,反转的子优先级高,正转的子优先级小
	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 = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}
//获取反转计数值
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
			{
				Encoder_Count --;
			}
		}
		//清除中断标志位
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}
//获取正转计数值
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
			{
				Encoder_Count ++;
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

主函数代码如下

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

int16_t Num;

int main(void)
{
	OLED_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "Num:");
	
	while (1)
	{
		//将计数值求代数和
		Num += Encoder_Get();
		OLED_ShowSignedNum(1, 5, Num, 5);
	}
}

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值