認識EC11旋轉(zhuǎn)編碼器&編寫驅(qū)動程序

前言

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

  • 獨立按鍵
  • 矩陣鍵盤
  • ADC按鍵
  • ADC搖桿
  • 電位器
  • 五向按鍵
  • 撥輪開關(guān)
  • 旋轉(zhuǎn)編碼器

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

遇見

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

了解EC11

ALPS旋轉(zhuǎn)編碼器選型手冊.EC11實物圖.垂直型
施工中——ALPS旋轉(zhuǎn)編碼器選型手冊.EC11實物圖.側(cè)裝型
ALPS旋轉(zhuǎn)編碼器選型手冊.EC11垂直型機械尺寸圖
ALPS旋轉(zhuǎn)編碼器選型手冊.EC11引腳圖
ALPS旋轉(zhuǎn)編碼器選型手冊.EC11內(nèi)部觸點開關(guān)結(jié)構(gòu)圖
ALPS旋轉(zhuǎn)編碼器選型手冊.EC11輸出時序圖

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

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

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

一定位一脈沖EC11轉(zhuǎn)動一格輸出波形圖

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

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

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

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

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

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

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


30定位15脈沖

20定位數(shù)20脈沖

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

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

EC11按下按鍵轉(zhuǎn)動時序圖

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

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


時序圖

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

沒有數(shù)據(jù)手冊時如何區(qū)別AB線

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


某國產(chǎn)側(cè)裝型EC11
ALPS側(cè)裝型EC11

一定位一脈沖EC11時序圖

一定位一脈沖EC11.典型正轉(zhuǎn)時序圖

實際操作順序:正轉(zhuǎn)一格-->停頓-->連續(xù)正轉(zhuǎn)-->停


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


一定位一脈沖EC11.典型反轉(zhuǎn)時序圖.PNG

實際操作順序:反轉(zhuǎn)一格-->停頓-->連續(xù)反轉(zhuǎn)-->停


時序圖解讀

若將EC11的A端視為時鐘,B端視為數(shù)據(jù),整個EC11就可以視為根據(jù)時鐘脈沖輸出信號的同步元件??梢钥醋鰯?shù)據(jù)在時鐘的邊沿處輸出(時鐘線檢測邊沿,數(shù)據(jù)線檢測電平,這個思路編程最簡單
??正轉(zhuǎn)時,在時鐘線的下降沿處,數(shù)據(jù)線為高電平。或在時鐘線的上升沿處,數(shù)據(jù)線為低電平;
??反轉(zhuǎn)時,在時鐘線的下降沿處,數(shù)據(jù)線為低電平?;蛟跁r鐘線的上升沿處,數(shù)據(jù)線為高電平。
??可以不嚴謹?shù)暮唵慰偨Y(jié)為時鐘的下降沿處A、B反相為正轉(zhuǎn),同相為反轉(zhuǎn)

EC11正反轉(zhuǎn)引腳動作 正轉(zhuǎn) 正轉(zhuǎn) 反轉(zhuǎn) 反轉(zhuǎn)
A 上升沿 下降沿 上升沿 下降沿
B L H H L

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

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

基于FPGA的光電編碼器四倍頻電路設(shè)計
基于CPLD的增量式旋轉(zhuǎn)編碼器接口電路模塊設(shè)計

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

  • 引腳定義
//----------------IO口定義----------------//
#define EC11_A_Now                    P36   //EC11的A引腳,視為時鐘線
#define EC11_B_Now                    P35    //EC11的B引腳,視為信號線
#define EC11_Key                      P37    //EC11的按鍵
  • 文件內(nèi)需要的靜態(tài)局部變量
//----------------局部文件內(nèi)變量列表----------------//
static  char    EC11_A_Last = 0;                        //EC11的A引腳上一次的狀態(tài)
static  char    EC11_B_Last = 0;                        //EC11的B引腳上一次的狀態(tài)
static  char    EC11_Type = 1;          //定義變量暫存EC11的類型---->>>>----  0:一定位對應(yīng)一脈沖;  1:兩定位對應(yīng)一脈沖
//所謂一定位對應(yīng)一脈沖,是指EC11旋轉(zhuǎn)編碼器每轉(zhuǎn)動一格,A和B都會輸出一個完整的方波。
//而  兩定位對應(yīng)一脈沖,是指EC11旋轉(zhuǎn)編碼器每轉(zhuǎn)動兩格,A和B才會輸出一個完整的方波,只轉(zhuǎn)動一格只輸出A和B的上升沿或下降沿
  • 部分EC11動作掃描代碼
char Encoder_EC11_Scan()     /*  這里只是部分代碼  */
{
//以下儲存A、B上一次值的變量聲明為靜態(tài)全局變量,方便對EC11對應(yīng)的IO口做初始化
//  static char EC11_A_Last = 0;
//  static char EC11_B_Last = 0;
    char ScanResult = 0;    //返回編碼器掃描結(jié)果,用于分析編碼器的動作
                            //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);  
                            //                  2:只按下按鍵;    3:按著按鍵正轉(zhuǎn);   -3:按著按鍵反轉(zhuǎn)

                            //======================================================//
    if(EC11_Type == 0)      //================一定位對應(yīng)一脈沖的EC11================//
    {                       //======================================================//
        if(EC11_A_Now != EC11_A_Last)   //以A為時鐘,B為數(shù)據(jù)。正轉(zhuǎn)時AB反相,反轉(zhuǎn)時AB同相
        {
            if(EC11_A_Now == 0)
            {
                if(EC11_B_Now ==1)      //只需要采集A的上升沿或下降沿的任意一個狀態(tài),若A下降沿時B為1,正轉(zhuǎn)                    
                    ScanResult = 1;     //正轉(zhuǎn)

                else                    //反轉(zhuǎn)
                    ScanResult = -1;
            }
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
        }
    }
return ScanResult;      //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);
}

兩定位一脈沖EC11時序圖

兩定位一脈沖EC11.典型正轉(zhuǎn)時序圖

上圖 實際操作順序:正轉(zhuǎn)第一格-->停頓-->正轉(zhuǎn)第二格-->停頓-->連續(xù)正轉(zhuǎn)-->停


兩定位一脈沖EC11.典型反轉(zhuǎn)時序圖

上圖 實際操作順序:反轉(zhuǎn)第一格-->停頓-->反轉(zhuǎn)第二格-->停頓-->連續(xù)反轉(zhuǎn)-->停


兩定位一脈沖EC11.正轉(zhuǎn)-反轉(zhuǎn)-正轉(zhuǎn)-正轉(zhuǎn)-反轉(zhuǎn)時序圖

上圖 實際操作順序:正轉(zhuǎn)一格-->反轉(zhuǎn)一格-->正轉(zhuǎn)一格-->正轉(zhuǎn)一格-->反轉(zhuǎn)一格-->停


時序圖解讀

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

  1. 初始AB線低電平正轉(zhuǎn)
  2. 初始AB線低電平反轉(zhuǎn)
  3. 初始AB線高電平正轉(zhuǎn)
  4. 初始AB線高電平反轉(zhuǎn)
  5. 正轉(zhuǎn)后反轉(zhuǎn)
  6. 反轉(zhuǎn)后正轉(zhuǎn)

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

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

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

/*  這是對上面char Encoder_EC11_Scan()函數(shù)的補充。else對應(yīng)的if為if(EC11_Type == 0) 即此處EC11_Type !=0  */                            
                            //======================================================//
    else                    //================兩定位對應(yīng)一脈沖的EC11================//
    {                      //======================================================//
        if(EC11_A_Now !=EC11_A_Last)        //當(dāng)A發(fā)生跳變時采集B當(dāng)前的狀態(tài),并將B與上一次的狀態(tài)進行對比。
        {                                   //若A 0->1 時,B 1->0 正轉(zhuǎn);若A 1->0 時,B 0->1 正轉(zhuǎn);
                                            //若A 0->1 時,B 0->1 反轉(zhuǎn);若A 1->0 時,B 1->0 反轉(zhuǎn)
            if(EC11_A_Now == 1)     //EC11_A和上一次狀態(tài)相比,為上升沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿               
                    ScanResult = -1;                        //反轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = 1;                                 //正轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1
                    ScanResult = -1;                                //反轉(zhuǎn)
            }

            else                    //EC11_A和上一次狀態(tài)相比,為下降沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = -1;                        //反轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = -1;                                //反轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1   
                    ScanResult = 1;                                 //正轉(zhuǎn)

            }               
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
            return ScanResult;      //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);
        }
    }                                                                   

按鍵檢測部分

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

完整的EC11掃描函數(shù)

在定時器中斷內(nèi)調(diào)用該掃描函數(shù)即可

/*-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,
否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------*/
//*******************************************************************/
//功能:初始化EC11旋轉(zhuǎn)編碼器相關(guān)參數(shù)
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖。
//返回:無
//詳解:對EC11旋轉(zhuǎn)編碼器的連接IO口做IO口模式設(shè)置。以及將相關(guān)的變量進行初始化
//*******************************************************************/
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旋鈕位置不確定導(dǎo)致一次動作誤判
    EC11_A_Last = EC11_A_Now;   
    EC11_B_Last = EC11_B_Now;

    //--------清除按鍵計數(shù)器和標(biāo)志位--------//
    EC11_KEY_COUNT = 0;                     //EC11按鍵動作計數(shù)器
    EC11_KEY_DoubleClick_Count = 0;         //EC11按鍵雙擊動作計數(shù)器
    FLAG_EC11_KEY_ShotClick = 0;            //EC11按鍵短按動作標(biāo)志
    FLAG_EC11_KEY_LongClick = 0;            //EC11按鍵長按動作標(biāo)志
    FLAG_EC11_KEY_DoubleClick = 0;          //EC11按鍵雙擊動作標(biāo)志
}



//*******************************************************************/
//功能:掃描EC11旋轉(zhuǎn)編碼器的動作并將參數(shù)返回給動作分析函數(shù)使用
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖
//返回:EC11旋轉(zhuǎn)編碼器的掃描結(jié)果-->>  char ScanResult  -->>  0:無動作;1:正轉(zhuǎn); -1:反轉(zhuǎn);2:只按下按鍵;
//                                                       3:按著按鍵正轉(zhuǎn);-3:按著按鍵反轉(zhuǎn)
//詳解:只掃描EC11旋轉(zhuǎn)編碼器有沒有動作,不關(guān)心是第幾次按下按鍵或長按或雙擊。
//返回值直接作為形參傳給 [ void Encoder_EC11_Analyze(char EC11_Value); ] 函數(shù)使用
//*******************************************************************/
char Encoder_EC11_Scan()
{
//以下儲存A、B上一次值的變量聲明為靜態(tài)全局變量,方便對EC11對應(yīng)的IO口做初始化
//  static char EC11_A_Last = 0;
//  static char EC11_B_Last = 0;
    char ScanResult = 0;    //返回編碼器掃描結(jié)果,用于分析編碼器的動作
                            //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);  
                            //                  2:只按下按鍵;    3:按著按鍵正轉(zhuǎn);   -3:按著按鍵反轉(zhuǎn)

                            //======================================================//
    if(EC11_Type == 0)      //================一定位對應(yīng)一脈沖的EC11================//
    {                       //======================================================//
        if(EC11_A_Now != EC11_A_Last)   //以A為時鐘,B為數(shù)據(jù)。正轉(zhuǎn)時AB反相,反轉(zhuǎn)時AB同相
        {
            if(EC11_A_Now == 0)
            {
                if(EC11_B_Now ==1)      //只需要采集A的上升沿或下降沿的任意一個狀態(tài),若A下降沿時B為1,正轉(zhuǎn)                    
                    ScanResult = 1;     //正轉(zhuǎn)

                else                    //反轉(zhuǎn)
                    ScanResult = -1;
            }
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
        }
    }   
                            //======================================================//
    else                    //================兩定位對應(yīng)一脈沖的EC11================//
    {                       //======================================================//
        if(EC11_A_Now !=EC11_A_Last)        //當(dāng)A發(fā)生跳變時采集B當(dāng)前的狀態(tài),并將B與上一次的狀態(tài)進行對比。
        {                                   //若A 0->1 時,B 1->0 正轉(zhuǎn);若A 1->0 時,B 0->1 正轉(zhuǎn);
                                            //若A 0->1 時,B 0->1 反轉(zhuǎn);若A 1->0 時,B 1->0 反轉(zhuǎn)
            if(EC11_A_Now == 1)     //EC11_A和上一次狀態(tài)相比,為上升沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿               
                    ScanResult = -1;                        //反轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = 1;                                 //正轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1
                    ScanResult = -1;                                //反轉(zhuǎn)
            }

            else                    //EC11_A和上一次狀態(tài)相比,為下降沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = -1;                        //反轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = -1;                                //反轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1   
                    ScanResult = 1;                                 //正轉(zhuǎn)

            }               
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
        }
    }                                                                       

    if(EC11_Key == 0)   //如果EC11的按鍵按下,并且沒有EC11沒有轉(zhuǎn)動,
    {
        if(ScanResult == 0)         //按下按鍵時未轉(zhuǎn)動
            ScanResult = 2;         //返回值為2
        else
        {
            if(ScanResult == 1)     //按下按鍵時候正轉(zhuǎn)
                ScanResult = 3;     //返回值為3
            if(ScanResult == -1)    //按下按鍵時候反轉(zhuǎn)
                ScanResult = -3;    //返回值為-3
        }
    }

    return ScanResult;      //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);
}       

EC11動作處理

掃描到EC11的動作后,可以在此函數(shù)內(nèi)做出對應(yīng)的動作處理。函數(shù)內(nèi)包含了按鍵的單擊,雙擊,長按和長按松手檢測。

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

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


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

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


    //>>>>>>>>>>>>>>>>編碼器按鍵按下處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 2)     //====檢測到按鍵按下====//
    {
        if(EC11_KEY_COUNT<10000)    //打開按鍵按下時間定時器
            EC11_KEY_COUNT++;
        if(EC11_KEY_COUNT == KEY_COUNT_DESHAKING)   //按下按鍵時間到達消抖時間時
        {                                           //置位短按按鍵標(biāo)志
            FLAG_EC11_KEY_ShotClick = 1;
        }

        if((EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME))   //松開按鍵后,又在定時器在雙擊時間內(nèi)按下按鍵
        {                                                                                               //置位雙擊按鍵標(biāo)志
            FLAG_EC11_KEY_DoubleClick = 1;
        }

        if(EC11_KEY_COUNT == KEY_COUNT_LONGTIME)    //按下按鍵時間到達長按時間
        {                                           //置位長按按鍵標(biāo)志并復(fù)位短按按鍵標(biāo)志
            FLAG_EC11_KEY_LongClick = 1;
            FLAG_EC11_KEY_ShotClick = 0;
        }

    }
    else                    //====檢測到按鍵松開====//     
    {
        if(EC11_KEY_COUNT < KEY_COUNT_DESHAKING)    //沒到消抖時長就松開按鍵,復(fù)位所有定時器和按鍵標(biāo)志
        {
            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))   //如果在規(guī)定雙擊時間內(nèi)再次按下按鍵
                {                                                                                               //認為按鍵是雙擊動作
                    FLAG_EC11_KEY_DoubleClick = 2;
                }   

                if((FLAG_EC11_KEY_DoubleClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME))    //如果沒有在規(guī)定雙擊時間內(nèi)再次按下按鍵
                    FLAG_EC11_KEY_ShotClick = 0;                                                                //認為按鍵是單擊動作
            }

            if(FLAG_EC11_KEY_LongClick == 1)        //檢測到長按按鍵松開
                FLAG_EC11_KEY_LongClick = 0;
        }

    }


    //>>>>>>>>>>>>>>>>編碼器按鍵分析處理程序<<<<<<<<<<<<<<<<//
    if(EC11_KEY_COUNT > KEY_COUNT_DESHAKING)    //短按按鍵延時到了時間
    {

        //短按按鍵動作結(jié)束代碼
        if((FLAG_EC11_KEY_ShotClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME)&&(EC11_KEY_COUNT < KEY_COUNT_LONGTIME))   //短按按鍵動作結(jié)束代碼
        {
            //--------短按按鍵動作結(jié)束代碼--------//
            


            AnalyzeResult = 1;
            //--------清除標(biāo)志位--------//
            EC11_KEY_COUNT = 0;
            EC11_KEY_DoubleClick_Count = 0;
            FLAG_EC11_KEY_DoubleClick = 0;
        }

        //雙擊按鍵動作結(jié)束代碼
        if((FLAG_EC11_KEY_DoubleClick == 2)&&(EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME)) //雙擊按鍵動作結(jié)束代碼
        {
            //--------雙擊按鍵動作結(jié)束代碼--------//
            


            AnalyzeResult = 2;
            //--------清除標(biāo)志位--------//
            EC11_KEY_COUNT = 0;
            EC11_KEY_DoubleClick_Count = 0;
            FLAG_EC11_KEY_ShotClick = 0;
            FLAG_EC11_KEY_DoubleClick = 0;
            
        }

        //連續(xù)長按按鍵按下代碼
        if((FLAG_EC11_KEY_LongClick == 1)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //連續(xù)長按按鍵按下代碼
        {
            TMP_Value ++;
            if(TMP_Value % KEY_LONG_REPEAT_TIME == 0)
            {
                TMP_Value = 0;
                //-------連續(xù)長按按鍵按下代碼--------//



                AnalyzeResult = 4;
            }
        }

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



            AnalyzeResult = 3;
            //--------清除標(biāo)志位--------//
            EC11_KEY_COUNT = 0;
        }


    }
    return AnalyzeResult;
}


完整的代碼(C51)

我把整個EC11的驅(qū)動程序?qū)懺贓ncoderEC11.h和EncoderEC11.c中。請按需自行移植。

  • EncoderEC11.h
//---->>>>----文件描述:EC11旋轉(zhuǎn)編碼器底層驅(qū)動程序---<<<<----//
//---->>>>----文件版本: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的按鍵

//----------------編碼器動作代碼相關(guān)定義----------------//
extern  int G_PWM_NUM1;
extern  int G_PWM_NUM2;
extern  int G_PWM_NUM3;
static unsigned char EC11_NUM_SW = 0;

//----------------編碼器參數(shù)微調(diào)宏定義----------------//
#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)       //長按按鍵的回報率的倒數(shù),即一直長按按鍵時響應(yīng)的時間間隔

//----------------局部文件內(nèi)變量列表----------------//
static  char    EC11_A_Last = 0;                        //EC11的A引腳上一次的狀態(tài)
static  char    EC11_B_Last = 0;                        //EC11的B引腳上一次的狀態(tài)
static  char    EC11_Type = 1;                          //定義變量暫存EC11的類型---->>>>----  0:一定位對應(yīng)一脈沖;  1:兩定位對應(yīng)一脈沖
                                                        //所謂一定位對應(yīng)一脈沖,是指EC11旋轉(zhuǎn)編碼器每轉(zhuǎn)動一格,A和B都會輸出一個完整的方波。
                                                        //而  兩定位對應(yīng)一脈沖,是指EC11旋轉(zhuǎn)編碼器每轉(zhuǎn)動兩格,A和B才會輸出一個完整的方波,只轉(zhuǎn)動一格只輸出A和B的上升沿或下降沿
                                                        
static   int    EC11_KEY_COUNT = 0;                     //EC11按鍵動作計數(shù)器
static   int    EC11_KEY_DoubleClick_Count = 0;         //EC11按鍵雙擊動作計數(shù)器
static  char    FLAG_EC11_KEY_ShotClick = 0;            //EC11按鍵短按動作標(biāo)志
static  char    FLAG_EC11_KEY_LongClick = 0;            //EC11按鍵長按動作標(biāo)志
static  char    FLAG_EC11_KEY_DoubleClick = 0;          //EC11按鍵雙擊動作標(biāo)志



//----------------函數(shù)快速調(diào)用(復(fù)制粘貼)列表----------------//
//
/*******************************************************************
void Encoder_EC11_Init(unsigned char Set_EC11_TYPE);        //初始化EC11旋轉(zhuǎn)編碼器IO口和類型以及變量初始化
char Encoder_EC11_Scan();                                   //掃描旋轉(zhuǎn)編碼器的動作
void Encoder_EC11_Analyze(char EC11_Value);                 //分析EC11旋轉(zhuǎn)編碼器的動作以及動作處理代碼
******************************************************************/
//-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------//
//-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------//
//-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------//

//----------------函數(shù)聲明列表----------------//
//
//*******************************************************************/
//功能:初始化EC11旋轉(zhuǎn)編碼器相關(guān)參數(shù)
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖。
//返回:無
//詳解:對EC11旋轉(zhuǎn)編碼器的連接IO口做IO口模式設(shè)置。以及將相關(guān)的變量進行初始化
//*******************************************************************/
void Encoder_EC11_Init(unsigned char Set_EC11_TYPE);

//*******************************************************************/
//功能:掃描EC11旋轉(zhuǎn)編碼器的動作并將參數(shù)返回給動作分析函數(shù)使用
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖
//返回:EC11旋轉(zhuǎn)編碼器的掃描結(jié)果-->>  char ScanResult  -->>  0:無動作;1:正轉(zhuǎn); -1:反轉(zhuǎn);2:只按下按鍵;3:按著按鍵正轉(zhuǎn);-3:按著按鍵反轉(zhuǎn)
//詳解:只掃描EC11旋轉(zhuǎn)編碼器有沒有動作,不關(guān)心是第幾次按下按鍵或長按或雙擊。返回值直接作為形參傳給 [ void Encoder_EC11_Analyze(char EC11_Value); ] 函數(shù)使用
//*******************************************************************/
char Encoder_EC11_Scan();
    
//*******************************************************************/
//功能:對EC11旋轉(zhuǎn)編碼器的動作進行分析,并作出相應(yīng)的動作處理代碼
//形參:無
//返回:char AnalyzeResult = 0;目前無用。若在該函數(shù)里做了動作處理,則函數(shù)的返回值無需理會
//詳解:對EC11旋轉(zhuǎn)編碼器的動作進行模式分析,是單擊還是雙擊還是長按松手還是一直按下。形參從 [ char Encoder_EC11_Scan(unsigned char Set_EC11_TYPE) ] 函數(shù)傳入。在本函數(shù)內(nèi)修改需要的動作處理代碼
//*******************************************************************/
char Encoder_EC11_Analyze(char EC11_Value);

#endif


//---->>>>----函數(shù)使用示例----<<<<----//
/********

#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;       //設(shè)置定時器模式
    TL0 = 0xCD;     //設(shè)置定時初值
    TH0 = 0xF8;     //設(shè)置定時初值
    TF0 = 0;        //清除TF0標(biāo)志
    TR0 = 1;        //定時器0開始計時
}

void main()
{

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

    Encoder_EC11_Init(1);

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

//-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------//
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旋轉(zhuǎn)編碼器底層驅(qū)動程序---<<<<----//
//---->>>>----文件版本:V1.0----<<<<----//
#include "EncoderEC11.h"

//-------->>>>>>>>--------注意事項:EC11旋轉(zhuǎn)編碼器的掃描時間間隔控制在1~4ms之間,否則5ms及以上的掃描時間在快速旋轉(zhuǎn)時可能會誤判旋轉(zhuǎn)方向--------<<<<<<<<--------//

//*******************************************************************/
//功能:初始化EC11旋轉(zhuǎn)編碼器相關(guān)參數(shù)
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖。
//返回:無
//詳解:對EC11旋轉(zhuǎn)編碼器的連接IO口做IO口模式設(shè)置。以及將相關(guān)的變量進行初始化
//*******************************************************************/
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旋鈕位置不確定導(dǎo)致一次動作誤判
    EC11_A_Last = EC11_A_Now;   
    EC11_B_Last = EC11_B_Now;

    //--------清除按鍵計數(shù)器和標(biāo)志位--------//
    EC11_KEY_COUNT = 0;                     //EC11按鍵動作計數(shù)器
    EC11_KEY_DoubleClick_Count = 0;         //EC11按鍵雙擊動作計數(shù)器
    FLAG_EC11_KEY_ShotClick = 0;            //EC11按鍵短按動作標(biāo)志
    FLAG_EC11_KEY_LongClick = 0;            //EC11按鍵長按動作標(biāo)志
    FLAG_EC11_KEY_DoubleClick = 0;          //EC11按鍵雙擊動作標(biāo)志
}


//*******************************************************************/
//功能:掃描EC11旋轉(zhuǎn)編碼器的動作并將參數(shù)返回給動作分析函數(shù)使用
//形參:EC11旋轉(zhuǎn)編碼器的類型-->>  unsigned char Set_EC11_TYPE  <<--  :0----一定位對應(yīng)一脈沖;1(或非0)----兩定位對應(yīng)一脈沖
//返回:EC11旋轉(zhuǎn)編碼器的掃描結(jié)果-->>  char ScanResult  -->>  0:無動作;1:正轉(zhuǎn); -1:反轉(zhuǎn);2:只按下按鍵;3:按著按鍵正轉(zhuǎn);-3:按著按鍵反轉(zhuǎn)
//詳解:只掃描EC11旋轉(zhuǎn)編碼器有沒有動作,不關(guān)心是第幾次按下按鍵或長按或雙擊。返回值直接作為形參傳給 [ void Encoder_EC11_Analyze(char EC11_Value); ] 函數(shù)使用
//*******************************************************************/
char Encoder_EC11_Scan()
{
//以下儲存A、B上一次值的變量聲明為靜態(tài)全局變量,方便對EC11對應(yīng)的IO口做初始化
//  static char EC11_A_Last = 0;
//  static char EC11_B_Last = 0;
    char ScanResult = 0;    //返回編碼器掃描結(jié)果,用于分析編碼器的動作
                            //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);  
                            //                  2:只按下按鍵;    3:按著按鍵正轉(zhuǎn);   -3:按著按鍵反轉(zhuǎn)

                            //======================================================//
    if(EC11_Type == 0)      //================一定位對應(yīng)一脈沖的EC11================//
    {                       //======================================================//
        if(EC11_A_Now != EC11_A_Last)   //以A為時鐘,B為數(shù)據(jù)。正轉(zhuǎn)時AB反相,反轉(zhuǎn)時AB同相
        {
            if(EC11_A_Now == 0)
            {
                if(EC11_B_Now ==1)      //只需要采集A的上升沿或下降沿的任意一個狀態(tài),若A下降沿時B為1,正轉(zhuǎn)                    
                    ScanResult = 1;     //正轉(zhuǎn)

                else                    //反轉(zhuǎn)
                    ScanResult = -1;
            }
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
        }
    }   
                            //======================================================//
    else                    //================兩定位對應(yīng)一脈沖的EC11================//
    {                       //======================================================//
        if(EC11_A_Now !=EC11_A_Last)        //當(dāng)A發(fā)生跳變時采集B當(dāng)前的狀態(tài),并將B與上一次的狀態(tài)進行對比。
        {                                   //若A 0->1 時,B 1->0 正轉(zhuǎn);若A 1->0 時,B 0->1 正轉(zhuǎn);
                                            //若A 0->1 時,B 0->1 反轉(zhuǎn);若A 1->0 時,B 1->0 反轉(zhuǎn)
            if(EC11_A_Now == 1)     //EC11_A和上一次狀態(tài)相比,為上升沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿               
                    ScanResult = -1;                        //反轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = 1;                                 //正轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1
                    ScanResult = -1;                                //反轉(zhuǎn)
            }

            else                    //EC11_A和上一次狀態(tài)相比,為下降沿
            {
                if((EC11_B_Last == 1)&&(EC11_B_Now == 0))   //EC11_B和上一次狀態(tài)相比,為下降沿
                    ScanResult = -1;                        //反轉(zhuǎn)

                if((EC11_B_Last == 0)&&(EC11_B_Now == 1))   //EC11_B和上一次狀態(tài)相比,為上升沿
                    ScanResult = 1;                         //正轉(zhuǎn)

                //>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 0))  //A上升沿時,采集的B不變且為0
                    ScanResult = -1;                                //反轉(zhuǎn)

                if((EC11_B_Last == EC11_B_Now)&&(EC11_B_Now == 1))  //A上升沿時,采集的B不變且為1   
                    ScanResult = 1;                                 //正轉(zhuǎn)

            }               
            EC11_A_Last = EC11_A_Now;   //更新編碼器上一個狀態(tài)暫存變量
            EC11_B_Last = EC11_B_Now;   //更新編碼器上一個狀態(tài)暫存變量
        }
    }                                                                       

    if(EC11_Key == 0)   //如果EC11的按鍵按下,并且沒有EC11沒有轉(zhuǎn)動,
    {
        if(ScanResult == 0)         //按下按鍵時未轉(zhuǎn)動
            ScanResult = 2;         //返回值為2
        else
        {
            if(ScanResult == 1)     //按下按鍵時候正轉(zhuǎn)
                ScanResult = 3;     //返回值為3
            if(ScanResult == -1)    //按下按鍵時候反轉(zhuǎn)
                ScanResult = -3;    //返回值為-3
        }
    }

    return ScanResult;      //返回值的取值:   0:無動作;      1:正轉(zhuǎn);           -1:反轉(zhuǎn);
}                           //              2:只按下按鍵;    3:按著按鍵正轉(zhuǎn);   -3:按著按鍵反轉(zhuǎn)


//*******************************************************************/
//功能:對EC11旋轉(zhuǎn)編碼器的動作進行分析,并作出相應(yīng)的動作處理代碼
//形參:無
//返回:char AnalyzeResult = 0;目前無用。若在該函數(shù)里做了動作處理,則函數(shù)的返回值無需理會
//詳解:對EC11旋轉(zhuǎn)編碼器的動作進行模式分析,是單擊還是雙擊還是長按松手還是一直按下。形參從 [ char Encoder_EC11_Scan(unsigned char Set_EC11_TYPE) ] 函數(shù)傳入。在本函數(shù)內(nèi)修改需要的動作處理代碼
//*******************************************************************/
char Encoder_EC11_Analyze(char EC11_Value)
{
    char AnalyzeResult = 0;
    static unsigned int TMP_Value = 0;  //中間計數(shù)值,用于連續(xù)長按按鍵的動作延時間隔
    //>>>>>>>>>>>>>>>>編碼器正轉(zhuǎn)處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 1) //正轉(zhuǎn)
    {
        //--------編碼器正轉(zhuǎn)動作代碼--------//
        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;
        }
        
    }

    //>>>>>>>>>>>>>>>>編碼器反轉(zhuǎn)處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == -1)    //反轉(zhuǎn)
    {
        //--------編碼器反轉(zhuǎn)動作代碼--------//
        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;
        }
        
    }


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

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


    //>>>>>>>>>>>>>>>>編碼器按鍵按下處理程序<<<<<<<<<<<<<<<<//
    if(EC11_Value == 2)     //====檢測到按鍵按下====//
    {
        if(EC11_KEY_COUNT<10000)    //打開按鍵按下時間定時器
            EC11_KEY_COUNT++;
        if(EC11_KEY_COUNT == KEY_COUNT_DESHAKING)   //按下按鍵時間到達消抖時間時
        {                                           //置位短按按鍵標(biāo)志
            FLAG_EC11_KEY_ShotClick = 1;
        }

        if((EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME))   //松開按鍵后,又在定時器在雙擊時間內(nèi)按下按鍵
        {                                                                                               //置位雙擊按鍵標(biāo)志
            FLAG_EC11_KEY_DoubleClick = 1;
        }

        if(EC11_KEY_COUNT == KEY_COUNT_LONGTIME)    //按下按鍵時間到達長按時間
        {                                           //置位長按按鍵標(biāo)志并復(fù)位短按按鍵標(biāo)志
            FLAG_EC11_KEY_LongClick = 1;
            FLAG_EC11_KEY_ShotClick = 0;
        }

    }
    else                    //====檢測到按鍵松開====//     
    {
        if(EC11_KEY_COUNT < KEY_COUNT_DESHAKING)    //沒到消抖時長就松開按鍵,復(fù)位所有定時器和按鍵標(biāo)志
        {
            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))   //如果在規(guī)定雙擊時間內(nèi)再次按下按鍵
                {                                                                                               //認為按鍵是雙擊動作
                    FLAG_EC11_KEY_DoubleClick = 2;
                }   

                if((FLAG_EC11_KEY_DoubleClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME))    //如果沒有在規(guī)定雙擊時間內(nèi)再次按下按鍵
                    FLAG_EC11_KEY_ShotClick = 0;                                                                //認為按鍵是單擊動作
            }

            if(FLAG_EC11_KEY_LongClick == 1)        //檢測到長按按鍵松開
                FLAG_EC11_KEY_LongClick = 0;
        }

    }


    //>>>>>>>>>>>>>>>>編碼器按鍵分析處理程序<<<<<<<<<<<<<<<<//
    if(EC11_KEY_COUNT > KEY_COUNT_DESHAKING)    //短按按鍵延時到了時間
    {

        //短按按鍵動作結(jié)束代碼
        if((FLAG_EC11_KEY_ShotClick == 0)&&(EC11_KEY_DoubleClick_Count > KEY_COUNT_DUALCLICKTIME)&&(EC11_KEY_COUNT < KEY_COUNT_LONGTIME))   //短按按鍵動作結(jié)束代碼
        {
            //--------短按按鍵動作結(jié)束代碼--------//
            EC11_NUM_SW++;
            if(EC11_NUM_SW >= 4)
                EC11_NUM_SW = 1;
            AnalyzeResult = 1;
            //--------清除標(biāo)志位--------//
            EC11_KEY_COUNT = 0;
            EC11_KEY_DoubleClick_Count = 0;
            FLAG_EC11_KEY_DoubleClick = 0;
        }

        //雙擊按鍵動作結(jié)束代碼
        if((FLAG_EC11_KEY_DoubleClick == 2)&&(EC11_KEY_DoubleClick_Count > 0)&&(EC11_KEY_DoubleClick_Count <= KEY_COUNT_DUALCLICKTIME)) //雙擊按鍵動作結(jié)束代碼
        {
            //--------雙擊按鍵動作結(jié)束代碼--------//
            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;
            //--------清除標(biāo)志位--------//
            EC11_KEY_COUNT = 0;
            EC11_KEY_DoubleClick_Count = 0;
            FLAG_EC11_KEY_ShotClick = 0;
            FLAG_EC11_KEY_DoubleClick = 0;
            
        }

        //連續(xù)長按按鍵按下代碼
        if((FLAG_EC11_KEY_LongClick == 1)&&(EC11_KEY_COUNT >= KEY_COUNT_LONGTIME))  //連續(xù)長按按鍵按下代碼
        {
            TMP_Value ++;
            if(TMP_Value % KEY_LONG_REPEAT_TIME == 0)
            {
                TMP_Value = 0;
                //-------連續(xù)長按按鍵按下代碼--------//
                AnalyzeResult = 4;
            }
        }

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


    }
    return AnalyzeResult;
}


【施工中】未完待續(xù)……


后記

【施工中,有心情再寫】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容