認識EC11旋轉編碼器&編寫驅動程序

前言

拋開工作,以電子愛好者的身份,單片機玩多了都會想著在單片機的外圍設備玩出一些花樣來。
這其中首先想到的就是“升級”輸入輸出設備。
這里先說一說我們常給單片機使用的輸入設備。

  • 獨立按鍵
  • 矩陣鍵盤
  • ADC按鍵
  • ADC搖桿
  • 電位器
  • 五向按鍵
  • 撥輪開關
  • 旋轉編碼器

以上這些輸入設備,不是按鍵就是電位器。厭倦了在矩陣鍵盤里一個個的找按鍵,也厭倦了使用ADC掃描的方式來讀取輸入值。就只有撥輪開關和旋轉編碼器最簡潔了。其中撥輪開關本質上就是三個按鍵,就只剩下旋轉編碼器看起來比較有逼格了。

遇見

一開始我只是在示波器和數控電源上見到過旋轉編碼器,那時候我聽人說這叫數字電位器,就覺得很神奇,是什么結構竟然可以讓一個“電位器”無限的轉下去。后來才知道汽車的車機上用的也是旋轉編碼器。
接著在一樂看到的V8電子負載,V9電源,以及一些白光焊臺上也用的旋轉編碼器之后我就深深的喜歡上了這個精巧的小玩意。不多廢話,下面來了解一下旋轉編碼器。
旋轉編碼器作為人機交互的輸入設備,最常用的是EC11(類似的還有EC12、EC16等)

了解EC11

ALPS旋轉編碼器選型手冊.EC11實物圖.垂直型
施工中——ALPS旋轉編碼器選型手冊.EC11實物圖.側裝型
ALPS旋轉編碼器選型手冊.EC11垂直型機械尺寸圖
ALPS旋轉編碼器選型手冊.EC11引腳圖
ALPS旋轉編碼器選型手冊.EC11內部觸點開關結構圖
ALPS旋轉編碼器選型手冊.EC11輸出時序圖

EC11結構上由編碼器部分和按鍵部分(帶按鍵的型號)組合而成,編碼器部分有A,B,C三個引腳。按鍵部分有D,E兩個引腳,為獨立按鍵。

EC11測試電路原理圖.接外部上拉電阻.適用于浮空輸入和輸入上拉模式的IO口
EC11測試電路原理圖.無外部上拉電阻.幾乎所有單片機IO口都可以設置為輸入上拉模式,就可以省略EC11的外部上拉電阻

在研究EC11的時序之前首先要了解一點,EC11按旋轉的輸出動作可以分為兩種。一種是轉兩格,A、B對C端輸出一個完整脈沖(轉一格就只是由低電平->高電平或由高電平->低電平);另一種就是轉一格,A、B對C端輸出一個完整脈沖。

一定位一脈沖EC11轉動一格輸出波形圖

一定位一脈沖的EC11按測試電路圖的接法,在靜止的時候AB兩線輸出都是高電平。轉動一格,AB兩線各自輸出一個低電平脈沖,然后又回到高電平狀態。對應于EC11內部AB兩個觸點開關的動作為斷開-->閉合-->斷開。

</br>
</br>
</br>
</br>

兩定位一脈沖EC11轉動一格輸出波形圖.下降沿
兩定位一脈沖EC11轉動一格輸出波形圖.上升沿

兩定位一脈沖的EC11稍微復雜一些,轉動一格只會輸出半個脈沖。靜止時,AB觸點開關可以是斷開的也可以是閉合的。

  • 若初始狀態時AB都是高電平,轉動一格就輸出從高電平到低電平的下降沿,隨后一直輸出低電平。對應于EC11內部AB兩個觸電開關的動作為斷開-->閉合。
  • 若初始狀態時AB都是低電平,轉動一格就輸出從低電平到高電平的上升沿,隨后一直輸出低電平。對應于EC11內部AB兩個觸點開關的動作為閉合-->斷開。
    由于兩脈沖一定位的EC11會有兩種初始狀態,寫驅動程序就需要考慮多一些情況。再者,這類EC11在轉動到內部AB觸點一直閉合的時候,就相當于把上拉電阻的另一端接地,無形中加大了系統的功耗(若外接10K上拉電阻到5V電源就是500uA的電流),這對于低功耗應用來說是非常不利的。
    因此對于無特殊要求的人機輸入應用來說,我都推薦使用一定位一脈沖的EC11。

當然了,有一些質量比較差的EC11會有一些額外的問題要考慮,例如開關的抖動問題,例如轉動定位不清晰,靜止時AB兩個觸點都要閉合或者都要斷開才對,但是定位點不清晰,轉動的角度不到位導致一個觸點已經閉合(斷開)了,另一個觸點卻還保持在斷開(閉合)。對于這些問題我們在后面再做考慮。

在淘寶上搜EC11可以看到一些買家描述為


30定位15脈沖

20定位數20脈沖

??以上指的就是兩定位一脈沖或者一定位一脈沖類型的EC11
??要辨別的話也很簡單,拿一個位未知類型的EC11,看準一個點轉一圈,變轉圈邊數這一圈有多少格就知道了。一般轉一圈有30格的都是兩定位一脈沖的類型,20格的都是一定位一脈沖的類型。
??有些汽車車機上用來調音量的EC11是無步進的,旋轉的時候是均勻的阻尼感而不會有一格一格的步進手感,這種時候想辨別就需要用一個萬用表了。如果是一定位一脈沖的類型,不轉動的時候A,B于C端都不導通。如果是兩定位一脈沖的類型,會有A,B與C端導通和A,B與C端不導通兩種情況。稍微轉一下轉軸然后測量,若A,B與C端都導通,那么就是兩定位一脈沖類型。
雖然這種EC11無步進手感,但是大多數也是轉一圈輸出15脈沖(30格)或20脈沖(20格)的類型。

除此之外,大多數用的EC11是帶獨立按鍵的,可以實現單按鍵或者按下按鍵轉動的功能。但是我也遇到過一些國產的(主要是特別便宜的)EC11的結構不同會使得按下按鍵轉動的時候,按鍵會有送開的過程,這時候隨著EC11的轉軸轉動,按鍵輸出端會出現開-關-開的過程。即市面上有少部分國產的(并且是便宜貨)EC11編碼器無法實現按著按鍵轉動。

EC11按下按鍵轉動時序圖

當然,EC11也有長得不常規的,這類EC11由于按鍵和轉軸的連接結構不同,也是可以按下按鍵轉動的。
【施工中——松下滑動式旋轉編碼器.型號不詳】
【施工中——松下滑動式旋轉編碼器.拆解圖】
??從結構來看,這款旋轉編碼器的按鍵部分和編碼器部分是分離的,并且按鍵受轉軸擠壓按下時,轉軸仍可正常轉動而不影響按鍵。

??也還有一些是用于便攜消費電子產品的EC11。例如松下的EVQWK系列撥盤編碼器。
【施工中——松下EVQWK撥盤編碼器.具體型號不詳】
【施工中——松下EVQWK撥盤編碼器.數據手冊】
【施工中——松下EVQWK撥盤編碼器.機械結構圖】


時序圖

要寫驅動程序,得先了解EC11的工作過程。使用邏輯分析儀(LA)抓取時序可以很方便的從單片機的角度了解EC11的工作過程并依此來編寫驅動程序。
??EC11的編碼器部分有3個引腳,A,B,和C。通常可以把C端接GND,A,B端接到輸入上拉模式的IO口。可以取A或B任意一根線作為時鐘線,另一根作為信號輸出線。我個人習慣把A線作為時鐘線,B線作為信號線。
??本文中出現的邏輯分析儀抓取的時序圖中均是最上方通道為EC11的A線,視為時鐘;下方一個通道為EC11的B線,視為數據輸出。

沒有數據手冊時如何區別AB線

由于不同廠家的A,C,B引腳順序可能是反的,沒有數據手冊就只能看時序。除了EC11EH這個型號,其余的EC11正轉時A引腳的輸出脈沖相位超前于B引腳。反轉則相反,A滯后于B。


某國產側裝型EC11
ALPS側裝型EC11

一定位一脈沖EC11時序圖

一定位一脈沖EC11.典型正轉時序圖

實際操作順序:正轉一格-->停頓-->連續正轉-->停


其實把正轉的時序圖從右往左看,就是反轉的時序圖了。


一定位一脈沖EC11.典型反轉時序圖.PNG

實際操作順序:反轉一格-->停頓-->連續反轉-->停


時序圖解讀

若將EC11的A端視為時鐘,B端視為數據,整個EC11就可以視為根據時鐘脈沖輸出信號的同步元件。可以看做數據在時鐘的邊沿處輸出(時鐘線檢測邊沿,數據線檢測電平,這個思路編程最簡單
??正轉時,在時鐘線的下降沿處,數據線為高電平。或在時鐘線的上升沿處,數據線為低電平;
??反轉時,在時鐘線的下降沿處,數據線為低電平。或在時鐘線的上升沿處,數據線為高電平。
??可以不嚴謹的簡單總結為時鐘的下降沿處A、B反相為正轉,同相為反轉

EC11正反轉引腳動作 正轉 正轉 反轉 反轉
A 上升沿 下降沿 上升沿 下降沿
B L H H L

由于每轉一格,A,B兩線就會各自輸出一個完整的脈沖,因此我們可以僅檢測時鐘線的時鐘單邊沿(上升沿或者下降沿任選一個做檢測),根據時鐘線的邊沿處,信號線的電平來判斷EC11是正轉還是反轉。由于一定位一脈沖的EC11在正常情況下AB線初始狀態都是高電平,所以直接檢測時鐘線的下降沿更方便。

另一種方法也可以看做數據與時鐘的相位關系(檢測數據線和時鐘線哪根線的邊沿先出現,這個思路編程較為復雜,適合做硬件實現):正轉時,A線相位超前于B線相位;反轉時,B線相位超前于A線相位。使用簡單的數字邏輯電路(異或門與D觸發器)就可以識別到AB的相位關系與轉動次數。
詳見

基于FPGA的光電編碼器四倍頻電路設計
基于CPLD的增量式旋轉編碼器接口電路模塊設計

部分驅動程序(以C51為例)

  • 引腳定義
//----------------IO口定義----------------//
#define EC11_A_Now                    P36   //EC11的A引腳,視為時鐘線
#define EC11_B_Now                    P35    //EC11的B引腳,視為信號線
#define EC11_Key                      P37    //EC11的按鍵
  • 文件內需要的靜態局部變量
//----------------局部文件內變量列表----------------//
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的上升沿或下降沿
  • 部分EC11動作掃描代碼
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;   //更新編碼器上一個狀態暫存變量
        }
    }
return ScanResult;      //返回值的取值:   0:無動作;      1:正轉;           -1:反轉;
}

兩定位一脈沖EC11時序圖

兩定位一脈沖EC11.典型正轉時序圖

上圖 實際操作順序:正轉第一格-->停頓-->正轉第二格-->停頓-->連續正轉-->停


兩定位一脈沖EC11.典型反轉時序圖

上圖 實際操作順序:反轉第一格-->停頓-->反轉第二格-->停頓-->連續反轉-->停


兩定位一脈沖EC11.正轉-反轉-正轉-正轉-反轉時序圖

上圖 實際操作順序:正轉一格-->反轉一格-->正轉一格-->正轉一格-->反轉一格-->停


時序圖解讀

兩定位一脈沖的EC11時序稍微復雜一些。按照轉動的初始情況和先后順序可以細分為這6種情況:

  1. 初始AB線低電平正轉
  2. 初始AB線低電平反轉
  3. 初始AB線高電平正轉
  4. 初始AB線高電平反轉
  5. 正轉后反轉
  6. 反轉后正轉

由于這類EC11輸出一個脈沖已經轉了兩格,因此對于上升沿和下降沿都需要檢測。由因為要考慮正轉再反轉或者反轉再正轉的情形,用檢測相位超前或滯后的思路的程序除了要檢測A線的上升沿下降沿外,還要檢測B線的前后狀態。即時鐘線邊沿來到之前之后都需要檢測B的狀態并作比較。

部分驅動程序(以C51為例)

下面為檢測相位超前或滯后的方式掃描EC11

/*  這是對上面char Encoder_EC11_Scan()函數的補充。else對應的if為if(EC11_Type == 0) 即此處EC11_Type !=0  */                            
                            //======================================================//
    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;   //更新編碼器上一個狀態暫存變量
            return ScanResult;      //返回值的取值:   0:無動作;      1:正轉;           -1:反轉;
        }
    }                                                                   

按鍵檢測部分

我們自己用EC11,大多數時候都是選擇帶按鍵的,這就可以把按鍵檢測加入到EC11的動作掃描程序中,實現單獨的檢測按鍵以及按下按鍵時轉動轉軸。

完整的EC11掃描函數

在定時器中斷內調用該掃描函數即可

/*-------->>>>>>>>--------注意事項: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:反轉;
}       

EC11動作處理

掃描到EC11的動作后,可以在此函數內做出對應的動作處理。函數內包含了按鍵的單擊,雙擊,長按和長按松手檢測。

/*與按鍵檢測相關的靜態局部變量*/
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按鍵雙擊動作標志
//----------------編碼器參數微調宏定義----------------//
#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)       //長按按鍵的回報率的倒數,即一直長按按鍵時響應的時間間隔
//*******************************************************************/
//功能:對EC11旋轉編碼器的動作進行分析,并作出相應的動作處理代碼
//形參:無
//返回:char AnalyzeResult = 0;目前無用。若在該函數里做了動作處理,則函數的返回值無需理會
//詳解:對EC11旋轉編碼器的動作進行模式分析,是單擊還是雙擊還是長按松手還是一直按下。形參從 [ char Encoder_EC11_Scan(unsigned char Set_EC11_TYPE) ] 函數傳入。在本函數內修改需要的動作處理代碼
//*******************************************************************/
char Encoder_EC11_Analyze(char EC11_Value)
{
    char AnalyzeResult = 0;
    static unsigned int TMP_Value = 0;  //中間計數值,用于連續長按按鍵的動作延時間隔
    //>>>>>>>>>>>>>>>>編碼器正轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 1) //正轉
    {
        //--------編碼器正轉動作代碼--------//
        
        
    }

    //>>>>>>>>>>>>>>>>編碼器反轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == -1)    //反轉
    {
        //--------編碼器反轉動作代碼--------//
        
        
    }


    //>>>>>>>>>>>>>>>>編碼器按鍵按下并正轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 3)
    {
        //--------編碼器按鍵按下并正轉動作代碼--------//
        
        
    }

    //>>>>>>>>>>>>>>>>編碼器按鍵按下并反轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == -3)
    {
        //--------編碼器按鍵按下并反轉動作代碼--------//
        
        
    }


    //>>>>>>>>>>>>>>>>編碼器按鍵按下處理程序<<<<<<<<<<<<<<<<//
    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 = 1;
            //--------清除標志位--------//
            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 = 2;
            //--------清除標志位--------//
            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 = 4;
            }
        }

        //長按按鍵動作結束代碼
        if((FLAG_EC11_KEY_LongClick == 0)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //長按按鍵動作結束代碼
        {                                                                           
            //--------長按按鍵按下動作結束代碼--------//
            



            AnalyzeResult = 3;
            //--------清除標志位--------//
            EC11_KEY_COUNT = 0;
        }


    }
    return AnalyzeResult;
}


完整的代碼(C51)

我把整個EC11的驅動程序寫在EncoderEC11.h和EncoderEC11.c中。請按需自行移植。

  • EncoderEC11.h
//---->>>>----文件描述:EC11旋轉編碼器底層驅動程序---<<<<----//
//---->>>>----文件版本:V1.0----<<<<----//
#ifndef __EncoderEC11_H
#define __EncoderEC11_H

#include    "config.H"

//----------------IO口定義----------------//
#define EC11_A_Now                  P36                             //EC11的A引腳,視為時鐘線
#define EC11_B_Now                  P35                             //EC11的B引腳,視為信號線
#define EC11_Key                    P37                             //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按鍵雙擊動作標志



//----------------函數快速調用(復制粘貼)列表----------------//
//
/*******************************************************************
void Encoder_EC11_Init(unsigned char Set_EC11_TYPE);        //初始化EC11旋轉編碼器IO口和類型以及變量初始化
char Encoder_EC11_Scan();                                   //掃描旋轉編碼器的動作
void Encoder_EC11_Analyze(char EC11_Value);                 //分析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


//---->>>>----函數使用示例----<<<<----//
/********

#include "config.h"
#include "delay.h"
#include "EncoderEC11.h"


int cnt = -1;                                                                   //流水燈速查表偏移變量
unsigned char disp_tmp[] = {~0x01,~0x02,~0x04,~0x08,~0x10,~0x20,~0x40,~0x80};   //流水燈速查表

void Timer0Init(void)       //1毫秒@22.1184MHz
{
    AUXR &= 0x7F;       //定時器時鐘12T模式
    TMOD &= 0xF0;       //設置定時器模式
    TL0 = 0xCD;     //設置定時初值
    TH0 = 0xF8;     //設置定時初值
    TF0 = 0;        //清除TF0標志
    TR0 = 1;        //定時器0開始計時
}

void main()
{

    P1_QB_ALL();
    P2_QB_ALL();
    P3_QB_ALL();
    delay_ms(50);  //延時100毫秒等待所有MCU復位

    Encoder_EC11_Init(1);

    EA = 1;
    ET0 = 1;
    Timer0Init();
    while(1)
    {
            
    }
 
}

//-------->>>>>>>>--------注意事項:EC11旋轉編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉時可能會誤判旋轉方向--------<<<<<<<<--------//
void T0_ISR() interrupt 1
{
    static int tmp =0;
    Encoder_EC11_Analyze(Encoder_EC11_Scan());
    if(P33 == 0)
    {
        tmp ++;
        if(tmp == 500)
        {
            tmp =0;
            P27 = !P27;
        }
    }   
}


********/

  • 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)
{
    char AnalyzeResult = 0;
    static unsigned int TMP_Value = 0;  //中間計數值,用于連續長按按鍵的動作延時間隔
    //>>>>>>>>>>>>>>>>編碼器正轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 1) //正轉
    {
        //--------編碼器正轉動作代碼--------//
        switch(EC11_NUM_SW)
        {
            case 1: G_PWM_NUM1+=10; if(G_PWM_NUM1>255)G_PWM_NUM1 = 0;   break;
            case 2: G_PWM_NUM2+=10; if(G_PWM_NUM1>255)G_PWM_NUM2 = 0;   break;
            case 3: G_PWM_NUM3+=10; if(G_PWM_NUM1>255)G_PWM_NUM3 = 0;   break;
            case 4: G_PWM_NUM1+=10;     if(G_PWM_NUM1>255)G_PWM_NUM1 = 0;   G_PWM_NUM3 = G_PWM_NUM2 = G_PWM_NUM1;   break;
            case 5: G_PWM_NUM1+=20; if(G_PWM_NUM1>=255){G_PWM_NUM1 = 0;G_PWM_NUM3+=20;if(G_PWM_NUM3>=255){G_PWM_NUM3 = 0;G_PWM_NUM2+=20;if(G_PWM_NUM2 >=255)G_PWM_NUM2 = 0;}}
            default :break;
        }
        
    }

    //>>>>>>>>>>>>>>>>編碼器反轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == -1)    //反轉
    {
        //--------編碼器反轉動作代碼--------//
        switch(EC11_NUM_SW)
        {
            case 1: G_PWM_NUM1-=10; if(G_PWM_NUM1<0)G_PWM_NUM1 = 255;   break;
            case 2: G_PWM_NUM2-=10; if(G_PWM_NUM2<0)G_PWM_NUM1 = 255;   break;
            case 3: G_PWM_NUM3-=10; if(G_PWM_NUM3<0)G_PWM_NUM1 = 255;   break;
            case 4: G_PWM_NUM1-=10; if(G_PWM_NUM1<0)G_PWM_NUM1 = 255;   G_PWM_NUM3 = G_PWM_NUM2 = G_PWM_NUM1;   break;
            case 5: G_PWM_NUM1-=20; if(G_PWM_NUM1<0){G_PWM_NUM1 = 255;G_PWM_NUM3-=20;if(G_PWM_NUM3<0){G_PWM_NUM3 = 255;G_PWM_NUM2-=20;if(G_PWM_NUM2 <0)G_PWM_NUM2 = 255;}}
            default :break;
        }
        
    }


    //>>>>>>>>>>>>>>>>編碼器按鍵按下并正轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 3)
    {
        //--------編碼器按鍵按下并正轉動作代碼--------//
        
        
    }

    //>>>>>>>>>>>>>>>>編碼器按鍵按下并反轉處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == -3)
    {
        //--------編碼器按鍵按下并反轉動作代碼--------//
        
        
    }


    //>>>>>>>>>>>>>>>>編碼器按鍵按下處理程序<<<<<<<<<<<<<<<<//
    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))   //短按按鍵動作結束代碼
        {
            //--------短按按鍵動作結束代碼--------//
            EC11_NUM_SW++;
            if(EC11_NUM_SW >= 4)
                EC11_NUM_SW = 1;
            AnalyzeResult = 1;
            //--------清除標志位--------//
            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)) //雙擊按鍵動作結束代碼
        {
            //--------雙擊按鍵動作結束代碼--------//
            if(EC11_NUM_SW == 5)
                EC11_NUM_SW = 0;
            if(EC11_NUM_SW == 4)
                EC11_NUM_SW = 5;

            if(EC11_NUM_SW <4)
            {
                EC11_NUM_SW = 4;
            }
            AnalyzeResult = 2;
            //--------清除標志位--------//
            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 = 4;
            }
        }

        //長按按鍵動作結束代碼
        if((FLAG_EC11_KEY_LongClick == 0)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //長按按鍵動作結束代碼
        {                                                                           
            //--------長按按鍵按下動作結束代碼--------//
            EC11_NUM_SW = 0;
            G_PWM_NUM1 = 0x20;
            G_PWM_NUM2 = 0x20;
            G_PWM_NUM3 = 0x20;
            AnalyzeResult = 3;
            //--------清除標志位--------//
            EC11_KEY_COUNT = 0;
        }


    }
    return AnalyzeResult;
}


【施工中】未完待續……


后記

【施工中,有心情再寫】

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容