日本高清不卡中文字幕-一起草草视频在线观看-亚洲精品一区二区三区色-国产亚洲精品免费视频

你好!歡迎來到深圳市穎特新科技有限公司!
語言
當(dāng)前位置:首頁 >> 技術(shù)中心 >> 單片機入門 >> 真正的單片機入門 從處理多任務(wù)開始

真正的單片機入門 從處理多任務(wù)開始

關(guān)鍵字:單片機 算法 作者:admin 來源:不詳 發(fā)布時間:2018-05-18  瀏覽:11

真正的單片機入門,是從學(xué)會處理多任務(wù)開始的(按鍵算法)


我在網(wǎng)上游逛了很久,也看過不少源程序了,沒有發(fā)現(xiàn)這種按鍵處理辦法的蹤跡,所以,我將他共享出來,和廣大同僚們共勉。我非常堅信這種按鍵處理辦法的便捷和高效,你可以移植到任何一種嵌入式處理器上面,因為C語言強大的可移植性。

同時,這里面用到了一些分層的思想,在單片機當(dāng)中也是相當(dāng)有用的,也是本文的另外一個重點。

對于老鳥,我建議直接看那兩個表達式,然后自己想想就會懂的了,也不需要聽我后面的自吹自擂了,我可沒有班門弄斧的意思,hoho~~但是對于新手,我建議將全文看完。因為這是實際項目中總結(jié)出來的經(jīng)驗,學(xué)校里面學(xué)不到的東西。

 以下假設(shè)你懂C語言,因為純粹的C語言描述,所以和處理器平臺無關(guān),你可以在MCS-51,AVR,PIC,甚至是ARM平臺上面測試這個程序性能。當(dāng)然,我自己也是在多個項目用過,效果非常好的。

好了,工程人員的習(xí)慣,廢話就應(yīng)該少說,開始吧。以下我以AVR的MEGA8作為平臺講解,沒有其它原因,因為我手頭上只有AVR的板子而已沒有51的。用51也可以,只是芯片初始化部分不同,還有寄存器名字不同而已。

 核心算法:
 
unsigned char Trg;

unsigned char Cont;

void KeyRead( void )

{

    unsigned char ReadData = PINB^0xff;   // 1

    Trg = ReadData & (ReadData ^ Cont);      // 2

    Cont = ReadData;                                // 3

}

完了。有沒有一種不可思議的感覺?當(dāng)然,沒有想懂之前會那樣,想懂之后就會驚嘆于這算法的精妙!!

下面是程序解釋:

Trg(triger) 代表的是觸發(fā),Cont(continue)代表的是連續(xù)按下。

1:讀PORTB的端口數(shù)據(jù),取反,然后送到ReadData 臨時變量里面保存起來。

2:算法1,用來計算觸發(fā)變量的。一個位與操作,一個異或操作,我想學(xué)過C語言都應(yīng)該懂吧?Trg為全局變量,其它程序可以直接引用。

3:算法2,用來計算連續(xù)變量。

 
看到這里,有種“知其然,不知其所以然”的感覺吧?代碼很簡單,但是它到底是怎么樣實現(xiàn)我們的目的的呢?好,下面就讓我們繞開云霧看青天吧。

我們最常用的按鍵接法如下:AVR是有內(nèi)部上拉功能的,但是為了說明問題,我是特意用外部上拉電阻。那么,按鍵沒有按下的時候,讀端口數(shù)據(jù)為1,如果按鍵按下,那么端口讀到0。下面就看看具體幾種情況之下,這算法是怎么一回事。

(1)       沒有按鍵的時候

端口為0xff,ReadData讀端口并且取反,很顯然,就是 0x00 了。

Trg = ReadData & (ReadData ^ Cont); (初始狀態(tài)下,Cont也是為0的)很簡單的數(shù)學(xué)計算,因為ReadData為0,則它和任何數(shù)“相與”,結(jié)果也是為0的。

Cont = ReadData; 保存Cont 其實就是等于ReadData,為0;

結(jié)果就是:

ReadData = 0;

Trg = 0;

Cont = 0;

 
(2)       第一次PB0按下的情況

端口數(shù)據(jù)為0xfe,ReadData讀端口并且取反,很顯然,就是 0x01 了。

Trg = ReadData & (ReadData ^ Cont); 因為這是第一次按下,所以Cont是上次的值,應(yīng)為為0。那么這個式子的值也不難算,也就是 Trg = 0x01 & (0x01^0x00) = 0x01

Cont = ReadData = 0x01;

結(jié)果就是:

ReadData = 0x01;

Trg = 0x01;Trg只會在這個時候?qū)?yīng)位的值為1,其它時候都為0

Cont = 0x01;


(3)       PB0按著不松(長按鍵)的情況

端口數(shù)據(jù)為0xfe,ReadData讀端口并且取反是 0x01 了。

Trg = ReadData & (ReadData ^ Cont); 因為這是連續(xù)按下,所以Cont是上次的值,應(yīng)為為0x01。那么這個式子就變成了 Trg = 0x01 & (0x01^0x01) = 0x00

Cont = ReadData = 0x01;

結(jié)果就是:

ReadData = 0x01;

Trg = 0x00;

Cont = 0x01;

因為現(xiàn)在按鍵是長按著,所以MCU會每個一定時間(20ms左右)不斷的執(zhí)行這個函數(shù),那么下次執(zhí)行的時候情況會是怎么樣的呢?

ReadData = 0x01;這個不會變,因為按鍵沒有松開

Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) = 0 ,只要按鍵沒有松開,這個Trg值永遠為 0 。!

Cont = 0x01;只要按鍵沒有松開,這個值永遠是0x01!

 
(4)       按鍵松開的情況

端口數(shù)據(jù)為0xff,ReadData讀端口并且取反是 0x00 了。

Trg = ReadData & (ReadData ^ Cont) = 0x00 & (0x00^0x01) = 0x00

Cont = ReadData = 0x00;

結(jié)果就是:

ReadData = 0x00;

Trg = 0x00;

Cont = 0x00;

很顯然,這個回到了初始狀態(tài),也就是沒有按鍵按下的狀態(tài)。

 
總結(jié)一下,不知道想懂了沒有?其實很簡單,答案如下:

Trg 表示的就是觸發(fā)的意思,也就是跳變,只要有按鍵按下(電平從1到0的跳變),那么Trg在對應(yīng)按鍵的位上面會置一,我們用了PB0則Trg的值為0x01,類似,如果我們PB7按下的話,Trg 的值就應(yīng)該為 0x80 ,這個很好理解,還有,最關(guān)鍵的地方,Trg 的值每次按下只會出現(xiàn)一次,然后立刻被清除,完全不需要人工去干預(yù)。所以按鍵功能處理程序不會重復(fù)執(zhí)行,省下了一大堆的條件判斷,這個可是精粹哦!Cont代表的是長按鍵,如果PB0按著不放,那么Cont的值就為 0x01,相對應(yīng),PB7按著不放,那么Cont的值應(yīng)該為0x80,同樣很好理解。

如果還是想不懂的話,可以自己演算一下那兩個表達式,應(yīng)該不難理解的。
 

因為有了這個支持,那么按鍵處理就變得很爽了,下面看應(yīng)用:

應(yīng)用一:一次觸發(fā)的按鍵處理

假設(shè)PB0為蜂鳴器按鍵,按一下,蜂鳴器beep的響一聲。這個很簡單,但是大家以前是怎么做的呢?對比一下看誰的方便?

#define KEY_BEEP 0x01

void KeyProc(void)

{

       if (Trg & KEY_BEEP) // 如果按下的是KEY_BEEP

    {

         Beep();            // 執(zhí)行蜂鳴器處理函數(shù)

    }

}

 

怎么樣?夠和諧不?記得前面解釋說Trg的精粹是什么?精粹就是只會出現(xiàn)一次。所以你按下按鍵的話,Trg & KEY_BEEP 為“真”的情況只會出現(xiàn)一次,所以處理起來非常的方便,蜂鳴器也不會沒事亂叫,hoho~~~

 
或者你會認(rèn)為這個處理簡單,沒有問題,我們繼續(xù)。


應(yīng)用2:長按鍵的處理

項目中經(jīng)常會遇到一些要求,例如:一個按鍵如果短按一下執(zhí)行功能A,如果長按2秒不放的話會執(zhí)行功能B,又或者是要求3秒按著不放,計數(shù)連加什么什么的功能,很實際。不知道大家以前是怎么做的呢?我承認(rèn)以前做的很郁悶。

但是看我們這里怎么處理吧,或許你會大吃一驚,原來程序可以這么簡單

 

這里具個簡單例子,為了只是說明原理,PB0是模式按鍵,短按則切換模式,PB1就是加,如果長按的話則連加(玩過電子表吧?沒錯,就是那個!)

 

#define KEY_MODE 0x01    // 模式按鍵

#define KEY_PLUS 0x02     // 加

void KeyProc(void)

{

       if (Trg & KEY_MODE) // 如果按下的是KEY_MODE,而且你常按這按鍵也沒有用,

    {                    //它是不會執(zhí)行第二次的哦 , 必須先松開再按下

         Mode++;         // 模式寄存器加1,當(dāng)然,這里只是演示,你可以執(zhí)行你想

                         // 執(zhí)行的任何代碼

    }

 

    if (Cont & KEY_PLUS) // 如果“加”按鍵被按著不放

    {

         cnt_plus++;       // 計時

         if (cnt_plus > 100) // 20ms*100 = 2S 如果時間到

         {

              Func();      // 你需要的執(zhí)行的程序

         }          

    }

}

 

不知道各位感覺如何?我覺得還是挺簡單的完成了任務(wù),當(dāng)然,作為演示用代碼
 

應(yīng)用3:點觸型按鍵和開關(guān)型按鍵的混合使用

點觸形按鍵估計用的最多,特別是單片機。開關(guān)型其實也很常見,例如家里的電燈,那些按下就不松開,除非關(guān)。這是兩種按鍵形式的處理原理也沒啥特別,但是你有沒有想過,如果一個系統(tǒng)里面這兩種按鍵是怎么處理的?我想起了我以前的處理,分開兩個非常類似的處理程序,現(xiàn)在看起來真的是笨的不行了,但是也沒有辦法啊,結(jié)構(gòu)決定了程序。不過現(xiàn)在好了,用上面介紹的辦法,很輕松就可以搞定。

原理么?可能你也會想到,對于點觸開關(guān),按照上面的辦法處理一次按下和長按,對于開關(guān)型,我們只需要處理Cont就OK了,為什么?很簡單嘛,把它當(dāng)成是一個長按鍵,這樣就找到了共同點,屏蔽了所有的細(xì)節(jié)。程序就不給了,完全就是應(yīng)用2的內(nèi)容,在這里提為了就是說明原理~~

 

好了,這個好用的按鍵處理算是說完了。可能會有朋友會問,為什么不說延時消抖問題?哈哈,被看穿了。果然不能偷懶。下面談?wù)勥@個問題,順便也就非常簡單的談?wù)勎易约河脮r間片輪辦法,以及是如何消抖的。

延時消抖的辦法是非常傳統(tǒng),也就是 第一次判斷有按鍵,延時一定的時間(一般習(xí)慣是20ms)再讀端口,如果兩次讀到的數(shù)據(jù)一樣,說明了是真正的按鍵,而不是抖動,則進入按鍵處理程序。

當(dāng)然,不要跟我說你delay(20)那樣去死循環(huán)去,真是那樣的話,我衷心的建議你先放下手上所有的東西,好好的去了解一下操作系統(tǒng)的分時工作原理,大概知道思想就可以,不需要詳細(xì)看原理,否則你永遠逃不出“菜鳥”這個圈子。當(dāng)然我也是菜鳥。我的意思是,真正的單片機入門,是從學(xué)會處理多任務(wù)開始的,這個也是學(xué)校程序跟公司程序的最大差別。當(dāng)然,本文不是專門說這個的,所以也不獻丑了。

我的主程序架構(gòu)是這樣的:

volatile unsigned char Intrcnt;

void InterruptHandle()    // 中斷服務(wù)程序

{

       Intrcnt++;          // 1ms 中斷1次,可變

}

 

void main(void)

{

       SysInit();

    while(1)           // 每20ms 執(zhí)行一次大循環(huán)

    {

        KeyRead();             // 將每個子程序都掃描一遍

        KeyProc();

        Func1();

        Funt2();

        …

        …

           while(1)

        {

              if (Intrcnt>20)     // 一直在等,直到20ms時間到

              {

                   Intrcnt="0";

                   break;       // 返回主循環(huán)

              }

        }

       }

}

 

貌似扯遠了,回到我們剛才的問題,也就是怎么做按鍵消抖處理。我們將讀按鍵的程序放在了主循環(huán),也就是說,每20ms我們會執(zhí)行一次KeyRead()函數(shù)來得到新的Trg 和 Cont 值。好了,下面是我的消抖部分:很簡單

基本架構(gòu)如上,我自己比較喜歡的,一直在用。當(dāng)然,和這個配合,每個子程序必須執(zhí)行時間不長,更加不能死循環(huán),一般采用有限狀態(tài)機的辦法來實現(xiàn),具體參考其它資料咯。

懂得基本原理之后,至于怎么用就大家慢慢思考了,我想也難不到聰明的工程師們。例如還有一些處理,

怎么判斷按鍵釋放?很簡單,Trg 和Cont都為0 則肯定已經(jīng)釋放了。

編輯:admin  最后修改時間:2018-05-18

聯(lián)系方式

0755-82591179

傳真:0755-82591176

郵箱:vicky@yingtexin.net

地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08

Copyright © 2014-2023 穎特新科技有限公司 All Rights Reserved.  粵ICP備14043402號-4

夹江县| 勃利县| 宜良县| 威宁| 淮安市| 宾阳县| 精河县| 怀仁县| 华池县| 通城县| 东辽县| 克拉玛依市| 乐东| 阳新县| 梁河县| 杨浦区| 山东| 新邵县| 湘西| 苏尼特右旗| 河东区| 进贤县| 竹溪县| 志丹县| 静乐县| 永嘉县| 孝感市| 大足县| 巫溪县| 防城港市| 嘉义县| 景东| 内丘县| 宜兰县| 南郑县| 文山县| 拉萨市| 新郑市| 金坛市| 宜兰市| 鹤山市|