【STM32入门】4.3旋转编码器计次

1.旋转编码器原理

旋转编码器旋转的时候A、B两个引脚会产生相位差90度的方波,这个地方要尤其注意:

1.方波的相位差决定了中断触发方式,是上升沿还是下降沿。

2.方波的相位差决定了判断逻辑。

逻辑:

1.A引脚的上升沿对应B引脚的低电平时,编码器是正转

2.B引脚的上升沿对应A引脚的低电平时,编码器是反转

因此,中断的触发方式我们选择上升沿触发,设置两个中断,一个由外部输入A触发,另一个由外部输入B触发,一旦监测到上升沿就进入中断程序,判断两个引脚的高低电平,从而判断出编码器是在正转还是反转。

2.新建工程(略)

3.硬件驱动

Encoder.c中首先要时钟使能和配置中断,使用中断的配置顺序见前面博文。主要是:

1.配置RCC时钟;

2.配置GPIO,端口配置为输入模式;

3.配置AFIO,选择GPIO链接到EXTI;

4.配置EXTI,选择边沿触发方式和触发的响应方式;

5.配置NVIC,配置中断优先级。

3.1配置RCC时钟

使能GPIOB和AFIO的时钟,因为编码器是接在了GPIOB0/1上,所以必须使能这一片区域。

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟,因为传感器引脚接在了GPIOB
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//AFIO时钟开启

3.2配置GPIO

跟往常一样定义GPIO的结构体完成配置,GPIO_Mode选择上拉输入,使能的管脚是GPIOB_0和GPIOB_1。

    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//EXTI输入,推荐配置是浮空,上拉或下拉
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz ;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

3.3配置AFIO

其实就是AFIO中断线的定义,函数第一个参数是GPIO片区,我们用的是GPIOB,第二个参数是本片区的哪个管脚链接到AFIO,是0号和1号。(两个基佬???)

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//参见函数定义,实际上是用来选择GPIO_pin作为中断线,第一个参数是GPIO分区,
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);

3.4配置EXTI

EXTI的配置也是采用结构体形式,首先是中断线,本次用了两个中断,分别为EXTI_Line0和EXTI_Line1。触发方式选择上升沿:EXTI_Trigger_Rsing。这个很重要

    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_Rising;//触发中断方式,选择上升沿触发
	EXTI_Init(&EXTI_InitStructure);

3.5.配置NVIC

因为由两个中断,因此NVIC配置两次,分组2方式,结构体只用定义一次,第二次配置NVIC的时候可以直接引用参数结构体。第一个中断的优先级是1,第二个是2.我现在还没整明白这一块。

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先集分组定义
	//调用NVIC初始化函数
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//指定NVIC的通道
	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的通道
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_Init(&NVIC_InitStructure);

3.6定义中断函数

分别定义两个中断函数EXTI0_IRQHandler和EXTI1_IRQHandler。基本逻辑跟第一小节讲的一样,先判断第一个中断是不是被触发了,即A管脚是否是上升沿。A管脚接的是GPIOB_0,对应的中断0.然后判断B管脚是不是低电平。如果是那就是在正转,因此Encoder_Count计数加一,否则就减去1.这里注意,教程里老师讲错了。

void EXTI0_IRQHandler(void)//正转:A的上升沿对应B的低电平;反转:B的上升沿对应A的低电平。
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)//判断编码器A是否为高电平,中断是否触发
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) ==0)//判断编码器B是否为低电平
		{
			Encoder_Count++;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);//如果是,则清空标志位
	}


}
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)//判断编码器B是否为高电平,中断是否触发
	{
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) ==0)//判断编码器A是否为低电平
		{
			Encoder_Count--;
		
		}
		EXTI_ClearITPendingBit(EXTI_Line1);//如果是,则清空标志位
	}


}

3.7完整的Encoder.c

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;//定义带符号变量,因为涉及正转和反转
void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟,因为传感器引脚接在了GPIOB
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//AFIO时钟开启
	//EXTI和NVIC的时钟默认打开,不需要配置;NVIC是内核外设,不用开时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//EXTI输入,推荐配置是浮空,上拉或下拉
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz ;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//AFIO外设并没有分配专门的库函数,它的库函数跟GPIO在同一个文件中,GPIO.h中第350行开始就是AFIO的库函数了
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//参见函数定义,实际上是用来选择GPIO_pin作为中断线,第一个参数是GPIO分区,
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
	//第二是引脚号,
	//第四步,配置EXTI
	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_Rising;//触发中断方式,选择上升沿触发
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先集分组定义
	//调用NVIC初始化函数
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//指定NVIC的通道
	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的通道
	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)//正转:A的上升沿对应B的低电平;反转:B的上升沿对应A的低电平。
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)//判断编码器A是否为高电平,中断是否触发
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) ==0)//判断编码器B是否为低电平
		{
			Encoder_Count++;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);//如果是,则清空标志位
	}


}
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)//判断编码器B是否为高电平,中断是否触发
	{
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) ==0)//判断编码器A是否为低电平
		{
			Encoder_Count--;
		
		}
		EXTI_ClearITPendingBit(EXTI_Line1);//如果是,则清空标志位
	}


}

4.主函数

首先定义一个有符号的整型变量Num。在死循环中,调用Encoder_Get()函数,这个函数的具体定义在完整的中断函数中,作用是返回当前的计数值。

在死循环中的操作就是:新的旋转计数=已有的计数+Encoder_Get(),而这个Encoder_Get()的返回值,如果是正转就是+1,反转就是-1.

#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);
	}
}

你可能感兴趣的:(stm32,嵌入式硬件,单片机)