前面一貼簡單讨論了一下51單片機的IO輸出功能
既然是IO,有output肯定就有input
今次咱聊聊input,輸入功能
有了input,51就能夠根據輸入的信号
去做出下一步工作的決定
其實單片機的輸入不外乎就是兩種
一是數字輸入,由單片機去判斷輸入的高低電平
再就是模拟輸入,最典型的就是内置ADC外設的單片機
可以把外部輸入的電壓值與基準電壓進行比較
從而得到一定數值的二進制編碼
譬如基準電壓3.3v,12位的線性ADC外設
就可以将外部輸入的0-3.3v電壓按比例用0-4096的十進制數去表示
換成二進制便是000000000000-111111111111
限于手頭上51片子沒有AD這個外設
咱今天聊的輸入,就從數字輸入下手
一、按鍵基本原理
先上電路
先看上面一個圖
P0.1口,連接到一個10k的電阻,上拉到電源Vcc(這裡Vcc=5.0v)
同時連接到按鍵的一端
按鍵的另一端則直接連到地
簡單分析一下這個電路
當按鍵沒有按下的時候
10k電阻和P0.1口沒有連接到地
由于P0端口是開漏輸出,可以看做是懸空
那麼經過10k電阻的電流為0,即10k電阻的壓降為0
這個時候,可以認為P0.1口的電位是5.0v,一個高電平
So,按鍵沒有按下,單片機可以認為P0.1口輸入了一個邏輯“1”,也就是高電平
當按鍵按下時
P0.1口和10k電阻均被連接到了地
這個時候,P0.1口的電位直接被拉到0v,也就是地
So,單片機可以認為P0.1口輸入了一個邏輯“0”,也就是低電平
這裡尤其注意一下這個10K電阻的作用
名曰上拉電阻
它的存在是非常有必要的
沒有這個電阻,P0.1口直接連接到按鍵一端或者電源,都會産生不好的後果
如果直接連至電源,按鍵按下的時候,Vcc和地……,後果你懂的……
如果直接連接到按鍵的一端,在按鍵按下前
P0.1都是懸空的,外界的幹擾(EMI之類),非常容易引起單片機的誤判
它的作用主要是按下前穩定電平、按下後限流
在按鍵按下的時候,電源—10k電阻—地 這個回路中
會産生5/100000=5mA電流,産生一定的功耗
有朋友會想,加大電阻就可以解決這個問題了呀
但是電阻太大的話,可能會出現開路情況
So,幾k到幾十k是比較合适的
再看下面一個圖
嗯,簡單的發光二極管回路
這裡就不多解釋了
順道拿電木闆焊了個電路
菊花面,10k上拉電阻和220歐LED限流電阻
這個電木闆質量真次,焊盤掉了好幾個
51讀端口的代碼特别簡單
隻要直接按位或者按port來逐位讀取即可
譬如要把P01口的邏輯值賦給變量temp
直接temp = P01即可
P01輸入一個低電平,temp=0x00
P01輸入一個高電平,temp=0x01
上個測試代碼
#include <reg51.h>
sbit P11 = P1^1;//按位定義
sbit P01 = P0^1;
int main()
{
while(1)
{
if(P01==0)//判斷P01口的值,0為按下
{P11 = ~P11;}//按下後的操作,P11口取反
else (P11 = P11;)
}
return 0;
}
簡單閱讀一下代碼
大概的功能就是判斷P01口電平是否為0
為0的話,P11口電平取反,也就是LED點亮或者熄滅
編譯下載,看看結果
不難發現
按鍵雖然能夠控制51去點亮或者熄滅LED
但是似乎并不是那麼順手
不能非常準确地操作LED的亮或者滅
為嘛?
二、去抖
在解決上面問題之前
必須意識到,按鍵其實是一個機械彈性開關結構
在按下或者松開的瞬間,也就是機械觸點斷開、閉合時
不會穩定地接通或者斷開,存在一連串的抖動
這種抖動時間與按鍵的機械特性有關,幾個ms到幾十個ms都有可能
由于51執行代碼的速度是us級
So,自己感覺隻按下了一次按鍵
其實51已經反複多次執行了按鍵按下的操作
所以造成上面gif中的現象
所以,按鍵去抖,是無論哪種單片機都需要面臨的問題
當然,有些比較有特點的單片機會有按鍵相關的外設,無需過多關注去抖,這個在此不讨論
有關去抖,有硬件和軟件兩種辦法
硬件上去抖的辦法
第一種是使用RS觸發器/鎖存器
鎖存器的工作特點就決定了,即使有按鍵抖動,也不會影響上如兩個與非門構成的RS鎖存器的輸出
再一種是在按鍵上并聯一個合适大小的電容
其實就是利用電容充放電的特性,無它
硬件去抖,基本上不占用單片機的資源
緻命的缺陷是需要額外的器件
會使PCB面積、BOM、成本有所增加
在成本為王的時代,基本上這種辦法很少有人使用了
但是非常可靠
再來看看軟件去抖
第一種是使用延時去抖,是目前用得比較多的
上個代碼
#include <reg51.h>
sbit P11 = P1^1;
sbit P01 = P0^1;
unsigned char flag;
void delay_ms(unsigned int xms)//定義一個ms級的延時函數
{
unsigned int i,j;
for(i=xms;i>0;i--)
{
for(j=124;j>0;j--);
}
}
unsigned char keyscan()//掃描按鍵
{
unsigned char key_value;
if(P01 == 0)//P01被拉低,也就是有按鍵按下
{
delay_ms(100);//延時100ms
if(P01 == 0) //延時後再判斷P01口是不是被拉低(按下)
{key_value = 1;}//如果是,鍵值位置1
else {key_value = 0;}//否則置0
}
return key_value;//返回鍵值
}
void key_op()
{
if(flag) //判斷标志位,然後進行操作
{
P11 = ~P11;
flag = 0;
}
else {;}
}
int main()
{
while(1)
{
flag = keyscan();//把掃描的鍵值賦給标志位
key_op();//根據标志位進行操作
}
return 0;
}
簡單分析一下上面的代碼
最最核心的就是
如果發現P01的按鍵被按下
就延時100ms後再讀一下P01的值
注意一下,這裡的100ms其實和按鍵的機械特性有關
根據實際情況調整
如果還是被按下的狀态,則認為按鍵确實被按下
單片機再進行下一步操作
上個GIF
可以發現
按鍵已經可以很準确地控制LED的亮滅了
但是,單片在執行delay_ms(100)的時候
其它任何事情都不能做呀
所以很多時候,這個延時會采用定時器中斷來完成
這個後面再讨論
其實還有一種不需要延時的去抖方法
可以理解成一個狀态機
上代碼
#include <reg51.h>
sbit P11 = P1^1;
sbit P01 = P0^1;
unsigned char flag;
void delay_ms(unsigned int xms)
{
unsigned int i,j;
for(i=xms;i>0;i--)
{
for(j=124;j>0;j--);
}
}
unsigned char keyscan()
{
static unsigned char key_state = 0;
static unsigned char key_value = 0;
unsigned char key_press,key_return = 0;
key_press = P01; //讀按鍵
switch(key_state)
{
case 0 ://按鍵初始态
if(key_press == 0){key_state = 1;}//按鍵被按下,但需要确認
break;
case 1://按鍵進行确認
if(key_press == 0)//如果還是被按下,則開始決定鍵值
{
key_value = 1;
key_state = 2;//确定被按下,轉到按鍵被按下狀态
}
else { key_value = 0;}//否則認為是抖動
break;
case 2://按鍵釋放狀态
if(key_press == 1)
{
key_return = key_value; //按鍵釋放後輸出鍵值
key_value = 0;
key_state = 0; //按鍵釋放,進入按鍵初始态
}
break;
}
return key_return; //返回鍵值
}
void key_op()
{
if(flag)
{
P11 = ~P11;
flag = 0;
}
else {;}
}
int main()
{
while(1)
{
flag = keyscan();
key_op();
}
return 0;
}
利用狀态機的編程思想
代碼上有解釋了,這裡不過多深入讨論
上GIF
可以看得出來
其實效果也還不錯
三、矩陣鍵盤
到這裡,一個基本鍵盤需要考慮的東西
大概就是這麼多了
還有一種針對比較多按鍵的電路組态
叫做是矩陣鍵盤
簡單介紹一下吧
上個結構圖
上圖是個4x4鍵盤,16個按鍵
按照前面的方法,得需要16個IO口才能完成接入
這裡其實隻需要P1口的8個端口即可
這種矩陣鍵盤判斷按鍵的方法有好幾種
用的比較多的有2種
一是行/列掃描法,分兩步
1、判斷鍵盤中有無鍵按丿将全部行線Y0-Y3置低電平,然後檢測列線的狀态。隻要有一列的電平為低,則表示鍵盤中有鍵被按下,而且閉合的鍵位于低電平線丿根行線相交叉皿個按鍵之中。若所有列線均為高電平,則鍵盤中無鍵按下
2、判斷閉合鍵所在的位置 在确認有鍵按下後,即可進入确定具體閉合鍵的過程。其方法是:依次将行線置為低電平,即在置某根行線為低電平時,其它線為高電平。在确定某根行線位置為低電平後,再逐行檢測各列線的電平狀态。若某列為低,則該列線與置為低電平的行線交叉處的按鍵就是閉合的按鍵
再一個是翻轉法,三步
1、行線輸出全為0,讀出列線值。
2、列線輸出上次讀入的列線值。
3、讀入行線值,并與前次列線值組合,生成組合碼值。根據這個組合碼來确定被按下的按鍵。
沒來得及做個矩陣鍵盤
如有疑問,可把問題發送給“雲漢電子社區”微信公衆号平台,我們會及時回複,關注公衆号可閱讀更多51系列教程!我們歡迎您的溝通!
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!