天天看點

關于單片機的using使用

C51的中斷函數的格式為:void FuncIr(void) interrupt x [using y] 以下是夢遊的一些分析:

       一、中斷函數是一個特殊的函數,沒有參數,也沒有傳回值;但是程式中允不允許使用return呢?答案是允許的,不過隻能用"return;",不能用"return(z);";用在一些需要快速傳回的地方,對應的彙編會有多個ret語句,相對效率會高一些。

       二、using的用法,using可以修飾任何函數,不過個人建議隻用來修飾中斷函數;簡單的說,“using”會指定工作寄存器組,由于中斷函數一般都是比較緊急的事情,有時一條語句都會斤斤計較,是以使用using切換寄存器組可以省去一些壓棧的動作,由于51隻有兩級中斷,同級中斷不能被打斷,是以,我們可以同級中斷設成同樣的寄存器組,從某種意義上來說,有一組寄存器是多餘的。同時個人建議中斷函數應該使用using這個關鍵字。

       三、中斷中調用函數,首先要讨論中斷函數中調用函數的必要性,前天在論壇上我和别人争論過這個問題,現在我還是這個觀點:有些情況中斷中調用函數還是必要的,這個時候是不是該調用函數,其實和普通函數差不多,首先是這個函數如果調用多次,或者要帶一些參數什麼的就更加必要的;前天有人跟我叫勁,說假如隻調用一次且無參數無傳回的函數要直接寫,因為如果用函數,至少會增加CALL和RET兩條語句,我不敢苟同,我是實際調試發現的,當你程式比較複雜時,你将那部單獨拉出來做成函數,可能代碼和時間都會更好。

       四、中斷中調用的函數最好不要被中斷外的其它函數調用,因為會出現“重複調用”的警告,有時這種調用是很緻命的,有人說這個函數可以用reentrant來修飾,是的,的确可以這樣解決,不過個人不建議這麼做,也許這樣會跟你減少很多堆棧空間,并且整個程式的優化要差很多,個人建議出現這種情況就把這個函數寫兩遍,分成兩個函數分别調用。

         五、中斷調用了函數,會出現一些莫名其妙的問題,一些資料不對(我現在遇到這個問題)其實一般是因為彙編中使用了絕對寄存器引起的,有人說中斷函數使用那個寄存器組,被中斷調用的函數就使用哪個寄存器組(我認為好參考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的函數不能有傳回值(這個地方有問題,應該可以有傳回值,下文說是不能不能傳回bit類型的值),這是緻命傷(是以這不是緻命傷,可以使用using解決這個問題)

       個人推薦的方法有兩種:

       1 、使用“#pragma NOAREGS”禁止使用絕對寄存器

       2 、使用“#pragme RB(x)”來指定本檔案的工作寄存器組

       六、一般說來,要求中斷函數盡可能的短,但也有特殊情況,有些前/背景的系統中,就會把很多相對重要的事情放到定時中斷(這個定時中斷類似實時作業系統中的時鐘節拍)去做,而且程式很長。我單獨提出來這點是想告訴大家,中斷函數也是一個函數而已,隻要系統有必要,可以做一些看似不合理的事情,該出手時就出手,就像goto語句一樣。

轉自http://www.ednchina.com/blog/hotchip/,請大家去他的部落格中支援他,裡面有不錯的文章。括号中是我的了解 關于using:

舉個例子來說:

定義一個函數

void func(unsigned char i) {

...

}

有如下一個中斷函數

void int_0(void) interrupt 0 using 1 {

....

}

在預設狀态下,func使用寄存器組0(BANK0),那麼當int_0調用func時是否存在當傳遞參數時會造成參數傳遞錯誤? 如果在中斷服務函數ISR中使用寄存器,那麼必須處理好using的使用問題:

1 、中斷服務函數使用using指定與主函數不同的寄存器組(主函數一般使用Register bank 0)。

2 、中斷優先級相同的ISR可用using指定相同的寄存器組,但優先級不同的ISR必須使用不同的寄存器組,在ISR中被調用的函數也要使用using指定與中斷函數相同的寄存器組。(應該是這樣的)

3 、如果不用using指定,在ISR的入口,C51預設選擇寄存器組0,這相當于中斷服務程式的入口首先執行指令:

MOV PSW #0

這點保證了,沒使用using指定的高優先級中斷。可以中斷使用不同的寄存器組的低優先級中斷。

4 、使用using關鍵字給中斷指定寄存器組,這樣直接切換寄存器組而不必進行大量的PUSH和POP操作,可以節省RAM空間,加速MCU執行時間。寄存器組的切換,總的來說比較容易出錯,要對記憶體的使用情況有比較清晰的認識,其正确性要由你自己來保證。特别在程式中有直接位址通路的時候,一定要小心謹慎!至于“什麼時候要用到寄存器組切換”,一種情況是:當你試圖讓兩個(或以上)作業同時運作,而且它們的現場需要一些隔離的時候,就會用上了。在ISR或使用實時作業系統RTOS中,寄存器非常有用。

寄存器組使用的原則:

1 、8051的最低32個位元組分成4組8寄存器。分别為寄存器R0到R7。寄存器組由PSW的低兩位選擇。在ISR中,MCU可以切換到一個不同的寄存器組。對寄存器組的通路不可位尋址,C51編譯器規定使用using或禁止中斷的函數(#pragma disable)均不能傳回bit類型的值。

2 、主程式(main函數)使用一組,如bank 0;低中斷優先級的所有中斷均使用第二組,如bank 1;高中斷優先級的所有中斷均使用再另外一組,如bank 2。顯然,同級别的中斷使用同一組寄存器不會有問題,因為不會發生中斷嵌套;而高優先級的中斷則要使用與低優先級中斷不同的一組,因為有可能出現在低優先級中斷中發生高優先級中斷的情況。編譯器會自動判斷何時可使用絕對寄存器存取。

3 、在ISR中調用其它函數,必須和中斷使用相同的寄存器組。當沒用NOAREGS指令做明确的聲明,編譯器将使用絕對寄存器尋址方式通路函數標明(即用using或REGISTERBANK指定)的寄存器組,當函數假定的和實際所選的寄存器組不同時,将産生不可預知的結果,進而可能出現參數傳遞錯誤,傳回值可能會在錯誤的寄存器組中。

舉一例子:當需要在中斷内和中斷外調用同一個函數,假定按照程式的流程控制,不會出現函數的遞歸調用現象,這樣的調用會不會出現問題?若确定不會發生重入情況,則有以下兩種情況:

1 、如果ISR和主程式使用同一寄存器組(主程式預設使用BANK 0,若ISR沒有使用using為其指定寄存器區,則預設也使用BANK 0),則不需其他設定。

2 、如果ISR和主程式使用不同的寄存器組(主程式預設使用BANK 0,ISR使用using指定了其他BANK),則被調用函數必須放在:

#pragma NOAREGS

#pragma AREGS

控制參數對中,指定編譯器不要對該函數使用絕對寄存器尋址方式;或者也可在Options->C51,選中“Don''t use absolute register accesses”,使所有代碼均不使用絕對寄存器尋址方式(這樣,執行效率将稍有降低)。不論以上的哪一種情況,編譯器均會給出重入警告,需手工更改OVERLAY參數,做重入說明。

3 、還有一種辦法:如果被調用函數的代碼不是很長,還是将該函數複制一份,用不同的函數名代替,這種情況适合ROM有足夠多餘的空間。

是以,對using關鍵字的使用,如果沒把握,甯可不用,交給編譯系統自己去處理好了。

例子

用上using可以精簡代碼,節省堆棧,不過有時會出現一個問題:

用上using ,在中斷服務程式裡調用函數要小心一點,因為keil C有時會産生依賴絕對位址的代碼,例如如下函數,功能是從片外的儲存設備中讀取一個位元組:

uchar ReadByte(uchar address)

{

  retrun PBYTE[address];

}

會被編譯成如下代碼:

  MOV      R0,0x07

  MOVX     A,@R0

  MOV      R7,A

這時,如果在中斷服務程式裡調用 ReadByte(0xAA); 就會發現讀出的資料根本不對,因為using 1使得中斷服務程式在調用函數時使用第一組寄存器傳遞參數,編譯器生成的代碼如下:

  MOV      R7,#0xAA

  LCALL    ReadByte

而ReadByte這個函數的代碼是使用絕對位址為0x07的第0組寄存器的R7來傳遞參數的,是以會出問題。

解決方法是在定義ReadByte這個函數的前面加上"#pragma noaregs",這樣編譯器就會生成不依賴于絕對位址的代碼了,函數ReadByte被編譯生成的代碼如下:

XCH      A,R0

MOV      A,R7

XCH      A,R0

MOVX     A,@R0

MOV      R7,A

這樣就可以大膽的使用using了,使用using才是充分利用51架構的使用方法。

繼續閱讀