按鍵的延時消抖是初學單片機的必經之路,因為只要是機械開關所傳遞的信號,都會存在波動,有時這些波動是「致命」的,所以消除其影響就是一門手藝了。硬體消抖有其優點,同樣的也有不方便的地方,開發成本高,操作難度大,可移植性差。軟體消抖就相對較好些,新手即可操作,延時消抖,並非消抖的最終最優方法,但好在簡單易懂,我們先認識消抖的方法和目的。
按鍵在按下和抬起時,都會出現短暫的抖動,稱之為前沿抖動和後沿抖動,他們持續的時間大致在5-10毫秒,鍵穩定時間會在100毫秒以上,就人的操作速度來看,鍵穩定的時間不會低於100毫秒,因為,1秒十次的操作,估計手都受不了。除非科幻世界或武俠世界的人。
既然抖動時間基本不變,那麼,我們就有這樣一種方法,當按鍵出現第一個電位變化,假設是高電位轉變成低電位,那麼我們就延時一段時間,設置10毫秒,10毫秒以後,我們再次判斷此時的電位狀態,是否是低電位,如果是低電位,那麼就認為按鍵按下了,如果是高電位,就認為按鍵是抖動。從低電位變成高電位也是一樣。
我們就是用延時來,把抖動的時間空過去了,這樣就不用擔心抖動產生的電位頻繁變化了。接下來,我們看下程序如何寫。
按流程來,基本上就能寫出來,程序分為兩大塊,一個是主函數,處理開關狀態,一個是延時函數。
我們先定義一個開關,然後我又聲明了一個位變量,其實這個位變量在這裡可以不用,不過習慣如此,對採集來的數據我習慣讓其保存在特定的變量中,這樣方便後期使用,以防自己改變變量值,造成埠的電位隨之改變。
主函數中,先把開關採集埠置1,這是讀取數據的前提條件,然後把需要採集的io的狀態轉移給中間變量,接著判斷此時中間變量是否為零,也就是按鍵是否按下,如果沒有按下,那就跳出,繼續賦值,接著判斷,直到判斷為零,進入語句中,先延時一段時間,讓抖動空過去,延時結束,再判斷一次,由於此時程序還沒走出去,所以中間變量的值也沒有實時切換,我們此時要判斷按鍵實時狀態是否為零,就需要判斷埠的實際值,當key10為零,就說明按鍵確實處於按下狀態,這就可以執行,移位指令。
需要說明,如果使用函數,在調用時,只需寫出函數名即可。但是在程序最開始位置,需要聲明函數,聲明時,要寫全,尤其是返回值的類型和變量名,不能省略。可以把函數直接複製到前方,然後加一個冒號即可。
程序看完,我們仿真一下測試下程序是否執行。
這是之前我們使用的仿真電路,直接使用就好。我沒有改變工程文件,所以無需重新導入可執行文件,程序會直接讀取我保存好的新的可執行文件,文件名沒有改變。
這是軟體的初始狀態,所有埠都是高電位,我們按下P10.
按鍵隨著按下,可以穩定的響應,我們再通過實際電路測試一下。
測試發現,我按下按鍵,還沒鬆手,就已經流水般的熄滅了5個燈了,什麼情況?
我們可以看程序的這裡
keybuff=key10; //賦值
if(keybuff==0) //判斷開關是否按下
{
delay(50); //延時一段時間
if(key10==0) //再次判斷開關是否按下
{
P3=P3>>1; //P3左移一位
}
}
從這一段可以看出,只要我能滿足keybuff為零,key10為零,那麼程序就會在延時結束再次進入程序,如此循環,就造成了,按鍵按下,P3被連續執行動作。我們怎麼才能讓這種情況不發生呢?這就需要我們不僅檢測按鍵按下,還需要檢測按鍵彈起,只有按鍵彈起我們才允許它執行下一步,這樣就能按下一次,抬起手,才會停止,保證了操作的準確。
執行流程如下:
判斷按鍵按下》按鍵按下》延時》判斷按鍵按下》按鍵按下》執行動作》判斷按鍵抬起》按鍵抬起》結束。
我們再次測試,此時發現,按下後,不鬆開,按鍵不再連續動作,但是鬆開按鍵後,原本熄滅的小燈又點亮了,我們梳理程序,可以發現,是不存在錯誤的,流程也沒有問題。其實這就涉及我們的硬體了,我們使用軟體仿真時,這些問題都是沒有的,但硬體跟仿真的區別就在這裡,在單片機中,如果我們沒有規定執行下一步的位置,單片機就會在流程走完後,隨機進入我們無法控制的流程,這在專業中稱之為跑飛。為了防止跑飛,我們一般會在結束添加循環語句,讓程序停止在我們設定的位置,這樣就不會有問題了。
此處我們需要連續監測按鍵狀態,所以就讓程序不斷的循環判斷按鍵即可。
再次測試,一切就按照程序執行了,動作也正常了。
這就是為什麼我們之前的測試程序,都會在主函數中添加循環的作用。通過這個示例,也是告訴大家,仿真只是學習的方法,最終目的還是要在實際的硬體上進行。不然你永遠不知道自己的程序能不能完成真正的功能,設計不能光紙上談兵哦。