剛才看到一位很牛的師兄寫的一篇日志中提到了Keil C51中using這個關鍵字的用法,粗心的我本來一直都沒有留意它是用來干嘛的(因為我一般看見它都是在中斷服務函數的定義開頭處,好像沒有了它也可以中斷呀,所以才沒怎么管),然而在日志中有看到這個關鍵字,所以也考究了一下,突然發現,原來這個東東和我最近在幫一個同學調的一個程序的時候突然遇到一個很怪的問題是有關系的,而且就是因為它才搞得程序莫明奇妙的出錯(因為編譯通過了,看起來也沒什么錯誤,按C語言的邏輯分析也分析不出個什么所以然來,所以才怪)。
后來調試了好久,甚至到http://www.zg4o1577.cn/keil%CF%C2%D4%D8.html 這里下載了好幾個版本的keil,反匯編代碼看了N次,基本各個版本出來的結果都差不多,不知情的情況下,推斷出是Keil C51的編譯器問題,以為Keil C51的編譯器的變量空間分配出問題了,R7本來是用來作參數傳遞的,卻也被編譯器分配用來在主函數中被用來作循環變量,結果就是循環變量的匯編判斷指令CJNE指令執行前,R7的值(也就是循環變量)被改變了,導致判斷R7總是不能達到循環設置的值,就不斷地在循環,也就致使按鍵掃描去抖動這個延時循環無法完成,就導致延時后的第二次判斷無法進行,自然按鍵下來也就沒反應了。后來嘗試著關了定時器中斷,又可以了,那就可以肯定是在執行CJNE前,R7的值被改變了,就是因為跳到了中斷,然后中斷函數執行的過程中,R7值被改變了(因為中斷函數中還調用了其它的函數,而R7后來被分析反匯編代碼時發現是被用作參數傳遞的),中斷執行完后R7的值當然不正確啦!
得出推斷后我的解決辦法就是:讓C51編譯好之后手動修改匯編的指令,不要直接使用寄存器來作循環變量,而是使用能實現間接尋地址的R1進行間接尋址(指向0x77)的方式,終于把問題解決了,因為0x77在執行中斷函數的過程中沒被修改,CJNE語句能正常判斷了,循環能完成了,按鍵也能掃下來了,問題也可以解決了。后來我又想到用_at_關鍵字直接指定循環變量的分配地址,這樣的話就不用手動改匯編指令了,編譯器直接就是編譯成使用間接尋址的,這也算解決了。但還是有個疑問,為什么編譯器會不夠聰明地把R7的空間分配錯了呢?一直在疑問。。。。。。奇怪,怎么編譯器會出這種問題?
直到我剛才考究過了using的用法之后,才發現原來自己的推斷是錯的,結論的總方向還是對的,只是問題并不在編譯器身上,真正的問題根源如下:
1.我的同學寫的那個程序,用到了定時器0,它的中斷服務函數定義是這樣寫的:
void t0(void) interrupt 1 using 0
{
........//一大串大代碼,其中包括調用了其他函數,用到了R7作參數傳遞
}
這里就是加了個using 0,而問題就出在加了這個using 0,為什么?且聽我的理解:
using關鍵字的作用就是指定某個函數在執行時切換寄存器組的:
51中有四個寄存器組,每個組有R0-R7這8個寄存器,用于CPU的數據處理,一般主函數main()默認使用第0組,因為PSW寄存器的初始值的第3、4位是00嘛,即默認指定了第0組。using 0就是指定在執行函數時切換為使用第0組,不加using關鍵字的話,一般都默認使用第0組,但這樣的話在調用其他函數(包括中斷服務函數)的時候,就會加入一些壓棧指令以保護原來的R0-R7寄存器的(這樣的話,程序執行會效率會低一點,因為會產生很多個指令是用來壓棧出棧的),照這樣說,只是加了using 0,使用的也是第0組又不是其它的組,程序就不應該有問題了吧,但是就是因為加了using 0,編譯了一次,才發現在定時器中斷函數t0()的入口中并沒有發現把R0-R7的代碼壓入棧呀,就是說沒有保護好R7呀,那當然就是在執行完之后回來R7不能回復原來的值啦,接下來的事情。。。。我就不說啦,這就是問題的根源,去掉using 0就可以了,編譯器就自動幫你將R0-R7壓棧,手動加了using 0,就是讓編譯器以為之前用的并不是第0組,而現在執行這個中斷函數時就切換到第0組,而省去了將R0-R7這8個寄存器壓入棧的指令了,這樣雖然看起來是快了,然而對于這個程序來說卻是致命的問題!!!因為根本沒有保護好R0-R7,而沒有保護R7并不是我預期發生的!!!這不是編譯器的問題,是自己沒有了解好、用好using的問題啊!還有,其實也可以在編譯器選項里有選項來硬性規定編譯器統一不直接使用寄存器而是使用間接尋址的辦法來改變循環變量分配的地址的,或者使用
#pragma NOAREGS
定義函數
#pragma AREGS
來規定某個函數的是這樣子。
當然,這樣的規定和使用using本身并不沖突,只是使用了這個關鍵字后就可能會間接地產生一系列的問題,讓人郁悶了這么久,其實本來如果有詳細地看整個程序的反匯編代碼,或許當時就會發現發現少了那段壓棧指令了,而不是說推斷為編譯器分配空間的問題了。。。。。。。。希望我的這次教訓對各位有所幫助!!!
另外,using的用法,其實就是手動指定函數使用的寄存器組,用得不好,如果在中斷里還有調用其它函數,用得不好會出現函數傳遞出錯的,不信可以反匯編看看,建議如果對這個關鍵字用法和C51的結構及匯編不熟的話,請還是讓C51編譯器幫你好了,不要胡亂使用,因為會比較容易出錯的,要切記哦!!!其實用using關鍵到底對在編譯后會造成什么影響,建議自己親自去查看匯編程序。。。
對不起,編譯器,我錯怪你了!!!