1、初识编码器,像示波器的旋转按钮,可左旋右旋,还可以按下,我们使用的是Grayhill编码器,如下图所示:
从图中可以看出,该编码器一共有6个IO,从1-6分别为GND,GND,C,B,A,VCC,。
其中VCC和GND接3.3V和GND,A、B对应旋转时电平的跳变IO,C对应按下时电平的跳变IO。
(1) 硬件电路设计上,为了方便代码编写与理解,最好把编码器的A、B接到单片机相邻的GPIO中,即使用同一个中断处理函数,比如本设计A、B分别接到PB12和PB11。C接任一个GPIO,当成中断使用。
(2) 顺时针和逆时针旋转编码器时,慢慢旋转一个单位(手指头会有明显旋转到位的感觉),用万用表测量A、B对应的IO电平,并记录下来。旋转一圈为止。如下表是我使用的编码器,在顺、逆时针旋转一圈时所记录下的IO电平(旋4次即满一圈):
(实际使用中,旋转了24小格才满一圈,这里只记录方法,不记录确切的值,需要自己测量)
|
|
旋转前 |
旋1/4圈 |
旋2/4圈 |
旋3/4圈 |
旋4/4圈 |
顺时针 |
A电平 |
0 |
1 |
1 |
0 |
0 |
B电平 |
0 |
0 |
1 |
1 |
0 |
|
逆时针 |
A电平 |
0 |
0 |
1 |
1 |
0 |
B电平 |
0 |
1 |
1 |
0 |
0 |
表1 顺、逆时针旋转编码器时A、B对应IO电平
2、A、B对应的IO初始化成中断双边沿触发方式,如下所示:
void KeyA_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the EXTI_PB1 Clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12); //配置PB12管脚为外部中断线路用
EXTI_InitStructure.EXTI_Line = EXTI_Line12; //配置为外部中断线2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //输入线路下降沿为中断请求
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //配置为外部中断2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure);
}
void KeyB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the EXTI_PB1 Clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); //配置PB11管脚为外部中断线路用
EXTI_InitStructure.EXTI_Line = EXTI_Line11; //配置为外部中断线11
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //输入线路下降沿为中断请求
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //配置为外部中断2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure);
}
3、根据表1设计的中断处理函数如下所示,使用TurningDir变量来标识检测到的旋转方向,TurnLeft表示逆时针(对应数值可以理解为减),TurnRight表示顺时针(对应数值可以理解为加):
typedef enum {TurnNone=0,TurnRight , TurnLeft}TurnDir ;
TurnDir TurningDir=TurnNone;//default turning dir = none
void EXTI15_10_IRQHandler(void)
{
u8 KeyAValue=0;
u8 KeyBValue=0;
//key left & right
if (EXTI_GetITStatus(EXTI_Line12) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line12);
KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
// printf("AB=%d,%d\r\n",KeyAValue,KeyBValue);
if(KeyAValue==1)
{
if(KeyBValue==0)
{
TurningDir=TurnLeft;
}
else
{
TurningDir=TurnRight;
}
}
else if(KeyAValue==0)
{
if(KeyBValue==1)
{
TurningDir=TurnLeft;
}
else
{
TurningDir=TurnRight;
}
}
}
if (EXTI_GetITStatus(EXTI_Line11) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line11);
KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
if(KeyBValue==1)
{
if(KeyAValue==0)
{
TurningDir=TurnRight;
}
else
{
TurningDir=TurnLeft;
}
}
else if(KeyBValue==0)
{
if(KeyAValue==1)
{
TurningDir=TurnRight;
}
else
{
TurningDir=TurnLeft;
}
}
}
}
编码器旋钮的按下功能,即C,可以把它当成一个普通的按键,软件设置成上拉输入,按下时为低电平,弹起时为高,同样也使用中断操作,这里就不再描述。
至此,编码器软件设计完成。经过实测,能完美检测左旋右旋。
如果你有什么问题,欢迎留言。
我们还建了一个QQ群,可以讨论相关内容,群号是:597254771