關(guān)于Keil C51中using關(guān)鍵字的使用心得
后來(lái)調(diào)試了好久,甚至到http://www.51hei.com/keil%CF%C2%D4%D8.html這里下載了好幾個(gè)版本的keil,反匯編代碼看了N次,基本各個(gè)版本出來(lái)的結(jié)果都差不多,不知情的情況下,推斷出是Keil C51的編譯器問(wèn)題,以為Keil C51的編譯器的變量空間分配出問(wèn)題了,R7本來(lái)是用來(lái)作參數(shù)傳遞的,卻也被編譯器分配用來(lái)在主函數(shù)中被用來(lái)作循環(huán)變量,結(jié)果就是循環(huán)變量的匯編判斷指令CJNE指令執(zhí)行前,R7的值(也就是循環(huán)變量)被改變了,導(dǎo)致判斷R7總是不能達(dá)到循環(huán)設(shè)置的值,就不斷地在循環(huán),也就致使按鍵掃描去抖動(dòng)這個(gè)延時(shí)循環(huán)無(wú)法完成,就導(dǎo)致延時(shí)后的第二次判斷無(wú)法進(jìn)行,自然按鍵下來(lái)也就沒(méi)反應(yīng)了。后來(lái)嘗試著關(guān)了定時(shí)器中斷,又可以了,那就可以肯定是在執(zhí)行CJNE前,R7的值被改變了,就是因?yàn)樘搅酥袛啵缓笾袛嗪瘮?shù)執(zhí)行的過(guò)程中,R7值被改變了(因?yàn)橹袛嗪瘮?shù)中還調(diào)用了其它的函數(shù),而R7后來(lái)被分析反匯編代碼時(shí)發(fā)現(xiàn)是被用作參數(shù)傳遞的),中斷執(zhí)行完后R7的值當(dāng)然不正確啦!
得出推斷后我的解決辦法就是:讓C51編譯好之后手動(dòng)修改匯編的指令,不要直接使用寄存器來(lái)作循環(huán)變量,而是使用能實(shí)現(xiàn)間接尋地址的R1進(jìn)行間接尋址(指向0x77)的方式,終于把問(wèn)題解決了,因?yàn)?x77在執(zhí)行中斷函數(shù)的過(guò)程中沒(méi)被修改,CJNE語(yǔ)句能正常判斷了,循環(huán)能完成了,按鍵也能掃下來(lái)了,問(wèn)題也可以解決了。后來(lái)我又想到用_at_關(guān)鍵字直接指定循環(huán)變量的分配地址,這樣的話就不用手動(dòng)改匯編指令了,編譯器直接就是編譯成使用間接尋址的,這也算解決了。但還是有個(gè)疑問(wèn),為什么編譯器會(huì)不夠聰明地把R7的空間分配錯(cuò)了呢?一直在疑問(wèn)。。。。。。奇怪,怎么編譯器會(huì)出這種問(wèn)題?
直到我剛才考究過(guò)了using的用法之后,才發(fā)現(xiàn)原來(lái)自己的推斷是錯(cuò)的,結(jié)論的總方向還是對(duì)的,只是問(wèn)題并不在編譯器身上,真正的問(wèn)題根源如下:
1.我的同學(xué)寫(xiě)的那個(gè)程序,用到了定時(shí)器0,它的中斷服務(wù)函數(shù)定義是這樣寫(xiě)的:
void t0(void) interrupt 1 using 0
{
........//一大串大代碼,其中包括調(diào)用了其他函數(shù),用到了R7作參數(shù)傳遞
}
這里就是加了個(gè)using 0,而問(wèn)題就出在加了這個(gè)using 0,為什么?且聽(tīng)我的理解:
using關(guān)鍵字的作用就是指定某個(gè)函數(shù)在執(zhí)行時(shí)切換寄存器組的:
51中有四個(gè)寄存器組,每個(gè)組有R0-R7這8個(gè)寄存器,用于CPU的數(shù)據(jù)處理,一般主函數(shù)main()默認(rèn)使用第0組,因?yàn)镻SW寄存器的初始值的第3、4位是00嘛,即默認(rèn)指定了第0組。using 0就是指定在執(zhí)行函數(shù)時(shí)切換為使用第0組,不加using關(guān)鍵字的話,一般都默認(rèn)使用第0組,但這樣的話在調(diào)用其他函數(shù)(包括中斷服務(wù)函數(shù))的時(shí)候,就會(huì)加入一些壓棧指令以保護(hù)原來(lái)的R0-R7寄存器的(這樣的話,程序執(zhí)行會(huì)效率會(huì)低一點(diǎn),因?yàn)闀?huì)產(chǎn)生很多個(gè)指令是用來(lái)壓棧出棧的),照這樣說(shuō),只是加了using 0,使用的也是第0組又不是其它的組,程序就不應(yīng)該有問(wèn)題了吧,但是就是因?yàn)榧恿藆sing 0,編譯了一次,才發(fā)現(xiàn)在定時(shí)器中斷函數(shù)t0()的入口中并沒(méi)有發(fā)現(xiàn)把R0-R7的代碼壓入棧呀,就是說(shuō)沒(méi)有保護(hù)好R7呀,那當(dāng)然就是在執(zhí)行完之后回來(lái)R7不能回復(fù)原來(lái)的值啦,接下來(lái)的事情。。。。我就不說(shuō)啦,這就是問(wèn)題的根源,去掉using 0就可以了,編譯器就自動(dòng)幫你將R0-R7壓棧,手動(dòng)加了using 0,就是讓編譯器以為之前用的并不是第0組,而現(xiàn)在執(zhí)行這個(gè)中斷函數(shù)時(shí)就切換到第0組,而省去了將R0-R7這8個(gè)寄存器壓入棧的指令了,這樣雖然看起來(lái)是快了,然而對(duì)于這個(gè)程序來(lái)說(shuō)卻是致命的問(wèn)題?。。∫?yàn)楦緵](méi)有保護(hù)好R0-R7,而沒(méi)有保護(hù)R7并不是我預(yù)期發(fā)生的?。?!這不是編譯器的問(wèn)題,是自己沒(méi)有了解好、用好using的問(wèn)題啊!還有,其實(shí)也可以在編譯器選項(xiàng)里有選項(xiàng)來(lái)硬性規(guī)定編譯器統(tǒng)一不直接使用寄存器而是使用間接尋址的辦法來(lái)改變循環(huán)變量分配的地址的,或者使用
#pragma NOAREGS
定義函數(shù)
#pragma AREGS
來(lái)規(guī)定某個(gè)函數(shù)的是這樣子。
當(dāng)然,這樣的規(guī)定和使用using本身并不沖突,只是使用了這個(gè)關(guān)鍵字后就可能會(huì)間接地產(chǎn)生一系列的問(wèn)題,讓人郁悶了這么久,其實(shí)本來(lái)如果有詳細(xì)地看整個(gè)程序的反匯編代碼,或許當(dāng)時(shí)就會(huì)發(fā)現(xiàn)發(fā)現(xiàn)少了那段壓棧指令了,而不是說(shuō)推斷為編譯器分配空間的問(wèn)題了。。。。。。。。希望我的這次教訓(xùn)對(duì)各位有所幫助?。。?/P>
另外,using的用法,其實(shí)就是手動(dòng)指定函數(shù)使用的寄存器組,用得不好,如果在中斷里還有調(diào)用其它函數(shù),用得不好會(huì)出現(xiàn)函數(shù)傳遞出錯(cuò)的,不信可以反匯編看看,建議如果對(duì)這個(gè)關(guān)鍵字用法和C51的結(jié)構(gòu)及匯編不熟的話,請(qǐng)還是讓C51編譯器幫你好了,不要胡亂使用,因?yàn)闀?huì)比較容易出錯(cuò)的,要切記哦?。?!其實(shí)用using關(guān)鍵到底對(duì)在編譯后會(huì)造成什么影響,建議自己親自去查看匯編程序。。。
評(píng)論