02、【江科大自化协stm32F103c8t6】笔记之【入门32单片机及EXTI外部中断初始化参数配置】

四、返回值函数

uint8_t Key_GetNum(void) 这里与我们常规的void写的函数不同,uint8_t 作为开头可以有返回值

return一经返回,程序结束(但只返回值的函数程序),该函数具有数值的作用

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}

五、EXTI外部中断

EXTI基本结构---------------------------------编写原理 如图中所述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1、配置RCC

打开所有涉及时钟

2、配置GPIO

选择我们的端口为输入模式

3、配置AFIO

选择我们用的这一路GPIO,连接到后面的EXTI

4、配置EXTI

选择边沿触发方式,比如上升沿、下降沿或者双边沿,还有选择触发响应方式,可以选择中断响应(一般配置这个)和事件响应

5、配置NVIC

给我们这个中断选择一个合适的优先级,通过NVIC,外部中断信号就能够进入CPU了,这样CPU能够接收到中断信号,才能跳转到中断函数执行中断程序
——————————————————————————————————————这里涉及五个外设RCC、GPIO、AFIO、EXTI(寄存器里面没有EXTI时钟的控制位)、NVIC(内核的外设,不用RCC开启时钟,RCC管的是外设)

比如 : 我们将PB12设为中断引脚

//首先,配置APB2总线上的RCC时钟 

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能gpio时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//使能afio时钟
	
//初始化GPIO

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入 (查手册 EXTI浮空、上拉、下拉 都行)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);  //配置gpio
	
//其次,选择AFIO中断引脚

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);//配置AFIO中断引脚选择
	
//配置EXTI 类似于GPIO初始化

	EXTI_InitTypeDef EXTI_InitStructure;//有四个参数
	EXTI_InitStructure.EXTI_Line   //EXIT_Line0 ~19  其中line16 ~line19 是 PVD(电压检测器) RTC(实时时钟) USB ETH(以太网)
	EXTI_InitStructure.EXTI_LineCmd //ENABLE 开启 FALSE 关闭
	EXTI_InitStructure.EXTI_Mode //   EXTI_Trigger_Rising  上边沿触发 EXTI_Trigger_Falling  下边沿触发 EXTI_Trigger_Rising_Falling 双边沿触发
	EXTI_InitStructure.EXTI_Trigger // EXTI_Mode_Interrupt  中断触发 EXTI_Mode_Event 事件触发
	EXTI_Init(&EXTI_InitStructure)//void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
	
//配置NVIC    NVIC函数在Library----misc.h里面

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//在中断之前,先指定一下中断分组,分组2,2抢占 ,2响应,抢占(先占):相当于重症直接进icu
响应(从占):相当于插队到下一个
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;  //指定中断通道来开启或关闭  //每个f1型号不一样,c8是MD中等型号  10-15 都在EXTI15_10_IRQn里 //5-9 中断线 共用一个中断通道 EXTI9_5_IRQn 10-15中断线 共用一个中断通道 EXIT15_10IRQn//EXTI0_IRQn 指定通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级  2组 0-3   //中断中有一个  //多个中断源可以考虑改变优先级个数
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //响应优先级  0-3   //中断中有一个
	NVIC_Init(&NVIC_InitStructure);//
//——————————————————————————————————————————————————————————————————————————————这里外部中断就写完了

//下面写中断函数




整个工程NVIC的分组方式只能用一种,模块化分组的话每个模块都用的同一个,都需要写一次,如果放在main.c开头就只需要写一次
在这里插入图片描述

中断函数

每个中断通道对应一个中断函数,中断通道的名字就是它对应的中断函数名,这时我们可以跳转到定义进行复制粘贴


——————————————————————————————————————————————————————————————————————————————这里外部中断就写完了

//下面写中断函数

void EXTI15_10_IRQHandler(void)///EXTI0_IRQHandler //中断函数不需要放在.h里面申明,它是自动执行的
{
	if (EXTI_GetITStatus(EXTI_Line11) == SET) //判断中断线是否是1
	{
		CountSensor_Count ++;
		EXTI_ClearITPendingBit(EXTI_Line12);//一般在中断函数里都要进行中断标志位的判断,确保是我们想要的中断源触发的这个函数    要不断清除中断标志位 ,否则中断标志位就是1,一直执行中断程序卡死在中断程序为里面
	}
}
//中断函数里查看清除状态
EXTI_GetITStatus(EXTI_Line12)
EXTI_ClearITPendingBit(EXTI_Line12)
//主程序里查看清除状态
EXTI_GetFlagStatus()
EXTI_ClearFlag()

最后献上代码:

viod CountSensor_Init(void)
{
	//首先,配置APB2总线上的RCC时钟 

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能gpio时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//使能afio时钟
	
//初始化GPIO

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入 (查手册 EXTI浮空、上拉、下拉 都行)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);  //配置gpio
	
//其次,选择AFIO中断引脚

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);//配置AFIO中断引脚选择
	
//配置EXTI 类似于GPIO初始化

	EXTI_InitTypeDef EXTI_InitStructure;//有四个参数
	EXTI_InitStructure.EXTI_Line   //EXIT_Line0 ~19  其中line16 ~line19 是 PVD(电压检测器) RTC(实时时钟) USB ETH(以太网)
	EXTI_InitStructure.EXTI_LineCmd //ENABLE 开启 FALSE 关闭
	EXTI_InitStructure.EXTI_Mode //   EXTI_Trigger_Rising  上边沿触发 EXTI_Trigger_Falling  下边沿触发 EXTI_Trigger_Rising_Falling 双边沿触发
	EXTI_InitStructure.EXTI_Trigger // EXTI_Mode_Interrupt  中断触发 EXTI_Mode_Event 事件触发
	EXTI_Init(&EXTI_InitStructure)//void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
	
//配置NVIC    NVIC函数在Library----misc.h里面

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//在中断之前,先指定一下中断分组,分组2,2抢占 ,2响应,抢占(先占):相当于重症直接进icu
响应(从占):相当于插队到下一个
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;  //指定中断通道来开启或关闭  //每个f1型号不一样,c8是MD中等型号  10-15 都在EXTI15_10_IRQn里 //5-9 中断线 共用一个中断通道 EXTI9_5_IRQn 10-15中断线 共用一个中断通道 EXIT15_10IRQn//EXTI0_IRQn 指定通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级  2组 0-3   //中断中有一个  //多个中断源可以考虑改变优先级个数
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //响应优先级  0-3   //中断中有一个
	NVIC_Init(&NVIC_InitStructure);//
}

//——————————————————————————————————————————————————————————————————————————————这里外部中断就写完了

//下面写中断函数

void EXTI15_10_IRQHandler(void)///EXTI0_IRQHandler //中断函数不需要放在.h里面申明,它是自动执行的
{
	if (EXTI_GetITStatus(EXTI_Line11) == SET) //判断中断线是否是1
	{
		CountSensor_Count ++;
		EXTI_ClearITPendingBit(EXTI_Line12);//一般在中断函数里都要进行中断标志位的判断,确保是我们想要的中断源触发的这个函数    要不断清除中断标志位 ,否则中断标志位就是1,一直执行中断程序卡死在中断程序为里面
	}
}


再献上两个中断函数的初始化代码(旋转编码器):

int16_t Encoder_Count;

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	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);
	
	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_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);
	}
}

建议: 1、中断函数不要太长,更不要加时间过长的延迟
2、不要在中断函数调用和主程序相同的硬件,如:同时调用OLED,OLED会显示错误,可以操作中断的变量和标志位,再在主程序里面操作显示

  • 17
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clockwisee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值