Holtek C Compiler V3--advanced 2014/11/21 合泰/陳培鑫
前言 本課程假定讀者已具備如下素質: 本課程基於HT-IDE3000 V7.85及Holtek C Compiler V3.3編寫 已經閱讀並理解所使用單片機的資料手冊 本課程基於HT-IDE3000 V7.85及Holtek C Compiler V3.3編寫 2
Contents 一、V3的優點 二、擴展語法及功能 三、V3不支持的C語法 四、選項介紹 五、編譯器使用的暫存器資源 六、常見問題的解決方式 3
一、V3的優點 4
V3的優點 與其他廠家編譯器比較 V2/V1轉V3實際案例 V3對V2改進的功能 5
與其他廠家編譯器比較 與其他廠家編譯器比較 以Hi-Tech C為100分, 分數越高代表 備註:測試條件 Holtek:以HT46CU67寫測試code (ROM: 8K word x 4, RAM: 192 bytes x 4) Compiler分別使用Holtek C V3與Hi-Tech C for Holtek兩版 Microchip:以PIC16F1518寫測試code (ROM: 16K word, RAM: 1024 bytes) 以Hi-Tech C for Microchip 進行比較 松翰: 以SN8P2839寫測試code (ROM: 24K word, RAM: 2048 bytes) 十速: 以TM57FM88寫測試code (ROM: 8K word, RAM: 288 bytes) ROM Code 效率 評比 Holtek C V3: 116分 Hi-tech C: 100分 Microchip: 145分 十速: 62分 Sonix: 40分 Holtek C V3(長指令): 136分 6
V2/V1轉V3實際案例 客戶/MCU信息 舊版code size V3 code size 北京客戶, 使用HT56RB27(V2) ROM:48K ROM:40% RAM bank0 overflow ROM:33% RAM bank0:剩餘44byte 歐洲客戶, 使用HT66F017(V1) ROM:2K ROM:81% ROM:72% USB FW程式 HT82K95A(V2) ROM:4K ROM:95% ROM:65% 7
V3對V2改進的功能 8 V3 V2 全域變數 支持初始化 不支持初始化 const變數 支持最大64page 支持extern 支援指定位址 超過1page可能出錯 不支持extern 不支援指定位址 陣列 支持三維以上 最多支援到二維 中斷 可調用函式 不能調用函式 入口函式 支援設定入口函式 不支援入口函式 長指令MCU(如HT66F70A) 支持 不支持 程式效能 較高 較低 8
二、擴展語法及功能 9
擴展語法及功能 中斷服務程式 絕對位址變數 指定const變數的位址 內嵌組合語言 定義位元變數 指定函式的位址 __attribute__關鍵字 V3代碼生成器 MCU頭文檔介紹 變數初始化 10
中斷服務程式(1/2) 中斷服務程式 語法: 範例: 說明:必須使用__attribute__((interrupt(vector)))設定中斷向量值 void __attribute__((interrupt(vector))) ISR_name (void) { } volatile unsigned char TM_5ms = 0; void __attribute__((interrupt(0x14))) ISR_STM (void) { TM_5ms ++; _t2af = 0; } 11
中斷服務程式(2/2) 中斷服務程式必須遵守下列規定: interrupt 返回的數據類型必須是 void 不能有參數 vector是中斷入口位址,為4的倍數,比如0x04,0x08… 中斷入口會自動保存暫存器(ACC,BP,STATUS,MP/TBLP),並在中斷出口時恢複 既可以被中斷服務程序訪問也可以被其他函式訪問的全局變數應定義為volatile 中斷可以調用子函式 注:若中斷與主程序調用同一個子函式(linker報出warning),需保證在執行這個子函式時不會進入中斷,否則會導致執行錯誤 注:MP/TBLP只有中斷程式有用到才會自動保存 12
絕對位址變數(1/2) 絕對位址變數 語法 範例 說明:addr表示變數在RAM中的地址;0x180是定義在RAM bank 1的0x80, static volatile var_type vname __attribute__((at(addr))); static volatile unsigned char num1 __attribute__ ((at(0x180))); static volatile unsigned char tab[3] __attribute__((at(0x280))); 13
絕對位址變數(2/2) 注意事項 absolute address volatile用來修飾被不同程式訪問和修改的變數(比如同時被主程序和中斷服務程式使用),使用volatile修飾,可以防止被優化 一般指定位址的變數都有特殊功能(如特殊暫存器),建議定義成volatile 編譯器會將之翻譯成組合語言的EQU指令,如下: 絕對位址變數的作用域為本文檔,需定義成static static volatile unsigned char num1 __attribute__((at(0x180))); num1 EQU [0180H] 14
定義const變數的位址(1/2) 定義const變數的位址 語法: 範例: 說明:0x700是PROM的地址,其範圍可以是整個PROM;兩個const變數的位址範圍不可以重疊;const變數可以被外部文檔使用,用extern引用 const ctype __attribute__ ((at(addr))) cname = number; const unsigned char __attribute__((at(0x700))) tab1[3]= {1,2,3}; 15
定義const變數的位址(2/2) 定義const變數的位址的條件 const MCU的PROM寬度需為16位且帶TBHP暫存器 勾選以下參數(路徑:選項—工程設置—工程編譯選項) 16
定義位元變數 定義位元變數 bit 先定義一個struct類型bit_type,裡面有N個位域成員 定義struct變數bit_var,它的每個成員就是一個位變數 為方便引用,可以定義一些巨集(flag_a,flag_b,flag_c) typedef struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char bit3 : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; } bit_type; bit_type bit_var; #define flag_a bit_var.bit0 #define flag_b bit_var.bit1 #define flag_c bit_var.bit2 void main() { flag_a=1; flag_b=0; } 17
內嵌組合語言(1/4) 內嵌組合語言 語法1: 範例 說明:compiler直接輸出string字串,可寫在函式外,也可寫在函式體內 18 asm(“string”); extern void FUN() ; asm(“extern _FUN_PAR1:byte”); //外部函式參數的聲明 void main( ) { asm(“mov a,1”); asm(“mov _FUN_PAR1,a”); asm(“call _FUN”); //函式呼叫 } 18
內嵌組合語言(2/4) 內嵌組合語言 語法2: 範例 說明:此語法只可以寫在函式體內,且變數大小不超過1byte。其中:varname 為變數名,“=m”表示輸出到記憶體,“=r”表示輸出到暫存器,“m”表示從記憶體輸入,“r”表示從暫存器輸入。若分不清楚可以寫:asm volatile(“opcode %0” :“=m”(varname)); asm(“opcode %0”:“=m”(varname)[:“m”(varname)]); #include “ht66f50.h” char i; void main() { char c; asm volatile(“rl %0” : “=m”(_pd)); //sfr asm(“mov a, %0” : “=r”(c) : “m”(c)); //區域變數讀值 asm(“mov %0, a” : “=m”(i)); //全域變數寫值 while(1); } 19
內嵌組合語言(3/4) 內嵌組合語言 使用asm()的語法,通常只能輸出一條組合語言指令 若需要輸出一大段組合語言程式,可以勾選以下參數,但此法只能寫在函式內。(路徑:選項—工程設置—工程編譯選項) 20
內嵌組合語言(4/4) 語法 語法: 範例: 21 #asm ;;Code … #endasm #asm INCLUDE HT66F50.INC CLR WDT SET BP.5 CALL _FUNC #endasm 21
指定函式的位址 指定函式的位址 absolute function 語法 範例 說明:addr為函式在PROM的地址,其範圍為整個PROM;兩個函式的位址範圍不可以重疊 Re_type __attribute__((at(addr))) func (par1,par2) { } unsigned char __attribute__((at(0x700)))delay (unsigned int x) { } 22
__attribute__關鍵字(1/4) V3 Compiler目前支持以下三個__attribute__ __attribute__((entry)):指定一個函式的屬性為入口函式 __attribute__((at(addr))):指定函式/變數的位址 __attribute__((interrupt(vector))):中斷入口位址 23
__attribute__關鍵字(2/4) __attribute__((entry)) entry 語法 範例 說明:不限制main/isr設定為entry,但此時attribute entry將無效 __attribute__((entry)) void func (void){} __attribute__((entry)) void init(void) { _acerl=0; _c0sel=0; _c1sel=0; _pac = 0; } 24
__attribute__關鍵字(3/4) __attribute__((at(addr))) 說明 addr為指定地址,不可缺失 變數定義了(at(addr))屬性後需定義成static const變數也可定義成(at(addr))屬性,僅限於具有TBHP,且PROM寬度為16bit的MCU main/isr也可設定(at(addr))屬性 25
__attribute__關鍵字(4/4) 一個函式可以定義多種屬性: __attribute__((entry,at(addr))) __attribute__((interrupt(vector),at(addr))) 26
V3代碼生成器(1/3) V3代碼生成器(路徑:工具—V3代碼生成器) 27
V3代碼生成器(2/3) V3代碼生成器功能 生成位元變數代碼 生成指定bank變數 生成指定位址變數 生成內嵌組合語言代碼 生成中斷函式頭 生成延時函式 生成入口函式和指定位址函式 28
V3代碼生成器(3/3) V3代碼生成器操作說明 example 參考《HT-IDE3000使用手冊》第三章“V3代碼生成器章節” 例: 29
MCU頭文檔介紹(1/7) MCU頭文檔介紹 HT-IDE3000有自帶MCU的頭文檔,裡面主要是一些暫存器及標誌位元的定義 選用V3 compiler,每顆MCU配有兩個頭文檔”MCU.H”及”build-in.h” 在工程中,只需要寫#include “MCU.h”,比如:#include “ht66f50.h”就會自動引入頭文檔,如下 30
MCU頭文檔介紹(2/7) 絕對位址及中斷語法的簡寫 - 範例: 31 #include “ht66f50.h” DEFINE_SFR(unsigned char, TM_5ms, 0x180); DEFINE_ISR(isr_tm2,0x14) { TM_5ms ++; _t2af = 0; } 31
MCU頭文檔介紹(3/7) 特殊暫存器的定義以及位的定義 - 範例 32 #include “ht66f50.h” void main() { _pac0=1; while(1); } 32
MCU頭文檔介紹(4/7) 內建函式的定義 33 說明: varname是參數,大小為1 byte 範例: #include “ht66f50.h” void main() { GCC_CLRWDT(); GCC_RL(_pa); GCC_NOP(); } 33
MCU頭文檔介紹(5/7) 寫EEPROM巨集的定義 若MCU帶EEPROM,則頭文檔會定義__EEPROM_DATA巨集 34
MCU頭文檔介紹(6/7) 說明 __EEPROM_DATA不可以在函式體內調用,需寫在函式外 參數是常數或字元,每次只可寫8個 35 資料編輯器路徑: 工具—編輯器—資料編輯器 範例: #include "HT66F50.h" __EEPROM_DATA(0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7); __EEPROM_DATA('a', 'b','c', 'd', 'e', 'f', 'g', 'h'); void main() { } 35
MCU頭文檔介紹(7/7) 選擇以下選項,就可以download到EEPROM 調試選項路徑: 選項—工程設置—調試選項 36
變數初始化(1/3) 變數初始化 全域變數的初始化用startup code來實現,在main()執行前完成初始化動作。 選項路徑:選項-工程設置-編譯選項-連接選項 PROM寬度16bit,有TBHP暫存器 不滿足左欄條件 無勾選此項 startup1_l.asm startup1.asm 有勾選此項 startup0_l.asm startup0.asm 37
變數初始化(2/3) 變數初始化 (選項路徑:選項-工程設置-編譯選項-連接選項) 38
變數初始化(3/3) 變數初始化 39
三、V3不支援的C語法及MCU 40
V3不支援的C語法及MCU MP(Memory Pointer)寬度只有7bit的MCU 函式指標 遞迴函式 部分舊架構的Holtek MCU的MP只有7bit,為達到更好的優化效能C Compiler V3不支援此類MCU,具體的MCU型號可參考文件《Holtek C Compiler V3 FAQ 》 函式指標 V3不支援函式指標,定義函式指標將會報error 遞迴函式 V3不支持遞迴呼叫,但一種比較特殊的遞迴呼叫(尾遞迴呼叫),編譯器可以將它優化成非遞迴函式. 41
四、V3編譯器的選項介紹 42
V3編譯器的選項介紹 定義符號 編譯參數 連接選項 增加庫檔 設定函式位址 43
定義符號 定義符號(選項路徑:選項-工程設置) - 可以在IDE“項目設定”框中輸入多個巨集定義,用逗號隔開 - 如下圖紅框設置,IDE將會給編譯器傳參數: -D__V3__ , -DGCC_COMPILER, -DCOUNT=5 (__V3__是IDE默認給V3傳遞的參數) 44
編譯參數(1/3) 編譯參數 (選項路徑:選項-工程設置-編譯選項) 45
編譯參數(2/3) 編譯參數 組合語言區分大小寫:用於混合語言工程,若一個工程既有c file又有asm file時,可以開啟這個選項(C語言區分大小寫,asm默認不區分). 組合語言字串,2個字元佔用一個word:用於asm file,當所選MCU寬度為16 bits(如HT66F50)時,可以開啟這個選項,則字串中的每兩個字元會佔用一個word,否則預設一個字元佔用一個word.若所選MCU為14,15bits(如HT46R4AE),則不可啟用這個選項. 開啟編譯器額外的編譯警告:比如:缺省的函式返回值,無符號數與0的比較等等。 相容Holtek C V2內嵌組合語言:可以使用#asm/#endasm的語法完成內嵌組合語言功能 存取const變數時使用查表指令進行優化:有TBHP暫存器且PROM寬度為16bit的短指令MCU會有此選項,勾選之後table的大小減半,若要指定table的位置,此選項必須勾選. 46
編譯參數(3/3) 編譯參數 開啟編譯器的所有編譯警告(默認勾選) 優化生成的代碼 [-Os](默認勾選) 存取const變數時使用查表指令進行優化(默認勾選) 47
連接選項(1/2) 連接選項 (選項路徑:選項-工程設置-編譯選項-連接選項) 48
連接選項(2/2) 連接選項 優化RAM空間(不允許巢狀中斷):優化RAM空間,不允許在一個中斷裡面進入另一個中斷。 刪除沒有被調用的函式(針對C):若一個函式不被調用,將不為其分配空間,這個選項預設開啟。 將未初始化的全域變數,全域/局部靜態變數預設設為0:若全域變數沒有初始值,則默認其初始值為0。在程式開始運行的時候,將其清0。 絕對位址變數位址重疊時發出警告:檢查絕對位址變數的位址是否重疊,並在重疊時發出warning。 49
增加庫檔(1/2) 增加庫檔 (選項路徑:選項-工程設置-目錄) - 將庫文件路徑目錄加入目錄 50
增加庫檔(2/2) 增加庫檔 敲入庫檔案名,如math.lib,mull.lib,用“ , ”隔開 即可在程式中調用lib中的函式,變數等. 51
設定函式位址 設定函式位址 格式: _fun_name=addr,_fun2_name=addr2,… 52
五、V3編譯器管理的資源 53
V3編譯器管理的資源 Holtek MCU的某些特殊功能暫存器由C Compiler V3使用,用戶使用時要 小心,進入中斷服務程式時會自動保存所有用到的暫存器。 編譯器用到的特殊暫存器 主要用途 MP/IR 用於存取RAM space的值,搭配RAM BP使用 BP RAM BP用於存取RAM space的值,ROM BP用於訪問ROM space STATUS 用於運算式計算 TBLP 用於查表 用於存儲RAM BP TBHP TBLH ACC 存儲函式返回值等 PCL 54
六、常見問題處理 55
常見問題處理(1/11) 如何將一個變數定義於其它bank(非bank0)? 若是無長指令架構的MCU,只能將變數定義在指定位址,如: 可使用V3代碼生成來生成代碼。 若是長指令架構的MCU,無需特別指定,linker會自動分配變數到任意bank。 static unsigned int buffer[20] __attribute__ ((at(0x180))); 56
常見問題處理(2/11) Error(L1038) RAM(bank0 )overflow,memory allocation fails for section …. 出現原因:RAM bank 0配置不下 - 對於短指令架構的MCU, C Compiler會預設把變數配置到RAM bank0(長指 令的MCU可以自動配置到任意bank),當bank0滿了之後,會報RAM bank 0 overflow出現此資訊後 解決方式: - 檢查資料類型是否誤用(特別是從V1 C Compiler移植過來的程式) - 若為multi RAM bank MCU,可手動將全域變數調到其它bank 57
常見問題處理(3/11) Error(L1038) ROM/RAM(bank* )overflow,memory allocation fails for section …. 出現原因 - ROM或RAM空間不夠 解決方式 - 檢查是否打開優化參數-Os - 優化程式寫法 58
常見問題處理(4/11) Warning(L3010)(absolute address:xxh,length:x) is overlay with(absolute address:xxh,length:x) 出現原因 - 兩個絕對位址變數重疊: 解決方式 - 確認是否有意重疊,否則,修改地址 - 若確定沒有變數發生重疊, 可不勾選連接選項“絕對位址重疊發出警告”讓 Linker不報出 DEFINE_SFR(unsigned int _a, 0x04); DEFINE_SFR(unsigned char _b, 0x05); DEFINE_SFR(unsigned char _b, 0x06); 59
常見問題處理(5/11) 中斷warning 出現原因 解決方式 中斷與main共同調用func函式 確認是否會在執行func時進入中斷 不會,則沒關係 會,修改程式寫法,不要共同調用 60
常見問題處理(6/11) 啟用V3優化參數後,無法在Watch Window查看某些變量數值? 因啟用優化參數,變數有可能被優化刪除,故無法查看 若要在debug時觀察變數值,可以將此變數暫時定義為volatile, debug結束後再刪去 比如: volatile int i,j,k; 61
常見問題處理(7/11) 中斷與主程式訪問同一個全域變數,此變數的相關語句被優化掉導致執行錯誤? interrupt 主程式與中斷沒有調用關係,Compiler不知中斷何時發生,影響到主程式中的變數,因此,建議將此變數用volatile修飾 62
常見問題處理(8/11) 使用V3 compiler,debug時行號出錯? - 某些語句被優化掉而不譯出code,自然也沒有debug信息 63
常見問題處理(9/11) 使用V3 compiler,debug時行號出錯? - 幾條語句翻譯出同一堆的code,只顯示一條line number 64
常見問題處理(10/11) 使用V3 compiler,debug時行號出錯? 以上行號出錯的幾種情況都不會影響執行正確性 - 尾部合併優化,編譯器中翻譯出其中一段,只顯示一條line number 以上行號出錯的幾種情況都不會影響執行正確性 00 if ( user_value ) 01 { 02 PORTB = 0x55; 03 user_value=0; 04 } 05 else 06 { 07 PORTB = 0x80; 08 user_value=0; 09 } 65
常見問題處理(11/11) 使用V3 compiler,用於延時的迴圈代碼被優化,怎麼解決? 將變數定義成volatile,或使用內建延時函式GCC_DELAY( x ) 其中x為unsigned int型常量。 delay 66
參考文檔 -《HT-IDE3000使用手冊》 介紹IDE,assembler,linker等tool的使用,在HT-IDE3000安裝檔DOC目錄下 -《Holtek C Compiler V3使用手冊》: 介紹Holtek C Compiler V3的使用,在HT-IDE3000安裝檔DOC目錄下 -《Holtek標準函式程式庫使用手冊》: 介紹Holtek C支援的標準函式程式庫及使用方式,在HT-IDE3000安裝檔DOC目錄下 -《Holtek C Compiler V3 FAQ》: C Compiler V3的常見問題,持續更新中,下載網址:http://www.holtek.com/chinese/tech/tool/MCU_Tools_Users_Guide.htm -《gcc manual》 GCC使用手冊下載網址http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc.pdf 67
END