關(guān)于單片機的using使用
以下是夢游的一些分析:
一、中斷函數(shù)是一個特殊的函數(shù),沒有參數(shù),也沒有返回值;但是程序中允不允許使用return呢?答案是允許的,不過只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,對應(yīng)的匯編會有多個ret語句,相對效率會高一些。
二、using的用法,using可以修飾任何函數(shù),不過個人建議只用來修飾中斷函數(shù);簡單的說,“using”會指定工作寄存器組,由于中斷函數(shù)一般都是比較緊急的事情,有時一條語句都會斤斤計較,所以使用using切換寄存器組可以省去一些壓棧的動作,由于51只有兩級中斷,同級中斷不能被打斷,因此,我們可以同級中斷設(shè)成同樣的寄存器組,從某種意義上來說,有一組寄存器是多余的。同時個人建議中斷函數(shù)應(yīng)該使用using這個關(guān)鍵字。
三、中斷中調(diào)用函數(shù),首先要討論中斷函數(shù)中調(diào)用函數(shù)的必要性,前天在論壇上我和別人爭論過這個問題,現(xiàn)在我還是這個觀點:有些情況中斷中調(diào)用函數(shù)還是必要的,這個時候是不是該調(diào)用函數(shù),其實和普通函數(shù)差不多,首先是這個函數(shù)如果調(diào)用多次,或者要帶一些參數(shù)什么的就更加必要的;前天有人跟我叫勁,說假如只調(diào)用一次且無參數(shù)無返回的函數(shù)要直接寫,因為如果用函數(shù),至少會增加CALL和RET兩條語句,我不敢茍同,我是實際調(diào)試發(fā)現(xiàn)的,當(dāng)你程序比較復(fù)雜時,你將那部單獨拉出來做成函數(shù),可能代碼和時間都會更好。
四、中斷中調(diào)用的函數(shù)最好不要被中斷外的其它函數(shù)調(diào)用,因為會出現(xiàn)“重復(fù)調(diào)用”的警告,有時這種調(diào)用是很致命的,有人說這個函數(shù)可以用reentrant來修飾,是的,的確可以這樣解決,不過個人不建議這么做,也許這樣會跟你減少很多堆??臻g,并且整個程序的優(yōu)化要差很多,個人建議出現(xiàn)這種情況就把這個函數(shù)寫兩遍,分成兩個函數(shù)分別調(diào)用。
五、中斷調(diào)用了函數(shù),會出現(xiàn)一些莫名其妙的問題,一些數(shù)據(jù)不對(我現(xiàn)在遇到這個問題)其實一般是因為匯編中使用了絕對寄存器引起的,有人說中斷函數(shù)使用那個寄存器組,被中斷調(diào)用的函數(shù)就使用哪個寄存器組(我認為好參考C51.PDF:Functions called from an interrupt procedure must function with the same register bank as the interrupt procedure. When the NOAREGS directive is not explicitly specified, the compiler may generate absolute register accesses using the register bank selected (by the using attribute or by the REGISTERBANK control) for that function. Unpredictable results may occur when a function assumes a register bank other than the one currently selected. Refer to “Register Bank Access” on page 124 for more information.),我認為這樣不好:
這樣會增加額外的消耗,使用using會增加一下語句:
PUSH PSW
MOV PSW, #XX
....
POP PSW
更重要的是,使用using的函數(shù)不能有返回值(這個地方有問題,應(yīng)該可以有返回值,下文說是不能不能返回bit類型的值),這是致命傷(所以這不是致命傷,可以使用using解決這個問題)
個人推薦的方法有兩種:
1、使用“#pragma NOAREGS”禁止使用絕對寄存器
2、使用“#pragme RB(x)”來指定本文件的工作寄存器組
六、一般說來,要求中斷函數(shù)盡可能的短,但也有特殊情況,有些前/后臺的系統(tǒng)中,就會把很多相對重要的事情放到定時中斷(這個定時中斷類似實時操作系統(tǒng)中的時鐘節(jié)拍)去做,而且程序很長。我單獨提出來這點是想告訴大家,中斷函數(shù)也是一個函數(shù)而已,只要系統(tǒng)有必要,可以做一些看似不合理的事情,該出手時就出手,就像goto語句一樣。
本文引用地址:http://cafeforensic.com/article/201611/317705.htm一、中斷函數(shù)是一個特殊的函數(shù),沒有參數(shù),也沒有返回值;但是程序中允不允許使用return呢?答案是允許的,不過只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,對應(yīng)的匯編會有多個ret語句,相對效率會高一些。
二、using的用法,using可以修飾任何函數(shù),不過個人建議只用來修飾中斷函數(shù);簡單的說,“using”會指定工作寄存器組,由于中斷函數(shù)一般都是比較緊急的事情,有時一條語句都會斤斤計較,所以使用using切換寄存器組可以省去一些壓棧的動作,由于51只有兩級中斷,同級中斷不能被打斷,因此,我們可以同級中斷設(shè)成同樣的寄存器組,從某種意義上來說,有一組寄存器是多余的。同時個人建議中斷函數(shù)應(yīng)該使用using這個關(guān)鍵字。
三、中斷中調(diào)用函數(shù),首先要討論中斷函數(shù)中調(diào)用函數(shù)的必要性,前天在論壇上我和別人爭論過這個問題,現(xiàn)在我還是這個觀點:有些情況中斷中調(diào)用函數(shù)還是必要的,這個時候是不是該調(diào)用函數(shù),其實和普通函數(shù)差不多,首先是這個函數(shù)如果調(diào)用多次,或者要帶一些參數(shù)什么的就更加必要的;前天有人跟我叫勁,說假如只調(diào)用一次且無參數(shù)無返回的函數(shù)要直接寫,因為如果用函數(shù),至少會增加CALL和RET兩條語句,我不敢茍同,我是實際調(diào)試發(fā)現(xiàn)的,當(dāng)你程序比較復(fù)雜時,你將那部單獨拉出來做成函數(shù),可能代碼和時間都會更好。
四、中斷中調(diào)用的函數(shù)最好不要被中斷外的其它函數(shù)調(diào)用,因為會出現(xiàn)“重復(fù)調(diào)用”的警告,有時這種調(diào)用是很致命的,有人說這個函數(shù)可以用reentrant來修飾,是的,的確可以這樣解決,不過個人不建議這么做,也許這樣會跟你減少很多堆??臻g,并且整個程序的優(yōu)化要差很多,個人建議出現(xiàn)這種情況就把這個函數(shù)寫兩遍,分成兩個函數(shù)分別調(diào)用。
五、中斷調(diào)用了函數(shù),會出現(xiàn)一些莫名其妙的問題,一些數(shù)據(jù)不對(我現(xiàn)在遇到這個問題)其實一般是因為匯編中使用了絕對寄存器引起的,有人說中斷函數(shù)使用那個寄存器組,被中斷調(diào)用的函數(shù)就使用哪個寄存器組(我認為好參考C51.PDF:Functions called from an interrupt procedure must function with the same register bank as the interrupt procedure. When the NOAREGS directive is not explicitly specified, the compiler may generate absolute register accesses using the register bank selected (by the using attribute or by the REGISTERBANK control) for that function. Unpredictable results may occur when a function assumes a register bank other than the one currently selected. Refer to “Register Bank Access” on page 124 for more information.),我認為這樣不好:
這樣會增加額外的消耗,使用using會增加一下語句:
PUSH PSW
MOV PSW, #XX
....
POP PSW
更重要的是,使用using的函數(shù)不能有返回值(這個地方有問題,應(yīng)該可以有返回值,下文說是不能不能返回bit類型的值),這是致命傷(所以這不是致命傷,可以使用using解決這個問題)
個人推薦的方法有兩種:
1、使用“#pragma NOAREGS”禁止使用絕對寄存器
2、使用“#pragme RB(x)”來指定本文件的工作寄存器組
六、一般說來,要求中斷函數(shù)盡可能的短,但也有特殊情況,有些前/后臺的系統(tǒng)中,就會把很多相對重要的事情放到定時中斷(這個定時中斷類似實時操作系統(tǒng)中的時鐘節(jié)拍)去做,而且程序很長。我單獨提出來這點是想告訴大家,中斷函數(shù)也是一個函數(shù)而已,只要系統(tǒng)有必要,可以做一些看似不合理的事情,該出手時就出手,就像goto語句一樣。
轉(zhuǎn)自http://www.ednchina.com/blog/hotchip/,請大家去他的博客中支持他,里面有不錯的文章。括號中是我的理解
關(guān)于using:
舉個例子來說:
定義一個函數(shù)
void func(unsigned char i) {
...
}
有如下一個中斷函數(shù)
void int_0(void) interrupt 0 using 1 {
....
}
在默認狀態(tài)下,func使用寄存器組0(BANK0),那么當(dāng)int_0調(diào)用func時是否存在當(dāng)傳遞參數(shù)時會造成參數(shù)傳遞錯誤?
舉個例子來說:
定義一個函數(shù)
void func(unsigned char i) {
...
}
有如下一個中斷函數(shù)
void int_0(void) interrupt 0 using 1 {
....
}
在默認狀態(tài)下,func使用寄存器組0(BANK0),那么當(dāng)int_0調(diào)用func時是否存在當(dāng)傳遞參數(shù)時會造成參數(shù)傳遞錯誤?
如果在中斷服務(wù)函數(shù)ISR中使用寄存器,那么必須處理好using的使用問題:
1、中斷服務(wù)函數(shù)使用using指定與主函數(shù)不同的寄存器組(主函數(shù)一般使用Register bank 0)。
2、中斷優(yōu)先級相同的ISR可用using指定相同的寄存器組,但優(yōu)先級不同的ISR必須使用不同的寄存器組,在ISR中被調(diào)用的函數(shù)也要使用using指定與中斷函數(shù)相同的寄存器組。(應(yīng)該是這樣的)
3、如果不用using指定,在ISR的入口,C51默認選擇寄存器組0,這相當(dāng)于中斷服務(wù)程序的入口首先執(zhí)行指令:
MOV PSW #0
這點保證了,沒使用using指定的高優(yōu)先級中斷??梢灾袛嗍褂貌煌募拇嫫鹘M的低優(yōu)先級中斷。
4、使用using關(guān)鍵字給中斷指定寄存器組,這樣直接切換寄存器組而不必進行大量的PUSH和POP操作,可以節(jié)省RAM空間,加速MCU執(zhí)行時間。寄存器組的切換,總的來說比較容易出錯,要對內(nèi)存的使用情況有比較清晰的認識,其正確性要由你自己來保證。特別在程序中有直接地址訪問的時候,一定要小心謹慎!至于“什么時候要用到寄存器組切換”,一種情況是:當(dāng)你試圖讓兩個(或以上)作業(yè)同時運行,而且它們的現(xiàn)場需要一些隔離的時候,就會用上了。在ISR或使用實時操作系統(tǒng)RTOS中,寄存器非常有用。
寄存器組使用的原則:
1、8051的最低32個字節(jié)分成4組8寄存器。分別為寄存器R0到R7。寄存器組由PSW的低兩位選擇。在ISR中,MCU可以切換到一個不同的寄存器組。對寄存器組的訪問不可位尋址,C51編譯器規(guī)定使用using或禁止中斷的函數(shù)(#pragma disable)均不能返回bit類型的值。
2、主程序(main函數(shù))使用一組,如bank 0;低中斷優(yōu)先級的所有中斷均使用第二組,如bank 1;高中斷優(yōu)先級的所有中斷均使用再另外一組,如bank 2。顯然,同級別的中斷使用同一組寄存器不會有問題,因為不會發(fā)生中斷嵌套;而高優(yōu)先級的中斷則要使用與低優(yōu)先級中斷不同的一組,因為有可能出現(xiàn)在低優(yōu)先級中斷中發(fā)生高優(yōu)先級中斷的情況。編譯器會自動判斷何時可使用絕對寄存器存取。
3、在ISR中調(diào)用其它函數(shù),必須和中斷使用相同的寄存器組。當(dāng)沒用NOAREGS命令做明確的聲明,編譯器將使用絕對寄存器尋址方式訪問函數(shù)選定(即用using或REGISTERBANK指定)的寄存器組,當(dāng)函數(shù)假定的和實際所選的寄存器組不同時,將產(chǎn)生不可預(yù)知的結(jié)果,從而可能出現(xiàn)參數(shù)傳遞錯誤,返回值可能會在錯誤的寄存器組中。
舉一例子:當(dāng)需要在中斷內(nèi)和中斷外調(diào)用同一個函數(shù),假定按照程序的流程控制,不會出現(xiàn)函數(shù)的遞歸調(diào)用現(xiàn)象,這樣的調(diào)用會不會出現(xiàn)問題?若確定不會發(fā)生重入情況,則有以下兩種情況:
1、如果ISR和主程序使用同一寄存器組(主程序缺省使用BANK 0,若ISR沒有使用using為其指定寄存器區(qū),則缺省也使用BANK 0),則不需其他設(shè)置。
2、如果ISR和主程序使用不同的寄存器組(主程序缺省使用BANK 0,ISR使用using指定了其他BANK),則被調(diào)用函數(shù)必須放在:
#pragma NOAREGS
#pragma AREGS
控制參數(shù)對中,指定編譯器不要對該函數(shù)使用絕對寄存器尋址方式;或者也可在Options->C51,選中“Dont use absolute register accesses”,使所有代碼均不使用絕對寄存器尋址方式(這樣,執(zhí)行效率將稍有降低)。不論以上的哪一種情況,編譯器均會給出重入警告,需手工更改OVERLAY參數(shù),做重入說明。
3、還有一種辦法:如果被調(diào)用函數(shù)的代碼不是很長,還是將該函數(shù)復(fù)制一份,用不同的函數(shù)名代替,這種情況適合ROM有足夠多余的空間。
因此,對using關(guān)鍵字的使用,如果沒把握,寧可不用,交給編譯系統(tǒng)自己去處理好了。
1、中斷服務(wù)函數(shù)使用using指定與主函數(shù)不同的寄存器組(主函數(shù)一般使用Register bank 0)。
2、中斷優(yōu)先級相同的ISR可用using指定相同的寄存器組,但優(yōu)先級不同的ISR必須使用不同的寄存器組,在ISR中被調(diào)用的函數(shù)也要使用using指定與中斷函數(shù)相同的寄存器組。(應(yīng)該是這樣的)
3、如果不用using指定,在ISR的入口,C51默認選擇寄存器組0,這相當(dāng)于中斷服務(wù)程序的入口首先執(zhí)行指令:
MOV PSW #0
這點保證了,沒使用using指定的高優(yōu)先級中斷??梢灾袛嗍褂貌煌募拇嫫鹘M的低優(yōu)先級中斷。
4、使用using關(guān)鍵字給中斷指定寄存器組,這樣直接切換寄存器組而不必進行大量的PUSH和POP操作,可以節(jié)省RAM空間,加速MCU執(zhí)行時間。寄存器組的切換,總的來說比較容易出錯,要對內(nèi)存的使用情況有比較清晰的認識,其正確性要由你自己來保證。特別在程序中有直接地址訪問的時候,一定要小心謹慎!至于“什么時候要用到寄存器組切換”,一種情況是:當(dāng)你試圖讓兩個(或以上)作業(yè)同時運行,而且它們的現(xiàn)場需要一些隔離的時候,就會用上了。在ISR或使用實時操作系統(tǒng)RTOS中,寄存器非常有用。
寄存器組使用的原則:
1、8051的最低32個字節(jié)分成4組8寄存器。分別為寄存器R0到R7。寄存器組由PSW的低兩位選擇。在ISR中,MCU可以切換到一個不同的寄存器組。對寄存器組的訪問不可位尋址,C51編譯器規(guī)定使用using或禁止中斷的函數(shù)(#pragma disable)均不能返回bit類型的值。
2、主程序(main函數(shù))使用一組,如bank 0;低中斷優(yōu)先級的所有中斷均使用第二組,如bank 1;高中斷優(yōu)先級的所有中斷均使用再另外一組,如bank 2。顯然,同級別的中斷使用同一組寄存器不會有問題,因為不會發(fā)生中斷嵌套;而高優(yōu)先級的中斷則要使用與低優(yōu)先級中斷不同的一組,因為有可能出現(xiàn)在低優(yōu)先級中斷中發(fā)生高優(yōu)先級中斷的情況。編譯器會自動判斷何時可使用絕對寄存器存取。
3、在ISR中調(diào)用其它函數(shù),必須和中斷使用相同的寄存器組。當(dāng)沒用NOAREGS命令做明確的聲明,編譯器將使用絕對寄存器尋址方式訪問函數(shù)選定(即用using或REGISTERBANK指定)的寄存器組,當(dāng)函數(shù)假定的和實際所選的寄存器組不同時,將產(chǎn)生不可預(yù)知的結(jié)果,從而可能出現(xiàn)參數(shù)傳遞錯誤,返回值可能會在錯誤的寄存器組中。
舉一例子:當(dāng)需要在中斷內(nèi)和中斷外調(diào)用同一個函數(shù),假定按照程序的流程控制,不會出現(xiàn)函數(shù)的遞歸調(diào)用現(xiàn)象,這樣的調(diào)用會不會出現(xiàn)問題?若確定不會發(fā)生重入情況,則有以下兩種情況:
1、如果ISR和主程序使用同一寄存器組(主程序缺省使用BANK 0,若ISR沒有使用using為其指定寄存器區(qū),則缺省也使用BANK 0),則不需其他設(shè)置。
2、如果ISR和主程序使用不同的寄存器組(主程序缺省使用BANK 0,ISR使用using指定了其他BANK),則被調(diào)用函數(shù)必須放在:
#pragma NOAREGS
#pragma AREGS
控制參數(shù)對中,指定編譯器不要對該函數(shù)使用絕對寄存器尋址方式;或者也可在Options->C51,選中“Dont use absolute register accesses”,使所有代碼均不使用絕對寄存器尋址方式(這樣,執(zhí)行效率將稍有降低)。不論以上的哪一種情況,編譯器均會給出重入警告,需手工更改OVERLAY參數(shù),做重入說明。
3、還有一種辦法:如果被調(diào)用函數(shù)的代碼不是很長,還是將該函數(shù)復(fù)制一份,用不同的函數(shù)名代替,這種情況適合ROM有足夠多余的空間。
因此,對using關(guān)鍵字的使用,如果沒把握,寧可不用,交給編譯系統(tǒng)自己去處理好了。
例子
用上using可以精簡代碼,節(jié)省堆棧,不過有時會出現(xiàn)一個問題:
用上using ,在中斷服務(wù)程序里調(diào)用函數(shù)要小心一點,因為keil C有時會產(chǎn)生依賴絕對地址的代碼,例如如下函數(shù),功能是從片外的存儲設(shè)備中讀取一個字節(jié):
uchar ReadByte(uchar address)
{
retrun PBYTE[address];
}
會被編譯成如下代碼:
MOVR0,0x07
MOVXA,@R0
MOVR7,A
這時,如果在中斷服務(wù)程序里調(diào)用 ReadByte(0xAA); 就會發(fā)現(xiàn)讀出的數(shù)據(jù)根本不對,因為using 1使得中斷服務(wù)程序在調(diào)用函數(shù)時使用第一組寄存器傳遞參數(shù),編譯器生成的代碼如下:
MOVR7,#0xAA
LCALLReadByte
而ReadByte這個函數(shù)的代碼是使用絕對地址為0x07的第0組寄存器的R7來傳遞參數(shù)的,所以會出問題。
解決方法是在定義ReadByte這個函數(shù)的前面加上"#pragma noaregs",這樣編譯器就會生成不依賴于絕對地址的代碼了,函數(shù)ReadByte被編譯生成的代碼如下:
XCHA,R0
MOVA,R7
XCHA,R0
MOVXA,@R0
MOVR7,A
這樣就可以大膽的使用using了,使用using才是充分利用51架構(gòu)的使用方法。
評論