旋转编码器
增量编码器是一种将旋转位移转换为一连串数字脉冲信号的旋转式传感器。这些脉冲用来控制角位移。在Eltra编码器中角位移的转换采用了光电扫描原理。读数系统以由交替的透光窗口和不透光窗口构成的径向分度盘(码盘)的旋转为依据,同时被一个红外光源垂直照射,光把码盘的图像投射到接收器表面上。接收器覆盖着一层衍射光栅,它具有和码盘相同的窗口宽度。接收器的工作是感受光盘转动所产生的变化,然后将光变化转换成相应的电变化。再使低电平信号上升到较高电平,并产生没有任何干扰的方形脉冲,这就必须用电子电路来处理。读数系统通常采用差分方式,即将两个波形一样但相位差为180°的不同信号进行比较,以便提高输出信号的质量和稳定性。读数是再两个信号的差别基础上形成的,从而消除了干扰。
增量编码器给出两相方波,它们的相位差90°,通常称为A通道和B通道。其中一个通道给出与转速相关的信息,与此同时,通过两个通道信号进行顺序对比,得到旋转方向的信息。还有一个特殊信号称为Z或零通道,该通道给出编码器的绝对零位,此信号是一个方波与A通道方波的中心线重合。现在实现检测这个增量编码器正转还是反转 。
编码器时序
实物图如图
程序实现(STM32F103):
实现思路:使用IO口检测K2何时变为电平,后立即检测K3电平,K3若为低电平则为方向1,若为高电平,则为方向二。
连接关系
- VCC~3.3V
- GND~GND
- SW~PA7
- DT~PA6
- C LK~PB2
encoder.h
#ifndef __ENCODER_H
#define __ENCODER_H
#include "sys.h"
#include "delay.h"
#define ENCODER_PORT_A GPIOA //定义IO接口组
#define ENCODER_L GPIO_Pin_6 //定义IO接口,DT,旋钮左转
#define ENCODER_D GPIO_Pin_7 //定义IO接口,SW;旋钮按下
#define ENCODER_PORT_B GPIOB //定义IO接口组
#define ENCODER_R GPIO_Pin_2 //定义IO接口,CLK,旋钮右转
void ENCODER_Init(void);//初始化
u8 ENCODER_READ(void);
#endif
encoder.c
#include "encoder.h"
u8 KUP;//旋钮锁死标志(1为锁死)
u16 cou;
void ENCODER_Init(void){ //接口初始化
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO的初始化枚举结构
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStructure.GPIO_Pin = ENCODER_L | ENCODER_D; //选择端口号
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻
GPIO_Init(ENCODER_PORT_A,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ENCODER_R; //选择端口号
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻
GPIO_Init(ENCODER_PORT_B,&GPIO_InitStructure);
}
u8 ENCODER_READ(void){ //接口初始化
u8 a;//存放按键的值
u8 kt;
a=0;
if(GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L))KUP=0; //判断旋钮是否解除锁死
if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)&&KUP==0){ //判断是否旋转旋钮,同时判断是否有旋钮锁死
delay_us(100);
kt=GPIO_ReadInputDataBit(ENCODER_PORT_B,ENCODER_R); //把旋钮另一端电平状态记录
delay_ms(3); //延时
if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)){ //去抖
if(kt==0){ //用另一端判断左或右旋转
a=1;//右转
}else{
a=2;//左转
}
cou=0; //初始锁死判断计数器
while(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)&&cou<60000){ //等待放开旋钮,同时累加判断锁死
cou++;KUP=1;delay_us(20); //
}
}
}
if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_D)&&KUP==0){ //判断旋钮是否按下
delay_ms(20);
if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_D)){ //去抖动
a=3;//在按键按下时加上按键的状态值
//while(ENCODER_D==0); 等等旋钮放开
}
}
return a;
}
初始化,仅是对IO管脚初始化,值得关注的是全局变量KUP——旋钮锁死标志,该变量的设置是由于旋转编码器为机械结构,有可能旋钮K2由于某种卡死在低电平。造成程序崩溃,为解决这种问题,故有以下语句
while(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)&&cou<60000){ //等待放开旋钮,同时累加判断锁死
cou++;KUP=1;delay_us(20); //
}
检测是否松开旋钮一段时间,若长时间不松开旋钮,则锁死旋钮。使旋钮的值不发生变化,放当松开旋钮时,对该变量解锁。
if(GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L))KUP=0; //判断旋钮是否解除锁死
针对其他语句结构,仔细一看就好了,很容易理解的,这样就完成了对编码器旋转方向以及旋转角度的检测,接下来就可以在主程序中调用他吧。
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "i2c.h"
#include "oled0561.h"
#include "encoder.h"
int main (void){//主程序
u8 a=0,b=0,c=0x01;
RCC_Configuration(); //系统时钟初始化
RTC_Config(); //RTC初始化
ENCODER_Init(); //旋转编码器初始化
I2C_Configuration(); // IIC 初始化
OLED0561_Init(); // oled 初始化
OLED_DISPLAY_LIT(100);//亮度设置
OLED_DISPLAY_8x16(6,8*8,a/10+0x30);//显示
OLED_DISPLAY_8x16(6,9*8,a%10+0x30);//显示
while(1){
b=ENCODER_READ(); //读出旋转编码器值
if(b==1){a++;if(a>99)a=0;} //分析按键值,并加减计数器值。
if(b==2){if(a==0)a=100;a--;}
if(b==3)a=0;
if(b!=0){ //如果有旋转器的操作
OLED_DISPLAY_8x16(6,8*8,a/10+0x30);//显示
OLED_DISPLAY_8x16(6,9*8,a%10+0x30);//显示
}
}
}