新聞中心

EEPW首頁 > 牛人業(yè)話 > 開關(guān)檢測(cè)二三事:端口不足,濾波時(shí)間不同

開關(guān)檢測(cè)二三事:端口不足,濾波時(shí)間不同

作者:驢三 時(shí)間:2020-03-18 來源:電子產(chǎn)品世界 收藏

熟悉灑家寫作風(fēng)格的朋友們都知道,灑家行文一向生動(dòng)活潑,甚而有時(shí)浮夸得沒個(gè)章法。但是,在這個(gè)全民戰(zhàn)疫的關(guān)鍵時(shí)刻,似乎任何的輕佻都是對(duì)前線戰(zhàn)士的不恭敬。故而,今天嚴(yán)肅緊張一把,跟大家開門見山,講一講開關(guān)檢測(cè)的問題。

本文引用地址:http://m.butianyuan.cn/article/202003/411046.htm

有的朋友可能會(huì)覺得,開關(guān)檢測(cè)對(duì)于每一個(gè)嵌入式工程師來講都是入門級(jí)別的問題,有什么好講的呢?好吧,對(duì)于你這種想法,我只能像春晚上曉明哥哥對(duì)祖兒妹妹講的那樣:

我不要你覺得,我要我覺得!

問你三個(gè)問題吧。第一,如果因?yàn)檫@樣或那樣的原因,您選用的MCU的IO口不夠,無法一一對(duì)應(yīng)地處理那么多路開關(guān)信號(hào),你該咋個(gè)辦?

第二,如何區(qū)分開關(guān)的“動(dòng)作”和“狀態(tài)”?按下和彈起的動(dòng)作一閃即逝,狀態(tài)卻長(zhǎng)期保持,怎么區(qū)分并處理?

第三,是關(guān)于大家耳熟能詳?shù)臑V波問題,您可不要說只需要進(jìn)行硬件濾波就夠了這種跌份的話哈。假設(shè)您的開關(guān)信號(hào)性質(zhì)有所不同,它們需要的濾波時(shí)間也不一樣,你怎么以一種統(tǒng)一的方式去處理他們呢?

今天灑家和大家分享的就是關(guān)于這三個(gè)問題的想法和解決方案。

要想逃避現(xiàn)實(shí),最好的方式就是深深介入現(xiàn)實(shí)之中。

嵌入式工程師的日常工作不是在錦繡河山里做文章,而是在螺絲殼里做道場(chǎng)。在日復(fù)一日的工作中,工程師逐漸積累了豐富的實(shí)戰(zhàn)經(jīng)驗(yàn)。比如硬件不夠可以用軟件來湊,內(nèi)存不足可以犧牲實(shí)時(shí)性,以時(shí)間換空間。

那么,IO口不夠呢?

魯迅先生曾說:“希望是本無所謂有,無所謂無的。這正如地上的路,其實(shí)地上本沒有路,走的人多了,也便成了路?!?/p>

勇敢的嵌入式工程師是遇山開路、遇水搭橋的開拓者,面對(duì)無路之境,自會(huì)以大無畏的霹靂手段趟出一條路來。IO口不夠,MCU又不能換(想一想老板那冷颼颼的目光),自然也會(huì)另辟蹊徑,柳暗花明,不至于山窮水復(fù),找不到出路的。

聰明的小伙伴們應(yīng)該已經(jīng)躍躍欲試,準(zhǔn)備搶答了。不過,授人以魚不如授人以漁,在直接給出答案之前,大家可以先想一個(gè)問題:為什么現(xiàn)在大家用的計(jì)算機(jī),基本上沒有并口了呢?

在計(jì)算機(jī)剛剛出現(xiàn)的那個(gè)年代里,打印和繪圖是非常重要的應(yīng)用。由于計(jì)算機(jī)速度的限制,串行傳輸速度相當(dāng)有限,無法應(yīng)對(duì)打印繪圖這種需要高速數(shù)據(jù)傳輸?shù)膽?yīng)用,于是,并口大行其道數(shù)十載。說句不怕暴露年齡的話,筆者剛上班時(shí),單位的臺(tái)式機(jī)和筆記本都是有并口的。

當(dāng)然,魚與熊掌不可兼得,并行傳輸也有其缺點(diǎn),那就是需要的端口和線路遠(yuǎn)高于串行傳輸,會(huì)消耗較高的電路板資源和軟件解析能力,所以,隨著計(jì)算機(jī)和串行傳輸速度的提升,并口也開始慢慢地退出了歷史的舞臺(tái)。

觸類旁通,見微知著,大家是不是品出來什么了?

對(duì),在嵌入式設(shè)計(jì)中,一個(gè)MCU端口處理一路開關(guān)信號(hào)是“并行處理方式”,類比于計(jì)算機(jī)并口,需要消耗較多的端口。端口不夠的解決方案自然是“并行轉(zhuǎn)串行”,以串行的方式進(jìn)行開關(guān)信號(hào)的檢測(cè)。

在具體的實(shí)現(xiàn)上,需要選擇“多路開關(guān)檢測(cè)接口芯片”,這種芯片可以檢測(cè)多路開關(guān)量輸入信號(hào),并將檢測(cè)到的開關(guān)狀態(tài)通過SPI發(fā)送給MCU。這種方式可以極大地節(jié)省MCU的IO口資源,比如說檢測(cè)16路開關(guān),并行方式需要16個(gè)MCU IO端口,串行方式只需要一個(gè)SPI端口就可以了。

話不多說,再來看第二個(gè)問題:怎么區(qū)分開關(guān)的“動(dòng)作”和“狀態(tài)”?

至于為什么要區(qū)分“動(dòng)作”和“狀態(tài)”。是因?yàn)樵谇度胧疆a(chǎn)品中,有一種很常見的應(yīng)用邏輯:開關(guān)A、B、C處于閉合狀態(tài)且開關(guān)D、E、F處于斷開狀態(tài)時(shí),按下或松開開關(guān)G,執(zhí)行某個(gè)操作。

在這種邏輯里,“按下”和“松開”是兩種動(dòng)作,“閉合”和“斷開”是兩種狀態(tài)。用電路的知識(shí)來類比的話,動(dòng)作是沿跳變,狀態(tài)是電平。

“動(dòng)作”是一閃即逝的花火,狀態(tài)是千年不變的承諾。我們做區(qū)分為的是,讓動(dòng)作“閱后即焚”,不至于成為反復(fù)觸發(fā)操作的脈搏。

為了說明這一點(diǎn),灑家跟大家分享一下自己設(shè)計(jì)的結(jié)構(gòu)體和代碼實(shí)現(xiàn),這部分也可以用在對(duì)第三個(gè)問題的解答上。

typedef struct{

     unsigned switch_state:1;

     unsigned swon_event:1;

     unsigned swoff_event:1;

     unsigned cursw:1;

     unsigned detect_cnt:4;

     e_SwId   switch_id;

}s_Switch;

在這個(gè)結(jié)構(gòu)體的成員變量里面,switch_id標(biāo)識(shí)開關(guān)節(jié)點(diǎn),大家可以用“解釋性”很強(qiáng)的枚舉來表示它。這里的switch_state表示的是開關(guān)信號(hào)的狀態(tài),swon_event和swoff_event分別表示開關(guān)從斷開到閉合和從閉合到斷開的變化,即上述的“動(dòng)作”。 cursw和detect_cnt用于開關(guān)信號(hào)采集的軟件消抖功能。

為了同時(shí)檢測(cè)開關(guān)狀態(tài)和動(dòng)作,可以設(shè)置一個(gè)10ms的周期定時(shí)器,周期性地對(duì)每個(gè)SWITCH_ID對(duì)應(yīng)的開關(guān)信號(hào)進(jìn)行檢測(cè),具體實(shí)現(xiàn)為:

void SwDetect(e_SwId sw_id)  

{

    uint8_t filter_time;

    filter_time = SW_DETECT_TIMES;

    if(SWITCH_OFF == Sw[sw_id].switch_state){

        if(IOVALID == Sw[sw_id].cursw){

            if(Sw[sw_id].detect_cnt < filter_time){

                Sw[sw_id].detect_cnt++;   

            }else{                                          

                Sw[sw_id].switch_state = SWITCH_ON;       

                Sw[sw_id].detect_cnt = 0;                      

                Sw[sw_id].swon_event = 1;          

            }

        }else{

            Sw[sw_id].detect_cnt = 0;

        }

    }else{

        if(IOINVALID == Sw[sw_id].cursw){

            if(Sw[sw_id].detect_cnt < filter_time){

                Sw[sw_id].detect_cnt++;

            }else{                                                        

                Sw[sw_id].switch_state = SWITCH_OFF;

                Sw[sw_id].swoff_event = 1;

                Sw[sw_id].detect_cnt = 0;

            }

        }else{

            Sw[sw_id].detect_cnt = 0;

        }

    }

}

當(dāng)開關(guān)動(dòng)作發(fā)生時(shí),swon_event和swoff_event置一,在執(zhí)行完相關(guān)操作之后,將swon_event和swoff_event清零,就完成了讓動(dòng)作“閱后即焚”。

所以,上面那種根據(jù)某些開關(guān)的狀態(tài)和動(dòng)作執(zhí)行相關(guān)操作的邏輯的具體實(shí)現(xiàn)為:

If(Switch[SWITCH_ID_1].swon_event == 1)

{

If(Switch[SWITCH_ID_2].switch_state == “ON”){

    操作1;

}

Switch[SWITCH_ID_1].swon_event = 0;

}

下面接著講第三個(gè)問題:怎么應(yīng)對(duì)不同的濾波時(shí)間?

正如上面講過的那樣,對(duì)于一般的開關(guān)節(jié)點(diǎn),設(shè)計(jì)一10ms的定時(shí)器周期性地讀取開關(guān)當(dāng)前狀態(tài)cursw,然后根據(jù)其維持當(dāng)前狀態(tài)的周期次數(shù)(根據(jù)不同應(yīng)用場(chǎng)景,可以設(shè)置為5次或者10次,分別對(duì)應(yīng)50ms或100ms的濾波時(shí)間)以判斷switch_state、swon_event、swoff_event。

那么,對(duì)于那些特殊的開關(guān)信號(hào),也許需要采用較典型值長(zhǎng)或者短的消抖時(shí)間,我們只需要針對(duì)該開關(guān)信號(hào)對(duì)應(yīng)的那個(gè)SWITCH_ID表征的結(jié)構(gòu)體變量,設(shè)置它的濾波次數(shù)filter_time(見上面那段程序)即可。

講到這里,有些不愛看代碼的同學(xué)可能模糊了,這里,幫人幫到底,灑家不惜筆墨,詳細(xì)開展一番。

首先,設(shè)定一個(gè)10ms的定時(shí)器,在它的中斷服務(wù)程序里,執(zhí)行開關(guān)信號(hào)檢測(cè)。

對(duì)應(yīng)在我們這里,可以認(rèn)為它的中斷服務(wù)程序(ISR)執(zhí)行的就是下面這個(gè)IoInputDetect函數(shù)。(需要說明的是,一般情況下我們不會(huì)在中斷服務(wù)程序里執(zhí)行這種耗時(shí)較長(zhǎng)的程序,這里只是為了方便大家理解)

void IoInputDetect(void)

{

    e_SwId sw_idx;

    ReadIoSwitch();

    for(sw_idx = MIN_SWITCH;sw_idx < MAX_SWITCH;sw_idx++){

        SwDetect(sw_idx);   

    } 

}

這個(gè)函數(shù)里面,在ReadIoSwitch函數(shù)里面讀取每個(gè)開關(guān)(以SWITCH_ID標(biāo)識(shí))的當(dāng)前狀態(tài),賦給其cursw,需要注意的是,這里的cursw表示的是當(dāng)下這一刻的開關(guān)狀態(tài),不是經(jīng)過濾波處理后的穩(wěn)定開關(guān)狀態(tài)。

第二步:根據(jù)每個(gè)開關(guān)的當(dāng)前狀態(tài)cursw,判斷其穩(wěn)定的開關(guān)狀態(tài)switch_state、開關(guān)動(dòng)作swon_event和swoff_event。即上面在for循環(huán)中執(zhí)行的SwDetect函數(shù)。

SwDetect函數(shù)語句在第二節(jié)中,它的核心思想就是判斷開關(guān)當(dāng)前狀態(tài)cursw是否持續(xù)穩(wěn)定在SWITCH_ON或者SWITCH_OFF狀態(tài)。當(dāng)前的switch_state為ON的狀態(tài)下,如果持續(xù)filter_time個(gè)10ms,cursw一直為OFF狀態(tài),則將switch_state賦為OFF狀態(tài),同時(shí),將swoff_event賦為1。反之亦然。

當(dāng)濾波時(shí)間不同時(shí),顯然只需要將該switch_id對(duì)應(yīng)的開關(guān)結(jié)構(gòu)體的filter_time置為不同于典型值的特殊值即可。

后記

灑家在這篇文章里面分享的開關(guān)檢測(cè)方法,不止適用于數(shù)字IO形式的開關(guān)信號(hào),還適用于其它信號(hào)。

比如通過RF方式接收的遙控信號(hào),雖然是一種射頻性質(zhì)的信號(hào),但是這種信號(hào)對(duì)應(yīng)的是遙控器上的物理按鍵,它在邏輯上自然也等價(jià)于本文講的開關(guān)信號(hào),所以,可以用上述那個(gè)結(jié)構(gòu)體和那些代碼判斷遙控信號(hào),解析出某個(gè)遙控按鍵按下、松開的動(dòng)作和狀態(tài),同時(shí)對(duì)它進(jìn)行濾波處理。

再舉一反三,無論是RF信號(hào)、模擬信號(hào)、數(shù)字信號(hào)、網(wǎng)絡(luò)信號(hào),只要該輸入信號(hào)在邏輯上可以等價(jià)于物理開關(guān),它就可以使用本文所述的方法處理。

你覺得呢?



關(guān)鍵詞:

評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉