旋转编码器
旋转编码器是一种可以左右旋转,同时也可以按下,也可以按下旋转的器件,通过左右旋转对应着内部不同开关的导通,同时按下也可以旋转,由此看来旋转编码器可以实现很复杂的功能,简单的通过左右旋转可以调节音量、亮暗等功能,按键可以发挥普通按键的作用,按下按键的同时左右旋转又可以区别普通旋转的按键,因此可以用一个旋转编码器同时调节音量和亮暗(举例),同时也可以通过不同的转速实现不同的功能,总之,功能很复杂,作为初学者,本次只介绍普通旋转和按键作用,同时分析卡死的问题。
开发板上的端口映射原理图如下:其中旋转编码器和模拟量摇杆线路复用,使用其中一个器件时,将对应的跳线帽断开即可。
旋转编码器的具体原理图如下所示,我们开发板中的1和4接地,如上图所示,按键按下时,开关1闭合,1和2接通,当向左右旋转时,开关2和3以一定的先后顺序闭合,也可能同时闭合,时序电路如下图所示,另外卡死是由于旋转编码器内部的机械结构不灵敏,正常情况下,每旋转一次,旋转编码器都会正常的咔哒一声,但是极少数情况下,会卡在咔哒声之前,即旋钮停在了两个格段之间,使得K2一直保持低电平,单片机退不出循环,从而导致卡死。
具体的程序如下,其中读取旋钮旋转的方向的方法有两种。1、同时读取两个按键的状态,如果同时读到K2是高电平、K3是低电平,则是方向1,如果同时读到K2是低电平、K3是高电平,则是方向2。2、只读取K2的状态,当K2的状态是低电平的时候,取一段及短的时间再判断K3的状态,若K3的状态是低电平,则是方向1,若K3的状态是高电平,则是方向2。卡死的状态变化通过标志位KUO以及累加COUT来判断。
读取程序如下:
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;
}
其中delay_us(100)是等待一小段时间,等待稳定之后检测K3的按键值,另外delay_ms(3)是消抖函数,因为在电平的变化时,会产生抖动,消抖之后如果仍然有效,则给相应的按键赋值。其中最后一段的锁死判断程序用了两个&&,即同时起作用,第一个条件是等待旋转之后的自动弹起,当弹起时,直接退出while语句这时KUP标志位为1,注意此时是正常的状态,通过单片机的不断扫描,下一次再进入读取函数后会通过第一个判断语句,将KUP清为0,如果真的是锁死状态则第一个条件恒为真,执行第二个为真的条件,这时延时大概1.2秒之后退出了循环(正常情况下,旋转的弹起时间必然小于1.2秒),这时仍然是锁死状态,单片机再次进入读取函数时KUP不会被清为0(因为锁死状态不满足if语句),因为一个KUP=1,所以以后的读取函数所有的语句都不被执行,这时单片机处于不被卡死的状态,即完成了卡死的程序处理。
主函数实例如下:
int main (void){//主程序
u8 a=0,b=0,c=0x01;
RCC_Configuration(); //系统时钟初始化
RTC_Config(); //RTC初始化
ENCODER_Init(); //旋转编码器初始化
TM1640_Init(); //TM1640初始化
TM1640_display(0,a/10); //显示数值
TM1640_display(1,a%10);
TM1640_display(2,20);
TM1640_display(3,20);
TM1640_display(4,20);
TM1640_display(5,20);
TM1640_display(6,20);
TM1640_display(7,20);
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){ //如果有旋转器的操作
TM1640_display(0,a/10); //显示数值
TM1640_display(1,a%10);
}
// TM1640_led(c); //与TM1640连接的8个LED全亮
// c<<=1; //数据左移 流水灯
// if(c==0x00)c=0x01; //8个灯显示完后重新开始
// delay_ms(150); //延时
}
}
这里需要注意的是,如果同时将下面的流水灯程序打开之后,因为延时函数的存在,会使得旋转按钮便的不灵敏,这是因为延时函数占用了CPU空间,从而使得扫描的频率变慢,使得灵敏度降低,解决的方法有两种。1、在延时函数中加入读取按键旋转的函数,同时扫描读取显示即可。2、将K2开关接入外部中断触发,即将读取函数放入中断函数,一旦旋转按钮,则直接从延时函数进入中断读取,进而判断扫描显示,这也是方法2的优势,只占用了一个中断资源。
图中所有图片出自洋桃电子。