優化級別說明(僅供參考):
則其中的 Code Optimization 欄就是用來設置C51的優化級別。共有9個優化級別(書上這麼寫的),高優化級別中包含了前面全部的優化級別。現將各個級別說明以下:
0級優化:
一、 常數摺疊:只要有可能,編譯器就執行將表達式化爲常數數字的計算,其中包括運行地址的計算。
二、 簡單訪問優化:對8051系統的內部數據和位地址進行訪問優化。
三、 跳轉優化:編譯器老是將跳轉延至最終目標上,所以跳轉到跳轉之間的命令被刪除。
1級優化:
一、 死碼消除:無用的代碼段被消除。
二、 跳轉否決:根據一個測試回溯,條件跳轉被仔細檢查,以決定是否可以簡化或刪除。
2級優化:
一、 數據覆蓋:適於靜態覆蓋的數據和位段被鑑別並標記出來。鏈接定位器BL51經過對全局數據流的分析,選擇可靜態覆蓋的段。
3級優化:
一、「窺孔」優化:將冗餘的MOV命令去掉,包括沒必要要的從存儲器裝入對象及裝入常數的操做。另外若是能節省存儲空間或者程序執行時間,複雜操做將由簡單操做所代替。
4級優化:
一、 寄存器變量:使自動變量和函數參數儘量位於工做寄存器中,只要有可能,將不爲這些變量保留數據存儲器空間。
二、擴展訪問優化:來自IDATA、XDATA、PDATA和CODE區域的變量直接包含在操做之中,所以大多數時候沒有必要將其裝入中間寄存器。
三、局部公共子式消除:若是表達式中有一個重複執行的計算,第一次計算的結果被保存,只要有可能,將被用做後續的計算,所以可從代碼中消除繁雜的計算。
四、 CASE/SWITCH語句優化:將CASE/SWITCH語句做爲跳轉表或跳轉串優化。
5級優化:
一、 全局公共子式消除:只要有可能,函數內部相同的子表達式只計算一次。中間結果存入一個寄存器以代替新的計算。
二、 簡單循環優化:以常量佔據一段內存的循環再運行時被優化。
6級優化:
一、 迴路循環:若是程序代碼能更快更有效地執行,程序迴路將進行循環。
7級優化:
一、 擴展入口優化:在適合時對寄存器變量使用DPTR數據指針,指針和數組訪問被優化以減少程序代碼和提升執行速度。
8級優化:
一、 公共尾部合併:對同一個函數有多處調用時,一些設置代碼可被重複使用,從而減少程序代碼長度。
9級優化:
一、 公共子程序塊:檢測重複使用的指令序列,並將它們轉換爲子程序。C51甚至會從新安排代碼以得到更多的重複使用指令序列。
固然,優化級別並不是越高越好,應該根據具體要求適當選擇。
Keil C51總線外設操做問題的深刻分析
閱讀了《單片機與嵌入式系統應用》2005年第10期雜誌《經驗交流》欄目的一篇文章《Keil C51對同一端口的連續讀取方法》(原文)後,筆者認爲該文並未就此問題進行深刻準確的分析 文章中提到的兩種解決方法並不直接和簡單。筆者認爲這並不是是Keil C51中不能處理對一個端口進行連續讀寫的問題,而是對Kei1 C51的使用不夠熟悉和設計不夠細緻的問題,所以特撰寫本文。
本文中對原文提到的問題,提出了三種不一樣於原文的解決方法。每種方法都比原文中提到的方法更直接和簡單,設計也更規範。(無心批評,請原文做者見諒)
1 問題回顧和分析
原文中提到:在實際工做中遇到對同一端口反覆連續讀取,Keil C51編譯並未達到預期的結果。原文做者對C編譯出來的彙編程序進行分析發現,對同一端口的第二次讀取語句並未被編譯。但惋惜原文做者並未分析沒有被編譯的緣由,而是匆忙地採用一些不太規範的方法試驗出了兩種解決辦法。
對此問題,翻閱Keil C51的手冊很容易發現:KeilC51的編譯器有一個優化設置,不一樣的優化設置,會產生不一樣的編譯結果。通常狀況缺省編譯優化設置被設定爲8級優化,實際最高可設定爲9級優化:
1. Dead code elimination。
2.Data overlaying。
3.Peephole optimization。
4.Register variables。
5.Common subexpression elimination。
6.Loop rotation。
7.Extended Index Access Optimizing。
8.Reuse Common Entry Code。
9.Common Block Subroutines。
而以上的問題,正是因爲Keil C51編譯優化產生的。由於在原文程序中將外設地址直接按以下定義:
unsigned char xdata MAX197 _at_ 0x8000
採用_at_將變量MAX197定義到外部擴展RAM 指定地址0x8000。所以,Keil C51優化編譯理所固然認爲重複讀第二次是沒有用的,直接用第一次讀取的結果就能夠了,所以編譯器跳過了第二條讀取語句。至此,問題就一目瞭然了。
2 解決方法
由以上分析很容易就能提出很好的解決辦法。
2.1 最簡單最直接的辦法
程序一點都不用修改,將Keil C51的編譯優化選擇設置爲0(不優化)就能夠了。選擇project窗口的Target,而後打開「Options for Target」設置對話框,選擇「C51」選項卡,將「Code Optimiztaion」中的「Level」選擇爲「0:Costant folding」。再次編譯後,你們會發現編譯結果爲:
CLR MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV down8,R7
SETB MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV up4,R7
兩次讀取操做都被編譯出來了。
2.2 最好的方法
告訴Keil C51,這個地址不是通常的擴展RAM,而是鏈接的設備,具備「揮發」特性,每次讀取都是有意義的。能夠修改變量定義,增長「volatile」關鍵字說明其特徵:
unsigned char volatile xdata MAX197 _at_ 0x8000;
也能夠在程序中包含系統頭文件;「#include<absacc.h>」,而後在程序中修改變量,定義爲直接地址:
#define MAX197 XBYTE
這樣,Keil C51的設置仍然能夠保留高級優化,且編譯結果中,一樣兩次讀取並不會被優化跳過。
2 3 硬件解決方法
原文中將MAX197的數據直接鏈接到數據總線,而對地址總線並未使用,採用一根端口線選擇操做高低字節。很簡單的修改方法就是使用一根地址線選擇操做高低字節便可。好比:將P2.0(A8)鏈接到原來P1.0鏈接的HBEN腳(MAX197的5腳).在程序中分別定義高低字節的操做地址:
unsigned char volatile xdata MAX197_L _at_ 0x8000;
unsigned char volatile xdata MAX197_H _at_ 0x8100;
將原來的程序:
MAXHBEN =0;
down8=MAX197;//讀取低8位
MAXHBEN =1;
up4=MAX197;//讀取高4位
改成如下兩句便可
down8= MAX197_L;//讀取低8位
up4=MAX197_H;//讀取高4位
3 小結
Keil C51通過長期考驗和改進以及大量開發人員的實際使用,已經克服了絕大多數的問題,而且其編譯效率也很是高。對於通常的使用.很難再發現什麼問題。筆者曾經粗略研究過一下Keil C51優化編洋的結果.很是佩服Keil C51設計者的智慧,一些C程序編譯產生的彙編代碼.甚至比通常程序員直接用匯編編寫的代碼還要優秀和簡練 經過研讀Kell C51編譯產生的彙編代碼.對提升彙編語言編寫程序的水平都是頗有幫助的。
由本文中的問題能夠看出:在設計中遇到問題時.必定不要被表面現象矇蔽,不要急於解決,應該認真分析,找出問題的緣由.這樣才能從根本上完全解決問題。
附表:Keil C51中的優化級別及優化做用 級別 說明
0 常數合併:編譯器預先計算結果,儘量用常數代替表達式。包括運行地址計算。
優化簡單訪問:編譯器優化訪問8051系統的內部數據和位地址。
跳轉優化:編譯器老是擴展跳轉到最終目標,多級跳轉指令被刪除。
1 死代碼刪除:沒用的代碼段被刪除。
拒絕跳轉:嚴密的檢查條件跳轉,以肯定是否能夠倒置測試邏輯來改進或刪除。
2 數據覆蓋:適合靜態覆蓋的數據和位段被肯定,並內部標識。BL51鏈接/定位器能夠經過全局數據流分析,選擇可被覆蓋的段。
3 窺孔優化:清除多餘的MOV指令。這包括沒必要要的從存儲區加載和常數加載操做。當存儲空間或執行時間可節省時,用簡單操做代替複雜操做。
4 寄存器變量:若有可能,自動變量和函數參數分配到寄存器上。爲這些變量保留的存儲區就省略了。
優化擴展訪問:IDATA、XDATA、PDATA和CODE的變量直接包含在操做中。在多數時間不必使用中間寄存器。
局部公共子表達式刪除:若是用一個表達式重複進行相同的計算,則保存第一次計算結果,後面有可能就用這結果。多餘的計算就被刪除。
Case/Switch優化:包含SWITCH和CASE的代碼優化爲跳轉表或跳轉隊列。
5 全局公共子表達式刪除:一個函數內相同的子表達式有可能就只計算一次。中間結果保存在寄存器中,在一個新的計算中使用。
簡單循環優化:用一個常數填充存儲區的循環程序被修改和優化。
6 循環優化:若是結果程序代碼更快和有效則程序對循環進行優化。
7 擴展索引訪問優化:適當時對寄存器變量用DPTR。對指針和數組訪問進行執行速度和代碼大小優化。
8 公共尾部合併:當一個函數有多個調用,一些設置代碼能夠複用,所以減小程序大小。
9 公共塊子程序:檢測循環指令序列,並轉換成子程序。Cx51甚至重排代碼以獲得更大的循環序列。
優化論
談到優化,其實不少人都啼笑皆非,由於在一個C51軟件工程師的生涯中,總要被KEIL的優化耍那麼一次到幾回。我被耍過,想必看着文章的你也被耍過,若是你回答說不,那隻能說你寫的C51程序很少!
看看KEILC的優化級別選項吧:
0-9共10個級別的優化,0是最低,9最高,一個普通的程序,設置最高級別和最低級別,編譯後代碼量有時會相差很遠,以DX板DEMO程序爲例,0級優化後是14K的CODE,9級優化後是10K的CODE,先後相差了4K。可見這個差異是多麼的大。
事實上咱們不須要知道對應的各個級別KEIL會如何優化你的程序或優化了些什麼,咱們只須要以一種嚴謹的態度去編寫和對待你的程序就能夠了。在我我的的觀念中,程序在9級優化後依然能保持完美無誤的運行,你纔算瞭解KEIL的脾氣。
好了,仍是說點正點的:
有些人習慣總體程序都選擇同一個優化級,事實上每一個C文件均可以有獨立的優化級別的:
在工做區右鍵選擇你的模塊(.C)而後選取Options for File xxx就會出現以下界面:
在C51選項中就能夠選擇優化級別和警告級別等東西了,被獨立設置過的C文件會有特殊的標記的:
用以提醒你這個文件的編譯處理並不是默認設置!
若是你以爲模塊優化都不夠細的話,你能夠考慮局部優化,也就是說對某個函數實行某個級別的優化。當你發現9級優化的時候某個函數老是變的不正常,但你又但願其它函數和程序段保持最高的簡潔度,那麼局部優化能夠說是至關有用的了。在KEIL手冊中有介紹這個功能:
#pragma OPTIMIZE(x) x就是你但願的優化級別,通常應用以下:
#pragma OPTIMIZE(6)
void FunA()
{
}
......
......
#pragma OPTIMIZE(9)
void FunB()
{
}
上面的意思就是說,在void FunA()到void FunB()以前的全部函數,包括FunA在內,都採用6級的優化,而從FunB開始直到以後,只要沒碰上#pragma OPTIMIZE,都採用9級優化了。
OPTIMIZE還能夠多一個參數,就是speed和size,
用法: #pragma OPTIMIZE(9,speed)或#pragma OPTIMIZE(5,size)
對應的就是9級優化,以速度爲主,或5級優化,以空間最小爲主。
4.StartUp.a51
在以前第一節的創建工程中就曾經提到過StartUp.a51這個東西了,就是在工程初建的時候有個對話框用於選擇是否爲工程添加這個a51文件。
其實這個文件給你們最最深入的感受就是:開機清空RAM。事實上它還有其餘特別的用途的,例如初始化堆棧(不少人不知道KEILC一開始把堆棧設定爲多少,事實上能夠經過軟件仿真的時候從這個文件找到答案),而後是再入函數的虛擬堆棧的設置,還有更高級一點的,BANK的初始化。
舊版本KEIL自動爲每一個工程默認添加相同的StartUp文件,後期的KEIL就有了上圖的選擇,若是選擇添加,則會爲每一個工程添加一個獨立的StartUp。用戶能夠經過手工改寫StartUp.a51實現某些必要的上電初始化。例如最一般的:取消單片機開機清RAM功能!!
關於STARTUP的介紹,我建議你們看看如下的文章,它的解釋很是詳盡。程序員