EC11旋转编码器驱动程序

   日期:2021-04-05     浏览:119    评论:0    
核心提示:EC11驱动程序不多做介绍,百度上很多相关的介绍首先,放上参考文档我是对该文档代码进行一些修改,偏向于我的习惯做的修改。主要是作者的代码在按下并旋转的时候会触发长按(也许是我移植的时候,改错了什么所导致的)、双击,我把它改为不触发。并且让函数做返回值处理,最终的按键操作在主函数或者单独写一个设置,便于后续移植。EncoderEC11.c//---->>>>----文件描述:EC11旋转编码器底层驱动程序---<<<<----////----

EC11驱动程序

不多做介绍,百度上很多相关的介绍
首先,放上参考文档
代码有正转、反转、按下正转、按下反转、单机、双击、长按和长安松手检测,有什么问题可以在下方留言

我是对该文档代码进行一些修改,偏向于我的习惯做的修改。
主要是作者的代码在按下并旋转的时候会触发长按(也许是我移植的时候,改错了什么所导致的)、双击,
我把它改为不触发。并且让函数做返回值处理,最终的按键操作在主函数或者单独写一个设置,便于后续移植。

  • EncoderEC11.c
//---->>>>----文件描述:EC11旋转编码器底层驱动程序---<<<<----//
//---->>>>----文件版本:V1.0----<<<<----//
#include "EncoderEC11.h"

//-------->>>>>>>>--------注意事项:EC11旋转编码器的扫描时间间隔控制在1~4ms之间,否则5ms及以上的扫描时间在快速旋转时可能会误判旋转方向--------<<<<<<<<--------//

/
//功能:初始化EC11旋转编码器相关参数
//形参:EC11旋转编码器的类型-->> unsigned char Set_EC11_TYPE <<-- :0----一定位对应一脉冲;1(或非0)----两定位对应一脉冲。
//返回:无
//详解:对EC11旋转编码器的连接IO口做IO口模式设置。以及将相关的变量进行初始化
/
void Encoder_EC11_Init(unsigned char Set_EC11_TYPE)
{ 
// //IO口模式初始化。初始化EC11的IO口为准双向模式
// P35_QB();
// P36_QB();
// P37_QB();
    
    EC11_A_Now = 1;
    EC11_B_Now = 1;
    EC11_Key = 1;

    //EC11类型选择:0-一定位一脉冲;1-两定位一脉冲
    if(Set_EC11_TYPE == 0)
    { 
        EC11_Type = 0;
    }
    else
    { 
        EC11_Type = 1;
    }

    //避免上电时EC11旋钮位置不确定导致一次动作误判
    EC11_A_Last = EC11_A_Now;   
    EC11_B_Last = EC11_B_Now;

    //--------清除按键计数器和标志位--------//
    EC11_KEY_COUNT = 0;                     //EC11按键动作计数器
    EC11_KEY_DoubleClick_Count = 0;         //EC11按键双击动作计数器
    FLAG_EC11_KEY_ShotClick = 0;            //EC11按键短按动作标志
    FLAG_EC11_KEY_LongClick = 0;            //EC11按键长按动作标志
    FLAG_EC11_KEY_DoubleClick = 0;          //EC11按键双击动作标志
}


/
//功能:扫描EC11旋转编码器的动作并将参数返回给动作分析函数使用
//形参:EC11旋转编码器的类型-->> unsigned char Set_EC11_TYPE <<-- :0----一定位对应一脉冲;1(或非0)----两定位对应一脉冲
//返回:EC11旋转编码器的扫描结果-->> char ScanResult -->> 0:无动作;1:正转; -1:反转;2:只按下按键;3:按着按键正转;-3:按着按键反转
//详解:只扫描EC11旋转编码器有没有动作,不关心是第几次按下按键或长按或双击。返回值直接作为形参传给 [ void Encoder_EC11_Analyze(char EC11_Value); ] 函数使用
/
char Encoder_EC11_Scan()
{ 
//以下储存A、B上一次值的变量声明为静态全局变量,方便对EC11对应的IO口做初始化
// static char EC11_A_Last = 0;
// static char EC11_B_Last = 0;
    char ScanResult = 0;    //返回编码器扫描结果,用于分析编码器的动作
                            //返回值的取值: 0:无动作; 1:正转; -1:反转; 
                            // 2:只按下按键; 3:按着按键正转; -3:按着按键反转

                            //======================================================//
    if(EC11_Type == 0)      //================一定位对应一脉冲的EC11================//
    {                        //======================================================//
        if(EC11_A_Now != EC11_A_Last)   //以A为时钟,B为数据。正转时AB反相,反转时AB同相
        { 
            if(EC11_A_Now == 0)
            { 
                if(EC11_B_Now ==1)      //只需要采集A的上升沿或下降沿的任意一个状态,若A下降沿时B为1,正转 
                    ScanResult = 1;     //正转

                else                    //反转
                    ScanResult = -1;
            }
            EC11_A_Last = EC11_A_Now;   //更新编码器上一个状态暂存变量
            EC11_B_Last = EC11_B_Now;   //更新编码器上一个状态暂存变量
        }
    }   
                            //======================================================//
    else                    //================两定位对应一脉冲的EC11================//
    {                        //======================================================//
        if(EC11_A_Now !=EC11_A_Last)        //当A发生跳变时采集B当前的状态,并将B与上一次的状态进行对比。
        {                                    //若A 0->1 时,B 1->0 正转;若A 1->0 时,B 0->1 正转;
                                            //若A 0->1 时,B 0->1 反转;若A 1->0 时,B 1->0 反转
            if(EC11_A_Now == 1)     //EC11_A和上一次状态相比,为上升沿
            { 
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次状态相比,为下降沿
                    ScanResult = 1;                         //正转

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次状态相比,为上升沿 
                    ScanResult = -1;                        //反转

                //>>>>>>>>>>>>>>>>下面为正转一次再反转或反转一次再正转处理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿时,采集的B不变且为0
                    ScanResult = 1;                                 //正转

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿时,采集的B不变且为1
                    ScanResult = -1;                                //反转
            }

            else                    //EC11_A和上一次状态相比,为下降沿
            { 
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次状态相比,为下降沿
                    ScanResult = -1;                        //反转

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次状态相比,为上升沿
                    ScanResult = 1;                         //正转

                //>>>>>>>>>>>>>>>>下面为正转一次再反转或反转一次再正转处理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿时,采集的B不变且为0
                    ScanResult = -1;                                //反转

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿时,采集的B不变且为1 
                    ScanResult = 1;                                 //正转

            }               
            EC11_A_Last = EC11_A_Now;   //更新编码器上一个状态暂存变量
            EC11_B_Last = EC11_B_Now;   //更新编码器上一个状态暂存变量
        }
    }                                                                       

    if(EC11_Key == 0)   //如果EC11的按键按下,并且没有EC11没有转动,
    { 
        if(ScanResult == 0)         //按下按键时未转动
            ScanResult = 2;         //返回值为2
        else
        { 
            if(ScanResult == 1)     //按下按键时候正转
                ScanResult = 3;     //返回值为3
            if(ScanResult == -1)    //按下按键时候反转
                ScanResult = -3;    //返回值为-3
        }
    }

    return ScanResult;      //返回值的取值: 0:无动作; 1:正转; -1:反转;
}                           // 2:只按下按键; 3:按着按键正转; -3:按着按键反转


/
//功能:对EC11旋转编码器的动作进行分析,并作出相应的动作处理代码
//形参:无
//返回:char AnalyzeResult = 0;目前无用。若在该函数里做了动作处理,则函数的返回值无需理会
//详解:对EC11旋转编码器的动作进行模式分析,是单击还是双击还是长按松手还是一直按下。形参从 [ char Encoder_EC11_Scan(unsigned char Set_EC11_TYPE) ] 函数传入。在本函数内修改需要的动作处理代码
/
char Encoder_EC11_Analyze(char EC11_Value)
{ 
		static bit FLAG_KEY_invalid;
    char AnalyzeResult = 0;
    static unsigned int TMP_Value = 0;  //中间计数值,用于连续长按按键的动作延时间隔
    //>>>>>>>>>>>>>>>>编码器正转处理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 1) //正转
    { 
        //--------编码器正转动作代码--------//
        AnalyzeResult = 1;
        
    }

    //>>>>>>>>>>>>>>>>编码器反转处理程序<<<<<<<<<<<<<<<<//
    else if(EC11_Value == -1)    //反转
    { 
        //--------编码器反转动作代码--------//
        AnalyzeResult = 2;
        
    }


    //>>>>>>>>>>>>>>>>编码器按键按下并正转处理程序<<<<<<<<<<<<<<<<//
    else if(EC11_Value == 3)
    { 
        //--------编码器按键按下并正转动作代码--------//
        AnalyzeResult = 3;
        
    }

    //>>>>>>>>>>>>>>>>编码器按键按下并反转处理程序<<<<<<<<<<<<<<<<//
    else if(EC11_Value == -3)
    { 
        //--------编码器按键按下并反转动作代码--------//
        AnalyzeResult = 4;
        
    }

		if((AnalyzeResult == 3) || (AnalyzeResult == 4))// 按下并转动
		{ 
			FLAG_KEY_invalid = 1;//使按键单操作无效
			EC11_KEY_COUNT = 0;
			FLAG_EC11_KEY_ShotClick = 0;
			FLAG_EC11_KEY_LongClick = 0;
			FLAG_EC11_KEY_DoubleClick = 0;
			EC11_KEY_DoubleClick_Count = 0;
		}

		if(FLAG_KEY_invalid == 0)
			{ 
				//>>>>>>>>>>>>>>>>编码器按键按下处理程序<<<<<<<<<<<<<<<<//
				if(EC11_Value == 2)     //====检测到按键按下====//
				{ 
						if(EC11_KEY_COUNT<10000)    //打开按键按下时间定时器
								EC11_KEY_COUNT++;
						if(EC11_KEY_COUNT == KEY_COUNT_DESHAKING)   //按下按键时间到达消抖时间时
						{                                            //置位短按按键标志
								FLAG_EC11_KEY_ShotClick = 1;
						}

						if((EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME))   //松开按键后,又在定时器在双击时间内按下按键
						{                                                                                                //置位双击按键标志
								FLAG_EC11_KEY_DoubleClick = 1;
						}

						if(EC11_KEY_COUNT == KEY_COUNT_LONGTIME)    //按下按键时间到达长按时间
						{                                            //置位长按按键标志并复位短按按键标志
								FLAG_EC11_KEY_LongClick = 1;
								FLAG_EC11_KEY_ShotClick = 0;
						}

				}
				else                    //====检测到按键松开====// 
				{ 
						if(EC11_KEY_COUNT < KEY_COUNT_DESHAKING)    //没到消抖时长就松开按键,复位所有定时器和按键标志
						{ 
								EC11_KEY_COUNT = 0;
								FLAG_EC11_KEY_ShotClick = 0;
								FLAG_EC11_KEY_LongClick = 0;
								FLAG_EC11_KEY_DoubleClick = 0;
								EC11_KEY_DoubleClick_Count = 0;
						}
						else
						{ 
								
								if(FLAG_EC11_KEY_ShotClick == 1)        //短按按键定时有效期间
								{ 
										if((FLAG_EC11_KEY_DoubleClick == 0)&&(EC11_KEY_DoubleClick_Count >= 0)) 
												EC11_KEY_DoubleClick_Count++;
										if((FLAG_EC11_KEY_DoubleClick == 1)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME))   //如果在规定双击时间内再次按下按键
										{                                                                                                //认为按键是双击动作
												FLAG_EC11_KEY_DoubleClick = 2;
										}   

										if((FLAG_EC11_KEY_DoubleClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME))    //如果没有在规定双击时间内再次按下按键
												FLAG_EC11_KEY_ShotClick = 0;                                                                //认为按键是单击动作
								}

								if(FLAG_EC11_KEY_LongClick == 1)        //检测到长按按键松开
										FLAG_EC11_KEY_LongClick = 0;
						}

				}

		
			//>>>>>>>>>>>>>>>>编码器按键分析处理程序<<<<<<<<<<<<<<<<//
			if(EC11_KEY_COUNT > KEY_COUNT_DESHAKING)    //短按按键延时到了时间
			{ 

					//短按按键动作结束代码
					if((FLAG_EC11_KEY_ShotClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME)&&(EC11_KEY_COUNT < KEY_COUNT_LONGTIME))   //短按按键动作结束代码
					{ 
							//--------短按按键动作结束代码--------//
							AnalyzeResult = 5;
							//--------清除标志位--------//
							EC11_KEY_COUNT = 0;
							EC11_KEY_DoubleClick_Count = 0;
							FLAG_EC11_KEY_DoubleClick = 0;
					}

					//双击按键动作结束代码
					if((FLAG_EC11_KEY_DoubleClick == 2)&&(EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME)) //双击按键动作结束代码
					{ 
							//--------双击按键动作结束代码--------//
							AnalyzeResult = 6;
							//--------清除标志位--------//
							EC11_KEY_COUNT = 0;
							EC11_KEY_DoubleClick_Count = 0;
							FLAG_EC11_KEY_ShotClick = 0;
							FLAG_EC11_KEY_DoubleClick = 0;
							
					}

					//连续长按按键按下代码
					if((FLAG_EC11_KEY_LongClick == 1)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //连续长按按键按下代码
					{ 
							TMP_Value ++;
							if(TMP_Value % KEY_LONG_REPEAT_TIME == 0)
							{ 
									TMP_Value = 0;
									//-------连续长按按键按下代码--------//
									AnalyzeResult = 8;
							}
					}

					//长按按键动作结束代码
					if((FLAG_EC11_KEY_LongClick == 0)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //长按按键动作结束代码
					{                                                                            
							//--------长按按键按下动作结束代码--------//
							AnalyzeResult = 7;
							//--------清除标志位--------//
							EC11_KEY_COUNT = 0;
					}
			}		
		}
		
		if(EC11_Key == 1)
		{ 
			FLAG_KEY_invalid = 0;
		}
    return AnalyzeResult;
		//1:正转
// 2:反转
// 3:按下并正转
// 4:按下并反转
// 5:短按
// 6:双击
// 7:长按结束
// 8:长按
}

  • EncoderEC11.h
//---->>>>----文件描述:EC11旋转编码器底层驱动程序---<<<<----//
//---->>>>----文件版本:V1.0----<<<<----//
#ifndef __EncoderEC11_H
#define __EncoderEC11_H

//#include "config.H"
#include <stc8.h>

//----------------IO口定义----------------//
#define EC11_A_Now P00 //EC11的A引脚,视为时钟线
#define EC11_B_Now P01 //EC11的B引脚,视为信号线
#define EC11_Key P02 //EC11的按键

//----------------编码器动作代码相关定义----------------//
extern  int G_PWM_NUM1;
extern  int G_PWM_NUM2;
extern  int G_PWM_NUM3;
static unsigned char EC11_NUM_SW = 0;

//----------------编码器参数微调宏定义----------------//
#define EC11_SCAN_PERIOD_MS 1 //EC11编码器扫描周期
#define KEY_COUNT_DESHAKING ( 20/EC11_SCAN_PERIOD_MS) //按键消抖时间
#define KEY_COUNT_LONGTIME (600/EC11_SCAN_PERIOD_MS) //长按按键判断时间
#define KEY_COUNT_DUALCLICKTIME (150/EC11_SCAN_PERIOD_MS) //双击按键判断时间
#define KEY_LONG_REPEAT_TIME (200/EC11_SCAN_PERIOD_MS) //长按按键的回报率的倒数,即一直长按按键时响应的时间间隔

//----------------局部文件内变量列表----------------//
static  char    EC11_A_Last = 0;                        //EC11的A引脚上一次的状态
static  char    EC11_B_Last = 0;                        //EC11的B引脚上一次的状态
static  char    EC11_Type = 1;                          //定义变量暂存EC11的类型---->>>>---- 0:一定位对应一脉冲; 1:两定位对应一脉冲
                                                        //所谓一定位对应一脉冲,是指EC11旋转编码器每转动一格,A和B都会输出一个完整的方波。
                                                        //而 两定位对应一脉冲,是指EC11旋转编码器每转动两格,A和B才会输出一个完整的方波,只转动一格只输出A和B的上升沿或下降沿
                                                        
static   int    EC11_KEY_COUNT = 0;                     //EC11按键动作计数器
static   int    EC11_KEY_DoubleClick_Count = 0;         //EC11按键双击动作计数器
static  char    FLAG_EC11_KEY_ShotClick = 0;            //EC11按键短按动作标志
static  char    FLAG_EC11_KEY_LongClick = 0;            //EC11按键长按动作标志
static  char    FLAG_EC11_KEY_DoubleClick = 0;          //EC11按键双击动作标志



//----------------函数快速调用(复制粘贴)列表----------------//
//

//-------->>>>>>>>--------注意事项:EC11旋转编码器的扫描时间间隔控制在1~4ms之间,否则5ms及以上的扫描时间在快速旋转时可能会误判旋转方向--------<<<<<<<<--------//
//-------->>>>>>>>--------注意事项:EC11旋转编码器的扫描时间间隔控制在1~4ms之间,否则5ms及以上的扫描时间在快速旋转时可能会误判旋转方向--------<<<<<<<<--------//
//-------->>>>>>>>--------注意事项:EC11旋转编码器的扫描时间间隔控制在1~4ms之间,否则5ms及以上的扫描时间在快速旋转时可能会误判旋转方向--------<<<<<<<<--------//

//----------------函数声明列表----------------//
//
/
//功能:初始化EC11旋转编码器相关参数
//形参:EC11旋转编码器的类型-->> unsigned char Set_EC11_TYPE <<-- :0----一定位对应一脉冲;1(或非0)----两定位对应一脉冲。
//返回:无
//详解:对EC11旋转编码器的连接IO口做IO口模式设置。以及将相关的变量进行初始化
/
void Encoder_EC11_Init(unsigned char Set_EC11_TYPE);

/
//功能:扫描EC11旋转编码器的动作并将参数返回给动作分析函数使用
//形参:EC11旋转编码器的类型-->> unsigned char Set_EC11_TYPE <<-- :0----一定位对应一脉冲;1(或非0)----两定位对应一脉冲
//返回:EC11旋转编码器的扫描结果-->> char ScanResult -->> 0:无动作;1:正转; -1:反转;2:只按下按键;3:按着按键正转;-3:按着按键反转
//详解:只扫描EC11旋转编码器有没有动作,不关心是第几次按下按键或长按或双击。返回值直接作为形参传给 [ void Encoder_EC11_Analyze(char EC11_Value); ] 函数使用
/
char Encoder_EC11_Scan();
    
/
//功能:对EC11旋转编码器的动作进行分析,并作出相应的动作处理代码
//形参:无
//返回:char AnalyzeResult = 0;目前无用。若在该函数里做了动作处理,则函数的返回值无需理会
//详解:对EC11旋转编码器的动作进行模式分析,是单击还是双击还是长按松手还是一直按下。形参从 [ char Encoder_EC11_Scan(unsigned char Set_EC11_TYPE) ] 函数传入。在本函数内修改需要的动作处理代码
/
char Encoder_EC11_Analyze(char EC11_Value);

#endif


//---->>>>----函数使用示例----<<<<----//


这里我买的编码旋钮不知道为什么无法把单片机准双向模式的IO口拉低(拿示波器才发现),于是设置为开漏模式,也可以设置为高阻模式

  • main.c
#include "EncoderEC11.h"
#include <stc8.h>
#include <stdio.h>

void Timer0Init(void);		//1毫秒@24.000MHz
void UartInit(void);		//115200bps@24.000MHz
void SeriPushSend(unsigned char *pStr);

//int dat;

void main(void)
{ 
// char str[5];
	P0M0 = 0xFF;//设置为开漏模式
	P0M1 = 0xFF;
	UartInit();		//115200bps@24.000MHz
	Timer0Init();		//1毫秒@24.000MHz
	Encoder_EC11_Init(0);//0-一定位对应一脉冲;1-两定位对应一脉冲
	ET0 = 1;
	EA = 1;
	while(1)
	{ 
// if(dat)
// { 
// sprintf(str,"%d\r\n",dat);
// SeriPushSend(str);
// dat = 0;
// }
	}
}

void timer0(void)	interrupt 1
{ 
	static int dat;
	char str[5];
	dat = Encoder_EC11_Analyze(Encoder_EC11_Scan());
	if(dat)
	{ 
		sprintf(str,"%d\r\n",dat);
		SeriPushSend(str);
// dat = 0;
	}
}

void Timer0Init(void)		//1毫秒@24.000MHz
{ 
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x40;		//设置定时初值
	TH0 = 0xA2;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}
void UartInit(void)		//115200bps@24.000MHz
{ 
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x40;		//定时器1时钟为Fosc,即1T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//设定定时器1为16位自动重装方式
	TL1 = 0xCC;		//设定定时初值
	TH1 = 0xFF;		//设定定时初值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}
void SeriPushSend(unsigned char *pStr)
{ 
	while(*pStr != '\0')//不是字符串结尾就一直执行
	{ 
		SBUF = *pStr;//首先发送第“0”个字符串
		while(TI == 0);//等待TI由硬件置一
		TI = 0;//TI置零
		pStr++;//地址++
	}
}

希望看完原作者文章的你,也能去注册并点个赞

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
更多>相关资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服