stm32使用外部中断写旋转编码器(包教包会)

链接:https://pan.baidu.com/s/1yBKbngf5VsZEIwxoJ4WNNA?pwd=ulmi 
提取码:ulmi

一、编码器原理介绍

旋转编码器也称为轴编码器,增量式旋转编码器拥有A、B、C三个输出通道,其中A、B为两个正交信号,相位差为90°,C相输出0脉冲信号,用于标识位置。当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后90°,在程序中可以根据A、B两相信号输出的先后顺序来判断编码器是正转还是反转.

以上呢,是旋转编码器正反转的A、B相波形图,程序中采用的是下降沿触发,由波形图,当A相为下降沿,B相为低电平时,反转;当B相为下降沿,A为低电平时,正转。根据该原理逻辑可实现编码器计数。

二、引脚连接

旋转编码器

stm32f103c8t6

VCC 3.3V
GND GND
A PB0
B PB1
C GND

三、代码解读

代码解读总思路就是配置中断服务函数,这里采用的是外部中断。

首先定义IO口

然后将PB0和PB1的总线映射到PB口上

然后再配置中断模式(这里选择的是下降沿触发)

然后配置抢占优先级和响应优先级,开启中断(这里PB0和PB1和抢占优先级一样,PB1的响应优先级大于PB0的响应优先级,就是再PB0和PB1的下降沿同时到来时,优先触发PB1的中断)

最后再配置中断服务函数,就可以使用了(在PB1的下降沿到来时,触发PB1的中断,如果此时PB0为低电平,计数++,后面同理)

#include "stm32f10x.h"                  // Device header

int16_t EncoderCount;//全局变量
void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE );//APB2_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 为A相,PB1为B相
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init (GPIOB ,&GPIO_InitStructure);//初始化GPIOB外设
	
	//配置AFIO 通过 AFIO 外设将 PB0 和 PB1 引脚映射到 EXTI 外设上,以便用于外部中断。
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);


	//配置EXTI	将第 0 条和第 1 条线路都初始化为中断模式,并指定下降沿触发中断
	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_Rising_Falling;//上升沿和下降沿都触发,遮挡和离开都+1
	//EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发,遮挡就+1
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发,离开就+1
	EXTI_Init(&EXTI_InitStructure); 
	
	//配置NVIC中断	置了抢占优先级和响应优先级,并使能了 EXTI0 和 EXTI1 的中断。
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选两位抢占两位响应
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//外部中断0
	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;//外部中断1
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定中断是使能还是失能(这里选使能)
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//响应优先级2大于1
	NVIC_Init (&NVIC_InitStructure);//调用NVIC_Init函数
	//PB0和PB1为同一抢占优先级,但PB1的响应优先级比PB0大,所以PB0可以被PB1打断
}

int8_t Encoder_Get(void)//获取中断服务函数中改变的数
{
	int8_t num;
	num=EncoderCount;
	return num;
}


void EXTI0_IRQHandler(void)//这个函数只有0这个引脚可以触发
{
	if(EXTI_GetFlagStatus (EXTI_Line0 )==SET)//外部中断0的线被触发,进入中断
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)//A下降沿,B低电平,反转
			{
				EncoderCount --;//计数--
			}
		}
		
		EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志位,跳出中断
	}
}
void EXTI1_IRQHandler(void)//这个函数只有1这个引脚可以触发
{
	if(EXTI_GetFlagStatus (EXTI_Line1)==SET)//外部中断1的线被触发,进入中断
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)//B下降沿,A低电平正转,
			{
				EncoderCount ++;//计数++
			}
		}
		
		EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位
	}
}

main.c

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

int main(void)
{
	int8_t num;
	OLED_Init();
	Encoder_Init();
	OLED_ShowString(1,1,"Num:");//第一行第一列显示字符串
	while(1)
	{
		num=Encoder_Get();
		OLED_ShowSignedNum(1,5,num,5);//第一行第5列显示5位数
	}
}

通过触发中断,改变num的值,可以直观感受外部中断和旋转编码器的原理及用途。完整的工程源码已经放在百度网盘中了,想要的可自取,有哪里不懂的或者解释有错误的欢迎在评论区留言。

作者:不就if else嘛

物联沃分享整理
物联沃-IOTWORD物联网 » stm32使用外部中断写旋转编码器(包教包会)

发表回复