51 單片機(jī)有四個 8 位的并行接口。
因為內(nèi)部結(jié)構(gòu)的特點,這些接口,在輸出 0、1 的時候,能力是不一樣的。
輸出 0 的時候,能力較強(qiáng),可以允許灌入十多毫安的電流,能夠直接驅(qū)動 LED 發(fā)光。
但是,這些接口在輸出 1 的時候,能力就很差了,特別是 P0 口,它自身根本就不具備輸出 1 的能力,總是要借助外接的上拉電阻才能輸出 1。
這時如果外接一個小電阻接地,引腳就維持不住高電平了。外接的電路,很容易就可以把引腳的電平拉低。
就是說,輸出了 1 之后,接口引腳的電平,就完全取決于外部電路。
正是因為這種特點,所以,就把輸出 1,規(guī)定為單片機(jī)的輸入方式。
------------------------
因為輸出了 1 之后,外接的電路就可以隨便的改變引腳電平。
那么,還想要用原來輸出的數(shù)據(jù),進(jìn)行計算,比如說加一:INC ?P1,數(shù)據(jù)又被外部電路改變了,這樣可不好。
其實,輸出的數(shù)據(jù)(0 或 1),是先存放在接口寄存器中,再由寄存器輸出到引腳。
接口寄存器也就是 P0~P3,它們都屬于單片機(jī)的特殊功能寄存器,它們之中任意的一個位,都稱為鎖存器。
引腳的電平,可以受到外部電路的影響,而接口寄存器的內(nèi)容是不變的。
------------------------
針對接口的讀出,有讀引腳指令,還有一種是讀寄存器的讀-改-寫指令。
讀引腳指令,也就是用于輸入數(shù)據(jù)的指令。
凡是以接口為源操作數(shù)的傳送指令,全都是讀引腳指令,如:MOV ?A, P1。
而讀-改-寫指令,是先讀出接口寄存器的數(shù)據(jù),修改后,再寫入接口寄存器。
如 INC ?P1,就是先讀出 P1 寄存器中內(nèi)容,加一后,再寫入 P1 寄存器。
讀-改-寫指令和引腳電平無關(guān),這種指令不能輸入數(shù)據(jù)。
屬于讀-改-寫的指令有個特點,就是以接口寄存器為目的操作數(shù),如:
ANL、ORL、XRL、DJNZ、INC、DEC、JBC、CPL、CLR、SETB、MOV ?PX.Y, C。
?
------------------------
學(xué)習(xí)匯編語言時,就會學(xué)到讀引腳、讀鎖存器的區(qū)別。
但是,以 C 語言為主的單片機(jī)教材,幾乎都沒有針對讀-改-寫指令的特點加以說明。
有很多人,對用 C 語言編程比較熱心,也確實能夠編寫出來一些成功的東西??墒强此麄儗纹瑱C(jī)的理解、對于某些問題的解答,難免貽笑大方。
可以看出,有些編程高手,其實,也并不懂單片機(jī)。
------------------------
有這樣一個問題:
P2 口外接 4*4 的矩陣鍵盤,采用反轉(zhuǎn)法來讀出按鍵信息,也就是在高、低四位,分別輸出0,再讀入另外四位的引腳電平。
錯誤的程序如下:
/*************鍵盤掃描******錯誤*********/
void scan()
{
? ? unsigned char media;
? ? P2 = 0x0f;
? ? P2 = P2 | 0xf0;
? ? key = P2;?
}
但是,key 并沒有反映出按鍵的信息,為什么錯了呢?
做而論道的回答如下:
/*************鍵盤掃描******錯誤*********/
void scan()
{
? ? unsigned char media;
? ? P2 = 0x0f; ? ? ? ?//在P2高四位輸出0,將以低四位為輸入
? ? P2 = P2 | 0xf0; ? //在P2高四位又輸出1
//前面兩條,在 P2 的八條線,都輸出了1
? ? key = P2; ? ? ? ? //讀入的,這是什么呢?
}
P2 口,如果外接的獨立按鍵,這么做,就是對的。
P2 口,如果外接的矩陣按鍵,這么做,就是錯的。因為讀入前,并沒有輸出0。
---------
追問:
//假設(shè)有鍵按下:
P2 = 0x0f; ? ? //高四位為0第四位為1,因為有鍵按下,則低四位中有0
P2 = P2 | 0xf0;//將高四位也賦值為1,因為低四位中有0,且有鍵按下則高四位中也有0
key = P2; ? ? ?//將P2的鍵值保存在key中, 不知問題在哪兒
做而論道回答:
P2 = P2 | 0xf0;//將高四位也賦值為1,因為低四位中有0,且有鍵按下則高四位中也有0
這條語句,并沒有讀入低四位的0。
P2 | 0xf0;,這里所用的 P2,是原來的 P2 = 0x0f。
而按鍵產(chǎn)生的《低四位中有0》,并沒有發(fā)揮作用。
---------
追問:
首先感謝您的耐心解答,這個問題讓我困惑了一天。
P2 = 0x0f; 這條語句在有 media 作為中間變量的時候,就能實現(xiàn):
【高四位為0第四位為1,因為有鍵按下,則低四位中有0】的功能。
而沒有media時,就不能讀入低四位的0呢 ?
做而論道回答:
這個問題,要從匯編語言中,才能找到答案。
使用 C 語言編程,好比是隔靴搔癢。出現(xiàn)了異常,也只能疑惑終身。
看看匯編語言里面《讀-改-寫》指令吧。
匯編語言里面,有《讀引腳》、《讀鎖存器》的區(qū)別。
在 C 語言里面,就葫蘆攪茄子的弄不清了。
匯編:
ORL ?P2, #0F0H ? ;讀鎖存器
MOV ?A, ?P2 ? ? ?;讀出引腳
剛才有錯誤的程序,關(guān)鍵是這條語句:
P2 = P2 | 0xf0;//將高四位也賦值為1,因為低四位中有0,且有鍵按下則高四位中也有0
表面上看,這是讀出了 P2,高四位或上 1111,低四位不變,低四位應(yīng)該是讀出按鍵的0。
但是,這條指令編譯成匯編語言之后,就是:
ORL ?P2, #0F0H
這是典型的讀-改-寫指令。
它讀的是接口寄存器,并不是讀出引腳,所以反映不出來按鍵的狀態(tài)。
讀出 4*4 鍵盤的正確程序,提問者也提供了,做而論道加上了說明,如下:
/*************鍵盤掃描******正確*********/
void scan()
{
? ? unsigned char media;
? ? P2 = 0x0f; ? ? ? ? //在P2高四位輸出0,將以低四位為輸入
? ? media = P2; ? ? ? ?//讀入引腳,低四位代表按鍵信息
//如果有鍵按下,低四位中,就有0
//那么,media 可能是下列之一:
//XXXX 0111
//XXXX 1011
//XXXX 1101
//XXXX 1110 ? 假如,就是這個吧。
? ? media | 0xf0;
//那么,media,就是:
//1111 1110 ? 就是這個。
? ? P2 = media | 0xf0; //以高四位為輸入,低四位將輸出0
//P2 = 1111 1110
? ? key = P2; ? ? ? ? ?//讀入引腳,高、低四位皆含有按鍵信息
//key 可能就是下列之一:
//0111 1110
//1011 1110?
//1101 1110
//1110 1110
//key 的內(nèi)容,就反映出來了按鍵信息。
//這些,就是正確讀出矩陣鍵盤程序的過程。
}
追問:嗯 非常感謝 我會深入去了解的 謝謝你的耐心解答
2014-01-15 12:36
------------------------
后記:
以 C 語言來講單片機(jī)的書,做而論道也看過幾本,說實在的,和單片機(jī)無關(guān)的垃圾太多了,也看不下去。
關(guān)于讀-改-寫的知識,做而論道還是注意找了找,但是,確實沒有發(fā)現(xiàn)寫在何處。
也許,看的書,還不全。
有些同學(xué),碰到難學(xué)的課程,考試掛了,也不知道哪里錯了,通過了,也不知道怎么過去的。
用 C 語言編程,就和這類似,編成功了,也不知道怎么弄成功的,碰到異常,也不知道有什么毛病。
特別是一些自認(rèn)是 C 語言的編程高手,針對這個問題,也是瞎說一氣,呵呵
------------------------