Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Revision: 1.30 Date: �������������� March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 目錄 第一章 內容簡介 ................................................................................................ 6 第二章 選定 Holtek C 編譯器的使用環境 .......................................................... 7 2.1 進入 HT-IDE3000, 建立新的專案時 , 選定 Enhanced Holtek C 編譯器 ....................7 2.2 已開啟專案後 , 如何選用 Enhanced Holtek C 編譯器...............................................8 第三章 微控制器 C 語言程式的速成 .................................................................. 9 3.1 定義主函式 main() .....................................................................................................9 3.2 定義副函式 (sub-function) ........................................................................................9 3.3 定義全域變數 (global variable) .................................................................................9 3.4 定義中斷服務函式 (Interrupt Service Routine : ISR) .............................................10 3.5 其他 ..........................................................................................................................10 第四章 C 程式語言 ...........................................................................................11 4.1 C 程式架構 ............................................................................................................... 11 4.2 開始用 C 語言設計一個程式 ..................................................................................... 11 4.�.1 定義主函式 main ............................................................................................ 11 4.�.� 將標頭檔引入 (in�lude a �eade� file) ..............................................................1� 4.�.3 定義文字符號及變數.......................................................................................1� 4.�.4 設定微控制器及裝置的初始狀態.....................................................................1� 4.�.� 設計子函式 .....................................................................................................13 4.�.6 設計中斷服務函式 ..........................................................................................14 4.3 變數 (variable) 及資料型態 (data type) ...................................................................14 4.3.1 變數名 ............................................................................................................14 4.3.� 資料型態.........................................................................................................14 4.3.3 變數的有效範圍 (s�ope) .................................................................................1� 4.3.4 變數的資料型態 (data type)............................................................................1� 4.3.� bit 資料型態 ....................................................................................................16 4.3.6 儲存類別 (sto�age �lass) 與修飾詞 (qualifie�).................................................17 4.3.7 絕對變數 (absolute va�iable) ..........................................................................18 4.3.8 常數 (�onstant) ...............................................................................................18 4.3.9 指標 (pointe�) 與陣列 (a��ay) ..........................................................................19 4.3.10 結構 (st�u�t) 與等位 (union)..........................................................................�0 4.4 運算子 (Operators) ..................................................................................................21 4.5 程式流程控制 (program flow control) ....................................................................23 4.�.1 if-else 敘述 .....................................................................................................�3 4.�.� swit�� 敘述 .....................................................................................................�4 4.�.3 fo� 敘述...........................................................................................................�� 4.�.4 w�ile 敘述.......................................................................................................�� 4.�.� do-w�ile 敘述 .................................................................................................�� 4.�.6 goto 敘述 ........................................................................................................�6 4.�.7 b�eak 與 �ontinue 敘述...................................................................................�6 4.6 函式 (Functions) .....................................................................................................26 4.6.1 參數 (a�guments) ...........................................................................................�6 4.6.� 返回值 (�etu�n values) ....................................................................................�7 4.7 中斷服務函式 (Interrupt Service Routines) ...........................................................27 Rev. 1.30 2 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.8 在 C 語言程式中嵌入組合語言 (in-line assembly code).........................................28 4.9 前置處理指令 (Preprocessor) .................................................................................29 4.9.1 定義文字符號 (#define) ..................................................................................�9 4.9.� 引入檔案 (#in�lude) ........................................................................................�9 4.9.3 內嵌組合語言 (inline assembly) .....................................................................�9 4.9.4 條件式編譯 (#if/#endif) ...................................................................................30 4.9.� 編譯器的特殊選項 p�agma .............................................................................31 4.10 Holtek C 編譯器的內建函式 (built-in functions) ..................................................33 第五章 基本 C 語言程式 .................................................................................. 34 5.1 C 語法觀念 ...............................................................................................................34 5.2 迴圈的應用 (loop) ....................................................................................................34 �.�.1 範例一 : 8 LED 霹靂燈 ...................................................................................34 �.�.� 延遲 ( 等候 ) 時序 (Delay) ...............................................................................3� 5.3 撰寫 MCU 應用程式的注意事項 ...............................................................................37 5.4 可供微控制器應用程式使用的範本 ...........................................................................38 5.5 設計微控制器應用程式的小技巧 ...............................................................................39 第六章 程式範例 – 初級 ................................................................................... 40 6.1 LED 跑馬燈 ..............................................................................................................40 6.1.1 目的 ................................................................................................................40 6.1.� 周邊元件.........................................................................................................40 6.1.3 電路圖 ............................................................................................................40 6.1.4 微控制器的架構設定 (�onfigu�ation option) ....................................................40 6.1.� 程式流程.........................................................................................................41 6.1.6 原始程式.........................................................................................................41 6.1.7 程式說明.........................................................................................................41 6.2 LED 霹靂燈 ..............................................................................................................42 6.�.1 目的 ................................................................................................................4� 6.�.� 周邊元件.........................................................................................................4� 6.�.3 電路圖 ............................................................................................................4� 6.�.4 微控制器的架構設定 (�onfigu�ation option) ....................................................4� 6.�.� 程式流程.........................................................................................................43 6.�.6 原始程式.........................................................................................................43 6.�.7 程式說明.........................................................................................................43 6.3 單顆七段顯示器 ........................................................................................................44 6.3.1 目的 ................................................................................................................44 6.3.� 周邊元件.........................................................................................................44 6.3.3 電路圖 ............................................................................................................44 6.3.4 微控制器的架構設定 (�onfigu�ation options) ..................................................44 6.3.� 程式流程.........................................................................................................4� 6.3.6 原始程式.........................................................................................................4� 6.3.7 程式說明.........................................................................................................4� 6.4 5*5 點矩陣 LED 顯示................................................................................................45 6.4.1 目的 ................................................................................................................4� 6.4.� 周邊元件.........................................................................................................4� 6.4.3 電路圖 ............................................................................................................46 6.4.4 微控制器的架構設定 (�onfigu�ation option) ....................................................46 6.4.� 程式流程.........................................................................................................46 Rev. 1.30 3 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.4.6 原始程式.........................................................................................................47 6.4.7 程式說明.........................................................................................................47 6.5 HT48 微控制器控制 HT1621 LCD 的顯示 ................................................................48 6.�.1 目的 ................................................................................................................48 6.�.� 周邊元件.........................................................................................................48 6.�.3 電路 ................................................................................................................�1 6.�.4 微控制器的架構設定 (�onfigu�ation option) ....................................................�1 6.�.� 程式流程.........................................................................................................�1 6.�.6 原始程式.........................................................................................................�� 6.�.7 程式說明.........................................................................................................�� 6.6 HT48 微控制器控制 LCD 模組的顯示 ......................................................................56 6.6.1 目的 ................................................................................................................�6 6.6.� 周邊元件.........................................................................................................�7 6.6.3 電路圖 ............................................................................................................�7 6.6.4 微控制器的架構設定 (�onfigu�ation option) ....................................................�8 6.6.� 程式流程.........................................................................................................�8 6.6.6 原始程式.........................................................................................................�8 6.6.7 程式說明.........................................................................................................60 6.7 具 LCD 驅動功能的微控制器之顯示應用程式 – HT46R63 .......................................61 6.7.1 目的 ................................................................................................................61 6.7.� 周邊元件.........................................................................................................61 6.7.3 電路圖 ............................................................................................................6� 6.7.4 微控制器的架構設定 (�onfigu�ation option) ....................................................6� 6.7.� 程式流程.........................................................................................................6� 6.7.6 原始程式.........................................................................................................63 6.7.7 程式說明.........................................................................................................64 6.8 顯示器之通用函式 – HD44780 LCM ........................................................................64 6.8.1 initialLCD( ) ....................................................................................................64 6.8.� PutC�a�( ) ......................................................................................................6� 6.8.3 �oveAt( ) .......................................................................................................6� 6.8.4 main( ) ............................................................................................................6� 6.9 鍵盤掃瞄程式............................................................................................................66 6.9.1 目的 ................................................................................................................66 6.9.� 周邊元件.........................................................................................................66 6.9.3 電路圖 ............................................................................................................66 6.9.4 微控制器的架構設定 (�onfigu�ation option) ....................................................66 6.9.� 程式流程.........................................................................................................67 6.9.6 原始程式.........................................................................................................68 6.9.7 程式說明.........................................................................................................68 第七章 應用範例 – 中斷程式............................................................................ 70 7.1 用時鐘控制 LED 的亮與滅........................................................................................70 7.1.1 目的 ................................................................................................................70 7.1.� 周邊元件.........................................................................................................70 7.1.3 電路圖 ............................................................................................................71 7.1.4 微控制器的架構設定 (�onfigu�ation option) ....................................................71 7.1.� 程式流程.........................................................................................................7� 7.1.6 原始程式.........................................................................................................7� 7.1.7 程式說明.........................................................................................................73 Rev. 1.30 4 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 7.2 類比 / 數位轉換 (ADC) 的應用 ..................................................................................73 7.�.1 目的 ................................................................................................................73 7.�.� 周邊元件.........................................................................................................73 7.�.3 電路圖 ............................................................................................................7� 7.�.4 微控制器的架構設定 (Configu�ation Options) ................................................76 7.�.� 程式流程.........................................................................................................76 7.�.6 原始程式.........................................................................................................76 7.�.7 程式說明.........................................................................................................78 第八章 HT46R52A 應用於鎳氫 (NiMH) 電池充電器 (HA0084T) .............................79 8.1 目的 ..........................................................................................................................79 8.2 暫存器及周邊元件 ....................................................................................................80 8.3 電路圖 ......................................................................................................................82 8.4 微控制器的架構設定 (Configuration Options) .......................................................82 8.5 程式流程 ...................................................................................................................83 8.6 原始程式與程式說明 .................................................................................................87 第九章 程式範例 – HT46R74D-1 胎壓計 (HA0105T) .................................... 102 9.1 目的 ........................................................................................................................102 9.2 功能說明 .................................................................................................................102 9.3 電路圖 ....................................................................................................................103 9.4 微控制器的架構設定 (Configuration Options) .....................................................107 9.5 程式流程圖 .............................................................................................................108 9.6 原始程式與程式說明 ............................................................................................... 113 第十章 組合語言與 C 語言的互用 (Mixed Language) .................................. 129 10.1 變數 , 函式與參數的命名規則 ...............................................................................129 10.2 參數的傳遞 ...........................................................................................................129 10.3 返回值的設定........................................................................................................129 10.4 在 C 語言程式中呼叫組合語言函式 (Calling Assembly Function From C Program) ................................................130 10.5 在組合語言程式中呼叫 C 語言函式 (Calling C Funftion from Assembly Progam) ...................................................131 Rev. 1.30 5 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第一章 內容簡介 盛群半導體公司 (Holtek) 開發一系列的八位元微控制器 (mi��o-�ont�olle�� �CU). 當開發微控制 器的應用程式時 � 除了可使用盛群提供的組合語言 (assembly language)� 也可使用標準的 C 語 言編譯器 (C �ompile�) 由於八位元微控制器的記憶體空間 � 不論是程式記憶體 (p�og�am memo�y spa�e) 或是資料記憶 體 (�am memo�y spa�e)� 皆是有限制的 � 通常會使用組合語言開發應用程式 . 但是越來越多的 微控制器支援更多的記憶體以及更多的功能 � 使得程式也相對的擴大 . 如果仍然使用組合語言開 發程式 � 不但費時費力 � 未來在維護及擴增功能的工作上也相當困難 因此 � 使用高階程式語言 � 例如 C 語言 � 來開發應用程式就是一種可行的趨勢。 C 語言是高階程式語言中的一種 � 它具有高度的的可讀性及可移植性 (po�tability)� 除了能夠快速 地完成應用程式的開發與偵錯 � 也很容易移植到其他的微控制器上 . 當程式需要縮減或擴充功能 時 � 也很容易的完成 � 因此很適合於微控制器的程式開發 本書主要是以 Holtek C 語言為主 � 說明如何使用 Holtek C 語言撰寫盛群微控制器的應用程式 � 包括 C 的程式架構 � C 語言的一般用法 � 特殊用法及應用範例 書中將說明在開發微控制器的應用程式時需要注意的地方及如何撰寫會比較恰當 � 並配以實例 解釋 . 讀者可以參考修改或直接採用到自己的程式中 � 再用發展工具 HT-ICE�HT-IDE3000 驗證 之 第二章介紹選用 Holtek C 編譯器的步驟 � 指引 HT-IDE3000 呼叫 Holtek C 編譯器去編譯 C 語 言的原始程式 第三章提供一種快速撰寫 C 程式的方法 � 對 ANSI C 語言熟悉的用者 � 可於閱讀本章之後即開 始撰寫微控制器的 C 語言程式 第四章介紹 C 語言 � 未曾使用過 C 語言的讀者應仔細閱讀本章以了解 C 語言的用法 第五章介紹使用 C 語言寫程式的基本觀念 � 注意事項及建議的寫作方法 第六章到第九章則是應用範例 � 針對盛群各系列的微控制器 � 以 C 語言撰寫的應用程式 . 包含 有功能說明 � 應用電路及程式說明 Rev. 1.30 6 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第二章 選定 Holtek C 編譯器的使用環境 2.1 進入 HT-IDE3000, 建立新的專案時 , 選定 Enhanced Holtek C 編譯器 進入 HT-IDE3000 開發環境後 � 依照下列方法建立一個新的專案 (p�oje�t): → 移動滑鼠游標到 P�oje�t 選單 � 按左鍵 → 移動滑鼠游標到 New 命令 � 按左鍵 → 出現如下的視窗 � 在 Language Tool 之處勾選 En�an�ed Holtek C �ompile�/Assemble� Rev. 1.30 7 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 2.2 已開啟專案後 , 如何選用 Enhanced Holtek C 編譯器 若 專 案 (p�oje�t) 已 開 啟 之 後 � 可 以 點 選 (�li�k) Option 選 單 下 的 P�oje�t Setting 命 令 � 在 Language Tool 中 點 選 En�an�ed Holtek C Compile�/Assemble� 以 設 定 使 用 Holtek C 的 En�an�e 版編譯器 En�an�e 版的 C �ompile� 包括 e���3�s�s�.exe � e���3�m�s�.exe 與 e���3�m�m�.exe 三個執 行檔 Rev. 1.30 8 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第三章 微控制器 C 語言程式的速成 本章介紹如何快速撰寫微控制器的 C 語言應用程式 . 已熟悉 ANSI C 標準語言的用法或有撰寫 的經驗者 � 在閱讀此章後即可開始設計撰寫微控制器的 C 應用程式 � 以下各節是基本的 C 程式 成員 � 某些是必須要有的 � 如 3.1� 其他的則視微控制器的功能及應用來決定是否需要 3.1 定義主函式 main() #in�lude “�t46�63.�" void main(void) { int Flag ; …… Tu�nOn_LCD() ; Flag = LCD_display(�st�) ; Tu�nOff_LCD() ; …… } 檔案 �t46�63.� 定義與微控制器有關的常數 � 例如暫存器的位址定義 � 將之引入 (in�lude) 可增 加程式的可讀性 3.2 定義副函式 (sub-function) 視程式的大小及功能決定是否需要定義副函式 . 基本上 � 主函式應將應用程式的架構做成模組 化 � 不需要將所有的程式皆放在主函式中 . 為了能很快的完成及了解應用程式 � 主函式中只需要 包含 ( 呼叫 ) 定義各功能的副函式即可 � 無論在設計或維護程式時皆能很快的進入與完成 . 例如 � 關於 LCD 的開啟 � 顯示及關閉等功能就可分別定義為單獨的副函式 � 如下例 . 任何其他的函式 或其他的應用專案都可去呼叫這些副函式 . 若設計成通用型的 � 也可藉由程式館管理器 (Lib�a�y �anage�) 將之建入程式館檔案 � 以供其他應用專案使用 void Tu�nOn_LCD(void) { } int LCD_display(char *cstr) { } void Tu�nOff_LCD(void) { } 副 函 式 的 參 數 和 返 回 值 可 以 是 基 本 類 型:int,float,��a�,void; 也 可 以 是 構 造 類 型: st�u�t�union�enum� 數組,指針類型 避免函式調用自身,函式調用最大層數要視 �CU 堆棧層數而定 3.3 定義全域變數 (global variable) 程式在運行中會需要一些變數做為資料存放的地方 , 由於微控制器資料記憶體大小的限制及 C 編譯器的設計 , 最好將常需使用的變數定義為全域型的變數 , 在編譯程式的大小與執行上皆較 佳 . 例如定義常數型指標變數 cstr 指到字串 “Hello!”, 則可如下 : ��a� * �onst �st� = “Hello!"; Holtek C 的全域變數若要初始化必需定義為 �onst 或 �onstant int 類型。 例:�onst ��a� aa = 3; �onstant int bb = 3; Rev. 1.30 9 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 3.4 定義中斷服務函式 (Interrupt Service Routine : ISR) 若微控制器的周邊裝置具有中斷功能 � 程式也需要此中斷機能以完成工作時 � 則必須定義此周邊 裝置的中斷服務函式 (Inte��upt Se�vi�e Routine� ISR)� 如下的格式 : #p�agma ve�to� ISR_tm�0 @ 0x0� void ISR_tm�0(void) { ti�k++ ; } 中斷服務函式必須遵守下列規定 : → 返回的資料型態必須是 void → 不能有參數 ( 必須為 void) → 必須使用前置處理指令 #p�agma ve�to� 設定中斷向量值 (inte��upt ve�to�)� 在函式名稱 ( 本 例子是 ISR_tm�0) 之後加上 @ 及中斷向量值 ( 本例是 0x0�). 也可使用先前定義好的常數 � 例如 : #define VECTOR_T�R0 0x0� #p�agma ve�to� ISR_tm�0 @ VECTOR_T�R0 void ISR_tm�0(void) { } → 中斷裡面若要調用一般函式有兩種方法: 1、直接調用,但被調用函數必需聲明為 nolo�al,以免造成共用 �am 空間的風險,例: #p�agma ve�to� ISR_tm�0 @ 0x0� #p�agma nolo�al fun fun() { } void ISR_tm�0(void) { fun() ; } �、內嵌組合語言,例: #p�agma ve�to� ISR_tm�0 @ 0x0� fun() { } void ISR_tm�0(void) { #asm �all _fun; #endasm } 注:一般不推薦中斷調用一般函式。 3.5 其他 上述的主函式 , 副函式及中斷服務函式不需要定義在同一個原始程式檔案內 . 為縮短編譯的時 間 , 最好是分別定義在不同的檔案中 , 並使用有意義的檔名 , 方便日後找尋所要的函式。 Rev. 1.30 10 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第四章 C 程式語言 基本上 � Holtek C 是仿 ANSI ( 美國國家標準局 ) 標準的 C 語言 � 為配合盛群八位元微控制器的 架構 � 將提供一些特殊的語法去存取或控制微控制器的資源 . 另外 � 本章會從八位元微控制器的 角度 � 說明如何使用 C 語言設計及撰寫微控制器的應用程式。 4.1 C 程式架構 C 語言的程式是由敘述 (statements) 所組成 � 每個敘述的最後必須有分號 " ; " 做為結束符號 . 敘 述分為四類 : → 宣告 (de�la�ation)� 宣告變數及資料型態 � 資料結構 . 例如 ��a� flag� ti�k_�nt ; // 宣告變數 flag 與 ti�k_�nt 為 ��a� 型態 → 定義 (definition)� 定義變數數值及位址 例如 int total = 10 ; // 定義變數 total� 設定值為 10 → 描述式 (exp�ession)� 執行數學及邏輯運算 � 控制程式的流程 例如 �ount = ( input > 10) ? 10 : input ; → 函式呼叫 (fun�tion)� 執行函式的功能 例如 put��a�(��) ; // 寫出一個字元到輸出口 每個敘述可以附加註解做說明 . C 編譯器不會對註解做編譯 � 下列為兩種可被接受的註解 : → 介於符號 /* 與 符號 */ 之間的數據及文字 � 包括換行字 `\n' 如果 /* 與 */ 不在同一行 � 則其間所有的行皆會被視為註解 例如 /* t�is is a �omment 1 */ → 從符號 // 開始到本行的結束皆被視為註解 例如 // 這是註解的新寫法 在 C 語言中 � 程式執行區 (p�og�am blo�k) 是以函式的格式定義 � 因此 � 所有要執行的敘述皆需 定義 ( 包含 ) 於某個函式 (fun�tion) 中 � 例如描述式 . 4.2 開始用 C 語言設計一個程式 依照下列步驟 ¸ 使用 C 語言實作一個簡單的應用程式。 4.2.1 定義主函式 main 範例 : void main(void) { } 在 C 語言中 � 主函式 main 是程式執行的起點 � 有如組合語言程式中的 sta�t ORG 00 jmp sta�t sta�t: void 是資料型態 � main 與 void 皆是保留字 � 必須用此字 � 小寫字母 Rev. 1.30 11 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.2.2 將標頭檔引入 (include a header file) 範例 : #in�lude “�t46�63.�" // 引入標頭檔 �t46�63.� void main(void) { } 標頭檔 �t46�63.� 中定義許多與微控制器有關的變數及文字符號 (symbol). 接下來寫程式時可以 使用這些變數與文字符號 (symbol)� 好處是寫程式或維護程式時會很容易了解程式的功能 � 增加 程式的易讀性 . 例如 : unsigned ��a� _pa @0x1� ; 定義 _pa 是一個 unsigned ��a� 型態的變數 � 它的位址在 RA� 的 0x1� ( 就是 A 埠 �po�t A). 所 以程式中如下的敘述 : _pa = 0 ; 則與組合語言的 CLR PA ; (PA=[1�H]) 有相同的功能。 4.2.3 定義文字符號及變數 在程式中使用文字符號能夠更容易的讀懂程式及修改 � 例如定義文字符號 _pa0 如下 : #define _pa0 _1�_0 表示 _pa0 是 RA� 位址 1�H 的位元 0 (bit 0)� 就是 A 埠的位元 0. 下列敘述 : _pa0 = 1 ; 表示將 A 埠的位元 0 設為 1� 與組合語言程式的 SET [1�H].0 有相同的功能。 前置處理指令 #define 是定義一個文字符號代表數值 � 或是文字串 � 或是巨集指令。 C 編譯器的前置處理器 (p�ep�o�esso�) 在編譯前 � 會先替換這些定義的文字符號。 前置處理指令 #undef 是將先前定義過的文字符號取消 � 變成無效 . 詳細的說明請參閱第 4.9 節。 微控制器中其他暫存器的變數或文字符號 � 皆定義於對應之微控制器的標頭檔案內 � 在設計程式 時可參考之 . 可以定義一些程式需要 � 但是在標頭檔案中沒有定義的變數或文字符號 � 方便程式 的開發及維護 � 例如 : #in�lude “�t48R�0A-1.�" #define scl _pa3 // SCL ( 時鐘線 ) 接到 �CU 的 A 埠的第 3 位元 #define scl_c _13_3 // A 埠之控制暫存器的第 3 位元 (bit 型變數 ) #define sda _pa1 // SDA ( 資料線 ) 接到 �CU 的 A 埠的第 1 位元 #define sda_c _13_1 // A 埠之控制暫存器的第 1 位元 void main(void) { } 定義四個文字符號 s�l� s�l_�� sda� sda_� 分別代表不同的輸出 / 輸入埠。 定義變數會佔用 RA�/RO� 的空間 � 如果又指定位址 � 則此變數佔用此位址 � 否則 Linke� 在做 連結時才會分派位址給變數 . 其效果有如組合語言的 _pa DB ? 定義文字符號的效果則與組合語言的 EQU 相同 � 例如 : #define s�l _pa3 與 s�l EQU _pa3 有相同的效果 4.2.4 設定微控制器及裝置的初始狀態 根據程式的功能 � 設定微控制器中各裝置的初始值 � 例如周邊設備的初始狀態 � 暫存器的型態等 . s�l_� = 0 ; // 設定 SCL (=PAC) 的狀態為輸出 sda_� = 0 ; // 設定 SDA (=PA) 的狀態為輸出 Rev. 1.30 12 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.2.5 設計子函式 獨立之功能可分別用子函式完成 , 在程式的偵錯 , 維護及重複使用上皆有好處 . 在設計時需要注 意函式的參數 , 返回值等 . 通常會將主函式 main() 放在程式檔的最後 , 各個子函式定義在前面 或其他的程式檔案內 . 下列範例只是部份 , 詳細的說明可參閱 4.6 節。 #in�lude “�t48��0a-1.�" // 引入標頭檔 #define s�l _pa3 // SCL ( 時鐘線 ) 接到 �CU 的 A 埠的第 3 位元 #define s�l_� _13_3 // A 埠 ( 位址 0x13) 之控制暫存器的第 3 位元 (bit 型變數 ) #define sda _pa1 // SDA ( 資料線 ) 接到 �CU 的 A 埠的第 1 位元 #define sda_� _13_1 // A 埠 ( 位址 0x13) 之控制暫存器的第 1 位元 // 函式 : Sta�tCondition() 子函式 // 功能 : 開始一個命令 // 輸入 : 無 // 輸出 : 無 void Sta�tCondition(void) { sda = 1 ; // SDA 輸出 �ig� s�l = 1 ; // SCL 拉 �ig� sda = 0 ; // SDA 輸出 low s�l = 0 ; // 完成 Sta�t of �ommand } // 函式 : StopCondition() // 功能 : 結束先前的命令 // 輸入 : 無 // 輸出 : 無 void StopCondition(void) { } // 函式 : main() // 功能 : 主函式 // 輸入 : 無 // 輸出 : 無 void main (void) { unsigned ��a� Rdata� type ; // 暫存器的初始設定 s�l_� = sda_� = 0 ; // 將 A 埠的位元 1� 3 設為輸出型態 (SCL� SDA 為輸出 ) Sta�tCondition() ; // 呼叫子函式 …… } Rev. 1.30 13 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.2.6 設計中斷服務函式 針對有硬體中斷的微控制器 � 需要設計中斷服務函式以處理中斷事件 � 定義周邊裝置的中斷服務 函式 (Interrupt Service Routine, ISR) 如下格式 : #p�agma ve�to� ISR_tm�0 @ 0x0� void ISR_tm�0(void) { ti�k++ ; } 中斷服務函式必須遵守下列規定 : → 返回的資料型態必須是 void → 不能有參數 → 必須設定中斷向量值 (inte��upt ve�to�)� 在函式名稱 ( 本例子是 ISR_tm�0 之後加上 @ 及中 斷向量值 ( 本例是 0x0�). 也可使用先前定義好的常數 � 例如 : #define VECTOR_T�R0 0x0� #pragma ve�to� ISR_tm�0 @ VECTOR_T�R0 void ISR_tm�0(void) { } 4.3 變數 (variable) 及資料型態 (data type) 程式執行過程中 � 可能會需要暫存一些資料 � 例如旗標 � 執行次數 � 延遲秒數等 � 因此就必須定 義變數以儲存這些資料 . 由於變數要佔用程式記憶體 (PRO�) 或資料記憶體 (RA�)� 因此在使 用變數之前 � 定義變數的資料型態 � 以便讓編譯器正確的編譯程式及配置記憶體空間 . 除了資料 型態之外 � 還可加入儲存類別 (sto�age �lass) 及修飾詞 (qualifie�)� 對變數做更詳細的安置。 4.3.1 變數名 變數名的規則 → 第一個字元必須是英文字母或底線符號 (unde�s�o�e)� 之後可緊接著字母或數字 → 變數名的前 3� 個字元有效 → 變數名內不可有 +� - � *� / � ……等符號字元 → 英文字母的大小寫是有區別的 (�ase-sensitive)� 例如 �ount 與 Count 是不同的變數名 範例 � numbe�� total_ti�k� _ti�k 是合法的變數名 � 而 �num� $dot� line\n 是非法的變數名 4.3.2 資料型態 Type ( 資料型態 ) bit ��a� signed ��a� unsigned ��a� s�o�t unsigned s�o�t int unsigned int long unsigned long void float double Size (bits) 1 8 8 8 16 16 16 16 3� 3� 0 3� 3� Arithmetic Type unsigned intege� signed intege� signed intege� unsigned intege� signed intege� unsigned intege� signed intege� unsigned intege� signed intege� unsigned intege� — �eal �eal 範圍大小 0� 1 -1�8 ~ +1�7 -1�8 ~ +1�7 0 ~ ��� -3�768 ~ +3�767 0 ~ 6��3� -3�768 ~ +3�767 0 ~ 6��3� -�147483648 ~ +�147483647 0 ~ 4�94967�9� — -3.40�8e+38~3.40�8e+38* -3.40�8e+38~3.40�8e+38* * float� double 皆使用 IEEE7�4 3� 位元的格式 Rev. 1.30 14 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.3.3 變數的有效範圍 (scope) 根據變數定義的所在 ¸ 決定此變數的有效範圍 . 可分為 → 區域變數 (lo�al va�iable) 定義在程式區塊內 (p�og�am blo�k� 例如函式 ) 的變數皆是區域變數 . 只有當此程式區塊被執 行時 � 區域變數才會有效 � 而在執行完畢並離開此程式區塊後 � 這些區域變數將無效 . 程式 區塊是指包含在左右大括號 `{` 及 `}' 之間的敘述行 定義在函式中的 stati� 變數則是全域變數 � 參考 4.3.� 說明 → 全域變數 (global va�iable) 定義在所有函式之外的變數為全域變數 . 當程式在執行時 � 此變數皆有效 � 任何函式都可以 存取或修改這個變數 範例 : #in�lude “�t48��0a-1.�" unsigned ��a� flag ; // 全域變數 void main(void) { ��a� type ; // 區域變數 � 只有在此函式被執行時才有效 stati� status = 0 ;// stati� 變數 � 只在第一次執行時設為 0 …… } 4.3.4 變數的資料型態 (data type) 當宣告變數時 � 必須指定它的資料型態 � 以告知編譯器此變數所需記憶體的大小 . 資料型態分為 整數型 (intege� type) 及浮點數型 (floating point type). 整數型又可區分為有正負號 (signed) 及 無正負號 (unsigned) ■ 整數型 (integer) → ��a� 佔用一個位元組 (byte) 的記憶體空間 . 如加上 signed 則表示有正負號 � 其大小範圍是 –1�8 到 1�7. 若加上 unsigned� 則表示沒有正負號 � 其大小範圍是 0 到 ���. 如果沒有 signed 或 unsigned� 則被視為 signed. 可用此型態定義字元 � 如 `A'� `d'� `$'� `3' 等 → s�o�t 佔用兩個位元組 (� bytes) 的記憶體空間 � 如加上 signed 則表示有正負號 � 其大小範圍是 –3�768 到 3�767. 若加上 unsigned� 則表示沒有正負號 � 其大小範圍是 0 到 6��3�. 如果沒 有 signed 或 unsigned� 則被視為 signed Holtek C 採用 little-endian 格式 � 就是變數的低位元組 (least signifi�ant byte) 存放在記憶體 的低位址 . 例如變數 �ount = 0x1�34 是存放於記憶體 40H 的位址 � 則低位元組的數值 0x34 存放於位址 40H� 高位元數值 0x1� 存放於位址 41H → int 與 s�o�t 型態相同 → long 佔用四個位元組 (4 bytes) 的記憶體空間 � 如加上 signed 則表示有正負號 � 其大小範圍是 –�147483648 到 �147483647. 若加上 unsigned� 則表示沒有正負號 � 其大小範圍是 0 到 4�94967�9�. 如果沒有 signed 或 unsigned� 則被視為 signed 在 little-endian 格式中 � 3� 位 元的變數 � 則是先存低字元 (least signifi�ant wo�d) 的低位元組 (least signifi�ant byte) 到記 憶體的低位址 � 再存放低字元的高位元組 (�ig� byte)� 再存放高字元 (�ig� wo�d) 的低位元組 � 最後才是高字元的高位元組 Rev. 1.30 15 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 浮點型 (floating point) Holtek C 支援 IEEE 7�4 3� 位元的格式 . 包括 float 及 double 兩個資料型態 � 浮點數值是以下 表的格式儲存在記憶體 : IEEE 7�4 3� bit sign x biased exponent xxxx xxxx mantissa xxx xxxx xxxx xxxx xxxx xxxx 與浮點數值的關係式為 : numbe� = (-1)sign x �(exponent – 1�7) x 1.mantissa 例如 � 浮點數為 �.77000e+37 在儲存到記憶體時換成 7DA6B69B 共佔 3� 位元。 3�bit 記憶體儲存值 sign 7DA6B69B� 0 Biased exponent 1111 1011b (=��1) 1.mantissa 1.0100110101101101001101b (=1.30�4476766�9) 十進位數值 �.77000e+37 4.3.5 bit 資料型態 類似整數型 � 但是只有 0 或 1 兩個值 � 所以只會取整數的最低位值 (LSB: least signifi�ant bit) 需要注意下列用法 : → bit 型態不可用於指標 (pointe�) 的數據型態,不可定義為 �onst → bit 型態可以設為函式的返回型態 � 它是存於累加器 (a��umulato�) 的相對位置 → 程式開始執行時 � 不會設定 bit 型變數的初始值 � 因此程式必須自行設定初始值 → bit 變數為局域時,佔用一個 byte(但只有一個 bit 有效),為全域時,佔用一個 bit → 以下是合法的使用方式 : stati� bit init_flag ; // 定義於函式內則被視為 區域變數 bit toggle_flag ; → 範例 : int data = 0x�4 ; bit flag ; flag = data ; 則 flag = 0 ( 取 data 的 LSB) 如果微控制器具有一個以上的 RA� bank� 例如 HT46R63� 在定義 bit 型態的變數時需要使用前 置處理指令 #p�agma �ambank0 指定在 RA� bank 0� 如下 : #p�agma �ambank0 bit flag ; #p�agma no�ambank Rev. 1.30 16 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.3.6 儲存類別 (storage class) 與修飾詞 (qualifier) 儲存類別 (storage class) auto� �egiste�� stati�� exte�n 修飾詞 (qualifier) �onst� �onstant �volatile� pe�sistent 指定詞 (specifier) typedef 變數於宣告或定義時必須指定其資料型態 � 但是儲存類別及修飾詞是可選擇的 � 可根據應用時的 需要去設定或不使用 . ■ 儲存類別 (storage class) 儲存類別與區域變數 (lo�al va�iable) 及全域變數 (global va�iable) 有關。 → 儲存類別 auto auto 是給區域變數使用的 � 沒有指定儲存類別的區域變數皆是 auto 寫與不寫 auto 都是相同效力 . 區域變數是存放在 RA� bank 0 的空間 → 儲存類別 �egiste� �egiste� 與 auto 類似 � 是給區域變數用的 � 當變數的存取很頻繁時 � 可將之設為 �egiste�� C 編譯器會使用暫存器而非資料記憶體空間來存放此變數 � 如此可增加存取的速度及減少編碼 . 目前並未實做此功能 → 儲存類別 stati� stati� 的變數會一直有效到整個程式結束後才失效 . 它的初始值只會在程式開始執行時被設 定一次 . 雖然 stati� 的變數在程式結束前皆有效 � 但是定義在函式內的 stati� 變數仍然是區 域變數 � 必須要在它所定義的函式中才可以讀寫 → 儲存類別 exte�n exte�n 通知 C 編譯器此變數是定義在其他的程式檔內 � 需要經由連結器 (Linke�) 連結定義此 變數的檔案後 � 才知道變數的所在 以目前在微控制器的應用程式上 , 比較需要使用 extern, 其他三種不具特別的優勢可用定義全 域變數 (global variabl) 的方式即可達到相同效果 . 建議不要使用 ■ 修飾詞 (qualifier) → 修飾詞 �onst C 編譯器會將 const 的變數放置於程式記憶體 (PROM). 在定義 const 變數時 , 必須要設定 其值 , 而程式在執行中不能修改此變數的值 const 變數目前還不能設置為 extern → 修飾詞 �onstant 這個修飾詞是 Holtek C 編譯器特別提供的 . 它會將 �onstant 的變數放置於程式記憶體 (PRO�) 的最後一頁 (last page). 定義 �onstant 變數時 � 必須要設定其值 � 而程式在執行中 不能修改此變數的值 . 使用此修飾詞要注意下列三點 : ● 只能使用在 int 或 unsigned int 的資料型態 ● 設定值必須配合微控制器程式記憶體的寬度 � 例如 � 若使用在 HT48R�0A-1 時 � 因為此 微控制器的寬度為 1� 個位元 � 最高位元是無效的 � 所以 0x9A 會被 C 編譯器改成 0x1A. 最高位元 � 位元 1� 被清除為 0 ● 所有設定此修飾詞的變數或陣列 � 總共佔用的字位元組 (wo�d) 不可超過 ��� 個 ● 若要將 int 或 unsigned int 變數設定為常量時,建議使用 �onstant 而不是 �onst,這樣會 使程式更高效 Rev. 1.30 17 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 指定詞 (specifier) → 指定詞 typedef typedef 是針對資料型態做新名稱的宣告 � 不是宣告資料型態的新變數 � 而是宣告一個新的名 字 . 例如將 UCHAR ( 新名字 ) 宣告為 unsigned ��a� 的資料型態 � 可使用 typedef unsigned ��a� UCHAR ; // UCHAR 為 unsigned ��a� 的新名字 UCHAR �ount ; // 變數 �ount 的資料型態為 unsigned ��a� // 等同於 unsigned ��a� �ount ; 使用 typedef 宣告資料型態的新名字可以讓程式的可讀性更高 � 更易了解 . 例如 � typedef unsigned int WORD ; // 使用 WORD 代表 unsigned int� 16 bits typedef unsigned long DWORD ; // DWORD 代表 3� bit 的 double wo�d 4.3.7 絕對變數 (absolute variable) 可以將全域變數或 stati� 變數指定一個固定的記憶體的位址 � 例如 unsigned ��a� Po�tA @ 0x1� ; 在變數名的後面再加上 `@' 及位址 C 編譯器在編譯時會將程式中出現絕對變數的程式改為此位址 � 但並未在記憶體中保留位置給 此變數 � 所以從連結器 (Linke�) 產出的對映檔 (map file) 中找不到此變數 C 編譯器會將之翻成 組合語言的 EQU 指令 � 如下 _Po�tA EQU 1�� 此種用法主要是對微控制器內的暫存器做定義 � 方便閱讀程式。 4.3.8 常數 (constant) 整數型常數 (integ�al �onstant) 可使用基底 (�adix) 格式表示之 . Radix ( 進位制 ) 常數格式 範例 二進制 0bnumber 或 0Bnumber 0b10111110 八進制 0numbe� 0�76 十進制 numbe� 190 十六進制 0xnumber 或 0Xnumber 0xBE 若常數的最後一字為 l 或 L� 則表示它使用 signed long 或 unsigned long 的型態 .S 字尾是 u 或 U� 則表示常數為 unsigned 型態。 浮點型 (floating point) 常數的型態是 double� 若它的字尾是 f 或 F� 則是 float. 字元常數 (��a�a�te� �onstant) 必須以單引號 ` ' 框住 � 例如 `a' 字串常數 (st�ing �onstant) 必須以雙引號 “ " 框起 � 例如 “Hello!". 字串常數的定義會影響 它所儲存的記憶體位址 � 如下 ��a� *�p = “one" ; // C 編譯器會發出錯誤 ��a� *�onst spt� = “Hello" ;// “Hello" 儲存在程式記憶體 (PRO�) 常數型的變數或陣列 (a��ay) 必須要設定其值 � 否則 C 編譯器會發出錯誤訊息 � 如上例 Rev. 1.30 18 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.3.9 指標 (pointer) 與陣列 (array) 指標本身是一個變數 � 它的內容是另一個變數存放的位址 � 類似組合語言的間接定址 . 在使用上 � 指標必須要指到一個已定義 ( 存放於記憶體 ) 的變數 � 否則在程式執行時會發生錯誤 . 指標的宣 告格式 資料型態 * 指標名 [� * 指標名 �….] ; 資料型態是這個指標所指的變數的資料型態 � 例如 ��a�. 指標名類似變數名 � 可以在一行中 � 宣 告指向相同資料型態的不同指標名 � 例如 ��a� *tpt�� *a��ay_pt� ; int *line_pt� ; 以上只是宣告指標變數 � 必須要將這些指標指向已定義的變數才能使用 目前尚未支援字符串數組,例: ��a� *�ainbow[] = { "�ed"� "o�ange"� "yellow" };// 不支援 ■ 指標的運算子 & 與 * 運算子 & 如果緊鄰在變數的前面 � 例如 &line� 則是代表取得此變數的記憶體位址 � 例如 � int line ; // 定義變數 int *line_pt� ; // 宣告指標 line = 1� ; line_pt� = &line ; 若變數 line 被安置於 RA� 的位址 64� 則指標 line_pt� 等於 64 運算子 * 緊鄰指標變數之前是表示取得這個指標指到的變數的內容 � 承上例 � int total ; // 定義變數 total = *line_pt� ; // 取得指標 line_pt� 所指到的變數 line 的內容 則變數 total 等於 1� 對於函式指針,目前 en�an�ed Holtek C 只支援全域的 �onst 函式指針,且所指函式不能帶有 參數,(�oltek � 不支援函式指針 ) 例: fun() { �etu�n 1; } int (*�onst p)() =fun; // 全域且要初始化 void main() { int a; a=(*p)(); } 指標的大小會根據微控制器具有之記憶體空間大小而定 � 如果微控制器只有一個以上的 RA� bank� 則指標本身佔一個字節,如果是多個 RA� bank,則佔兩個字節, 如果變數有 �onst 或 �onstant 修飾詞 � 則是指向程式記憶體 (PRO�)� 而且此變數的內容不能被修改 . 如果所指向的 變數不具有 �onst 或 �onstant� 則指標會指向資料記憶體 (datamemo�y� RA�) 內的變數 . 目前, Holtek C 並不能取得 �onst 常量的地址,例: �onst int ld� = 0; void main(){ int *a; a = &ld�; // 不能通過 } Rev. 1.30 19 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 陣列 (array) 陣列是由相同資料型態的元素組成的 , 例如 ��a� a��ay_name[3�] ; 是由 32 個 char 型態的元素組成的 , 這些元素的名字是以陣列名 array_name 為準 , 而以索引 (index) 區分各個元素 , 例如 array_name[3] 是第 4 個元素 . 陣列的索引是正整數 , 從 0 開始直 到元素的總個數減一 , 上例中 , 最後一個元素是 array_name[31]. 這種資料型態在建表格 (table) 時非常有用。 陣列也可當做指標的一種 , 只是使用時格式不同 , 下列範例說明指標與陣列之間的使用方法 ��a� *npt�� *fpt� ; // 宣告指標 ��a� ��� tbl ; // 定義變數 ��a� table[�] = { `a'� `b'� `�'� `d'� `e' } ; // 定義陣列 npt� = table ; // 指標 npt� 指到陣列 table 的第一個元素 table[0] �� = *npt� ; // 將字元 `a' 存入變數 �� tbl = table[0] ; // 將字元`a'存入變數 tbl � 所以變數 �� 與變數 tbl 的內容相同 fpt� = &table[4] ; // 指標 fpt� 指到陣列 table 的第 � 個元素 � table[4] �� = *fpt� ; // 將字元 `e' ( 第 � 個元素 ) 存入變數 �� 內 tbl = *(npt�+4) ; // npt� 指到陣列 table 的第一個元素 � 加 4� 所以 tbl =`e' 注:陣列 size 的最大值視 �CU 的 RA�/RO� 空間而定,所有 �onst a��ay 的長度不超過一個 �ombank(�000�), 所有 �onstant a��ay 的長度不超過一個 �ompage(��6�),一般的 a��ay 則不 超過 ��6 Bytes,目前不支持三維及以上的陣列。 4.3.10 結構 (struct) 與等位 (union) 結構是一個或多個成員的集合 � 每個成員的資料型態可以不同 � 並且使用結構的名字去讀寫這些 成員 . 結構的宣告是等同於定義一個新的資料型態 � 當變數宣告為一個結構的型態時 � 便可用結 構的名字去讀寫各成員的內容。 成員的資料型態不可以使用 bit 型態 � 但是可以用 bit-field 方式宣告成員的型態 � 例如 st�u�t st�_name { unsigned flag : 1 ; // 此成員放於最低位元 least signifi�ant bit unsigned no_used : 7 ; unsigned sta�k : � ; // 此成員置於最高位元 �ig� bits } usage ; 每個 bit field 會置放於 8 位元的單位內 � 不會橫跨兩個 8 位元的單位 � 且不能定義超過 9 位元的 bit field 等位 (union) 的格式與結構相同 � 唯一不同的是記憶體空間的分配方式 . 等位是安排共 用的空間來存放不同資料型態的成員 . 每個成員必須要宣告其資料型態 � 而等位的大小則是所有 成員中佔用最大位元組 (bytes) 的型態大小 . 每個成員的開始 位址皆相同 � 就是等位的位址。 union union_name { ��a� num_byte ; // 佔用 1 個位元組 int num_int ; // 佔用 � 個位元組 long num_long ; // 佔用 4 個位元組 } numbe� ; 等位 numbe� 的大小為 4 個位元組 結構與等位的型態也可使用修飾詞 (qualifie�)� 例如 �onst� 其效果與一般的資料型態相同。 Rev. 1.30 20 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 結構的運算子 -> 與 . 指標所指變數的資料型態若是結構或等位 � 可使用右箭頭運算子“->"去讀寫其成員 . 也可以使 用此變數名以及句點 (dot .) 去讀寫成員變數。 範例 : st�u�t tag { ��a� flag ; int numbe� ; unsigned a� : 1 ; unsigned z : 1 ; }; st�u�t tag status ; // 定義變數 status 是結構 tag 的資料型態 st�u�t tag *spt� ; // 宣告 指標 spt� 為結構 tag 的資料型態 bit va�� pa� ; // 定義變數 va�� pa� 是 bit 的資料型態 status.flag = 1 ; // 寫 1 到 結構的成員 flag status.a� = 1 ; // 寫 1 到 結構成員 a� va� = status.a� ; // va� = 1 spt� = &status ; // 指標 spt� 指到結構 status pa� = spt�->a� ; // pa� = 1 4.4 運算子 (Operators) 運算子是針對變數所存的數據加以運算的符號 , 運算式 (expression) 是運算子 , 數據 , 變數 , 函 式及運算式的組合表示式 . 有下列的運算子 → 數學運算子 (a�it�meti� ope�ato�s) +� -� *� /� % 分別為加法 � 減法 � 乘法 � 除法 � 餘數運算子 → 比較運算子 (�ompa�ison ope�ato�s) 將兩個運算元做比較 � 回傳比較後的結果為真 ( 非零 ) 或假 ( 零 ) > 大於 >= 大於或等於 < 小於 <= 小於或等於 == 等於 != 不等於 → 邏輯運算子 (logi�al ope�ato�s) 取運算子的真假值做邏輯運算 � 傳回結果是真 ( 非零 ) 或假 ( 零 ) && 邏輯 AND | | 邏輯 OR ! 邏輯 NOT → 位元邏輯運算子 (bitwise logi�al ope�ato�s) 位元的邏輯運算 & 位元 AND | 位元 OR ^ 位元 XOR ~ 位元反相 (�omplement) >> 向右移位元 << 向左移位元 → 算式設定運算元 (assignment ope�ato�s) <va�> += <exp�> ; 變數加上 exp� 的值 � 將結果存入變數 <va�> -= <exp�> ; 變數減去 exp� 的值 � 將結果存入變數 <va�> *= <exp�> ; 變數乘以 exp� 的值 � 將結果存入變數 <va�> /= <exp�> ; 變數除以 exp� 的值 � 將商數存入變數 <va�> %= <exp�> ; 變數除以 exp� 的值 � 將餘數存入變數 <va�> &= <exp�> ; 變數與 exp� 的值做位元 AND� 將結果存入變數 Rev. 1.30 21 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 <va�> |= <exp�> ; 變數與 exp� 的值做位元 OR� 將結果存入變數 <va�> ^= <exp�> ; 變數與 exp� 的值做位元 XOR� 將結果存入變數 <va�> >>= <exp�> ; 變數向右移 exp� 個位元 � 將結果存入變數 <va�> <<= <exp�> ; 變數向左移 exp� 個位元 � 將結果存入變數 → 遞增 (in��ement) 與遞減 (de��ement) 運算子 ++<va�> 變數先加 1� 再做運算 <va�>++ 先做運算 � 變數再加 1 --<va�> 變數先減 1� 再做運算 <va�>-- 先做運算 � 變數再減 1 → 條件運算子 (�onditional ope�ato�). <exp�> ? <statement1> : <statement�> ; 若 <exp�> 運算的結果為真 � 則執行 <statement1>� 否則就執行 <statement�> → 運算子的優先性 (p�io�ity) 與結合性 (asso�iativity) 一個運算式中包含多個運算子時 � 必須注意運算子執行的優先順序 (p�io�ity). 避免運算結果 與預期的不合 � 下表為運算子的優先順序及結合性 � 前面欄位內的運算子 � 具有較高的優先 權 . 同一欄位中的運算子則由結合性決定優先 運算子 結合性 陣列元素 函式或運算式中的括號 結構成員或等式成員的間接存取 數據所佔的位元組大小 (byte) 由左到右 ++ -~ ! + & * 增1 減1 取 1 補數 (one's �omplement) 否定 負號 (una�y minus sign) 正號 (una�y plus sign) 取變數的位址 存取指標所指位址的內容 由右到左 * / % 乘法 除法 餘數 由左到右 + - 加法 減法 由左到右 << >> < <= > >= 左移 右移 小於 小於或等於 大於 大於或等於 由左到右 == != & ^ | && || ?: 相等 不相等 位元 AND 位元 XOR 位元 OR 邏輯 AND 邏輯 OR 條件運算 由左到右 = *= /= %= += -= <<= >>= &= |= ^= 設定 相乘再存入變數 相除再將商數存入變數 取餘數再存入變數 相加再存入變數 相減再存入變數 左移再存入變數 右移再存入變數 位元 AND 後 � 再存入變數 位元 OR 後 � 再存入變數 位元 XOR 後 � 再存入變數 � Rev. 1.30 說明 [] () -> sizeof 分隔變數或算式 22 由右到左 由左到右 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 運算前的型態轉換 當運算子要處理不只一個運算元 (ope�and) 時 � 這些運算元必須是相同的資料型態 �C 編譯器會 自動將運算元的型態轉換成一致 � 為避免因轉換而造成資料的遺漏 � 將以轉成較大的型態為原 則 . 有些情況 � 即使運算元的型態是一樣 � 但是在執行運算前也會先做型態的轉換 � C 編譯器會 根據運算的狀況自動做轉換 . 如果希望運算能照所要的方式運作 � 可以對運算元的資料型態做強 制性的轉換 (type �ast). 範例 : ��a� �ount� a = 0 � b = �0 ; if( a – b < 10 ) �ount++ ; C 編譯器做減法及比較 � 結果是 –�0� 因此 �ount++ 會被執行 . 若改成 if( (unsigned int)(a – b) < 10 ) �ount++ ; 則 因 為 先 轉 成 unsigned int� 運 算 結 果 是 �06� 所 以 �ount++ 不 會 被 執 行 位 移 運 算 子 (s�ift ope�ato�s) 會根據運算元的型態做不同的處理 . 右移運算子 (�ig�t s�ift ope�ato�� >>) 對 signed ��a�/int/s�o�t/long 型態的運算會將原先的最高位元 (most signifi�ant bit� �SB) 複製到右移後的 最高位元 � 例如 signed int 0x01�4 在右移一個位元後 � 變成 0x009� (�SB=0) signed int 0x80�4 在右移一個位元後 � 變成 0xC01� (�SB=1) 右移運算元對 unsigned ��a�/int/s�o�t/long 型態的運算 � 會將 �SB 填 0 左移運算元對 unsigned/signed 型態的運算 � 皆會將最低位元 (least signifi�ant bit) 清為零 4.5 程式流程控制 (program flow control) 在熟悉程式架構 � 變數及其資料型態 � 運算式及運算子之後 � 根據功能需求去設計最恰當的程式 流程 � 使用最適合的流程控制敘述式 . 下列各敘述的語法中 � 粗字是保留字 � 使用到此敘述時必 須用這個字 . 不可使用這些保留字當做變數名 . 語法內的中括號代表被圍住的部分是可選擇的 � 可有可無 � 視需要決定 . 4.5.1 if-else 敘述 語法 : if ( �ond-exp�ession ) statement1 ; [ else statement� ; ] 說明 : 這是一個條件判斷的控制敘述 . 在不同的條件下會需要不同的處理時 , 可以使用這種敘述做 不同的流程控制 . 如果條件 cond-expression 的結果為真 ( 不等於零 ) 則 statement1 會被 執行 . 否則 , 若有 else 部分 , 則會執行 else 部分的敘述 statement2. 中括號代表此部分可 有可無 , 完全視程式有無需要 . statement1 及 statement� 可以不只一個敘述 � 如果超過一個敘述時 � 必須要以左右大括號 包含這些敘述 . 範例 : if( se�onds > �9 ) { minutes++ ; se�onds = 0 ; } else se�onds++ ; Rev. 1.30 23 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.5.2 switch 敘述 語法 : switch( exp�ession ) { case �onstant1 : statement1 ; break ; case �onstant� : statement� ; break ; …… [ default : statementX ; break ; ] } 說明 : if-else 是針對只有兩種結果的運算式 (exp�ession) 導出對應的流程 � 如果運算式的 結果超過兩個值 � 使用 swit��-�ase 敘述會讓程式增加可讀性 � 更容易了解程式的功能 . 如 果 exp�ession 的 結 果 是 �onstant1� 則 statement1 會 被 執 行 . 若 exp�ession 的 結 果 等 於 �onstant�� 則 statement� 會被執行 . 如果沒有任何的 �ase 與 exp�ession 的結果相等 � 則 default 部分的 statementX 會被執行 . default 部分可有可無 . 每個 �ase ( 或 default) 中可以有多個敘述 � 例如 �ase �onstant� : statement�0 ; statement�1 ; …… b�eak ; exp�ession 可以是一個變數也可以是一個能計算出數值的運算式 . 不要忘記在每個 �ase 的最後一個敘述後 � 加上 b�eak ; 敘述行 � 否則一直到下一個 b�eak 敘 述之前或 swit�� 右括號之前的所有敘述皆會被執行 � 除非程式確實要如此執行 . 下例中 � 如果 �ase `1' 中沒有 b�eak ; 則當 input_key 等於 1 時 � �olo�=RED 被執行 � 因為沒有 b�eak� 所 以下一個敘述 �olo� = GREEN 也被執行 � 結果就不是程式所要的 範例 : swit��( input_key ) { �ase 1 : �olo� = RED ; b�eak ; �ase � : �olo� = GREEN ; b�eak ; …… default : �olo� = WHITE ; b�eak ; } Rev. 1.30 24 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.5.3 for 敘述 語法 : for (initial-exp�ession ; �ond-exp�ession ; update-exp�ession) statement1 ; 說明 : fo� 敘述是重複執行相同的動作 ( 敘述 ). 做條件迴圈的功能 . 首先會執行 initial-exp�ession� 通常是初始設定一些變數 � 這個運算式只會執行一次 . 如果沒 有此運算式 � 則會直接做 �ond-exp�ession 的比較 . �ond-exp�ession 是條件判斷式 � 與 if-else 的 �ond-exp�ession 相同 . 當條件成立時 � 則會 去執行 statement1. 否則會離開此迴圈 � 跳到 statement1 之後的敘述去執行 . 在條件成立並 執行 statement1 之後 � 接著會去執行 update-exp�ession 的部分 � 再回到 �ond-exp�ession 做比較 . 重複此動作 � 直到條件不成立 � 就跳出此迴圈 � 回到下一個敘述 . 如果 updateexp�ession 不存在 � 則會直接對 �ond-exp�ession 做比較 . statement1 可以是一個以上的敘述 � 但必須要以左右大括號包含這些敘述 . 範例 : 要將陣列變數的每個元素清為零 � int idx ; ��a� �buf[�0] ; fo�( idx = 0 ; idx < �0 ; idx++ ) �buf[idx] = 0 ; 4.5.4 while 敘述 語法 : while ( �ond-exp�ession ) statement1 ; 說明 : 與敘述 fo� 有類似的條件迴圈功能 . 但是沒有 initial-exp�ession 與 update-exp�ession. 只 要 �ond-exp�ession 條件成立就執行 statement1 直到條件不成立 � 則跳出迴圈 . statement1 可 以不只一個敘述 � 但要用左右大括號包含之 . 範例 : int idx ; idx = 0 ; w�ile( idx < �0 ) { �buf[idx] = 0 ; idx++ ; } 4.5.5 do-while 敘述 語法 : do { statement1 ; } while ( �ond-exp�ession ) ; 說明 : do-w�ile 會先執行 statement1� 之後再做 �ond-exp�ession 的比較 � 若條件成立 � 就 再執行 statement1� 一直到條件不成立 � 就跳出迴圈 . 這個敘述與 w�ile 敘述相似 � 不同點是 statement1 至少會執行一次 . statement1 可以不只一個敘述 範例 : idx = 0 ; do { �buf[idx] = 0 ; idx++ ; } w�ile( idx < �0 ) ; Rev. 1.30 25 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.5.6 goto 敘述 語法 : goto label ; 說明 : goto 是一種強制性的流程控制敘述 . 可以直接跳出迴圈將程式移轉到標名 label 的敘述或 是從 swit��-�ase 中移轉到 label 處 . 標名 label 必須和 goto 敘述在同一個函式內 � 也就是不允 許跳到其他的函式裡 . 範例 : 參看 4.�.7 範例 4.5.7 break 與 continue 敘述 語法 : b�eak ; �ontinue ; 說明 : b�eak 是從迴圈或是 swit�� �ase 跳出 � 一次只能跳出一層 �ontinue 是跳過 �ontinue 敘 述之後的其他敘述 � 再從下一迴圈繼續執行 範例 : idx = 0 ; w�ile( idx < �0 ) { if( �buf[idx] == 0 ) // �buf[] 沒有字元 b�eak ; // 跳出 w�ile 的迴圈 idx++ ; } fo�( idx =0 ; idx < �0 ; idx++ ) { if( �buf[idx] != 0 ) �ontinue ; // �buf[] 仍有字元 � 回到 idx++ 繼續 goto �ount_l ; // 沒有字元 � 計算字元總數 } …… // 其他敘述 �ount_l : // 標號 // idx = 字元總數 4.6 函式 (Functions) 函式是敘述的集合 . 所有可被執行的敘述必須定義於函式中 (main() 是主函式 ). 使用函式前 � 必須要宣告及定義函式 � 否則編譯器會發出錯誤訊息 . 除了內容的敘述 � 函式最重要的是參數 (a�guments) 及返回值 (�etu�n values). 它的格式如下 return-type fun�tion_name(var-type a�g1� var-type a�g�� …) { statements ; } 其中 � return-type 是函式的返回值的型態 � 可使用 4.� 節中的資料型態 . fun�tion_name 是函 式名 � 可由字元 (��a�a�te�)� 數字 (0~9)� 底線 (unde�s�o�e) 所組成 . 採用 �ase-sensitive. 大寫 字元與小寫字元是不同的 . var_type 是參數的資料型態 . a�g1� a�g�� .. 是參數名稱 � 採用 �asesensitive. 大寫字元與小寫字元是不同的 . 4.6.1 參數 (arguments) 根據參數的資料型態 (data type)� 是以下列的存放方式傳入被呼叫的函式 . → 所有參數皆存放於內部變數中 � 變數的名字是以函式名為準 � 再加上數字 � 從 0 開始 � 例 如函式名字為 multiple� 則第一個參數會存入變數 multiple0� 第二個參數則存放於變數 multiple1� 以此類推 . 如果參數型態為 int 或 unsigned int� 則使用兩個位元組儲存 � 例如第二 個參數是 int 型態 � 則變數 multiple1 儲存參數的低位元組 � 變數 multiple1+1 的位置儲存參 數的高位元組 Rev. 1.30 26 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.6.2 返回值 (return values) 從組合語言的角度看 → 返回值的大小為一個位元組 (byte)� 則此值會放入累加器 (a��umulato�) 傳回 呼叫函式的敘述行 → 返回值是兩個位元組 (int� s�o�t)� 則返回值的低位元組 (low byte) 存放在累加器 � 高位元組 (�ig� byte) 則放在變數 RH. → 返回值是四個位元組 (long� float)� 則返回值的低字低位元組 (low byte of low wo�d) 存放在累 加器 � 返回值的低字高位元組 (�ig� byte of low wo�d) 放在變數 RH. 返回值的高字低位元組 (low byte of �ig� wo�d) 放在變數 R�. 返回值的高字高位元組 (�ig� byte of �ig� wo�d) 放在 變數 RU. 為了讓程式更模組化及可讀性更高 , 將程式的各功能分別以獨立之函式實作之 , 會是最佳的 方式 . 主函式負責管理及呼叫各功能的函式 , 這些函式可以不要與主函式放在同一個程式檔 . 4.7 中斷服務函式 (Interrupt Service Routines) 微控制器的硬體中斷可以用 C 語言撰寫它的中斷服務函式 (Inte��upt se�vi�e �outine� ISR). 必須 遵照下列的規則來定義中斷服務函式 : → 函式的返回值型態必須是 void → 函式不可有參數 (pa�amete�) → 使用 #p�agma ve�to� 設定中斷服務函式的中斷向量 (inte��upt ve�to�) 使用 @ 指定中斷向量 → 最好不要從程式的其他地方呼叫中斷服務函式 → 針對不具有中斷可重疊 (nested) 發生的微控制器 � 則在中斷服務函式內不可開啟中斷功能 範例 : #pragma vector ISR_Time� @ 0x08 void ISR_Time�(void) { } // 定義 : 返回值的型態 � 沒有參數 � 設定中斷向量為 0x08 C 編譯器會根據中斷服務函式對暫存器的需要 � 在進入中斷服務函式後 � 將這些暫存器的內容 儲存 . 等執行完中斷服務函式的工作後 � 再恢復先前所儲存的暫存器內容 . 最後回到被中斷的地 方繼續執行 � 同時中斷功能也打開 � 允許中斷產生 . 在程式的其他地方若需要將中斷功能開啟 (enable)� 可自行設定相關的暫存器 . 當中斷服務函式正在處理此中斷時 � 另一個更優先的中斷發生並需要盡快處理 � 則微控制器必須 暫停目前的中斷服務函式 � 轉而處理新發生並且是更優先的中斷事件 . 亦即微控制器具有中斷 可重疊 (inte��upt nested) 的特性 . 在編譯中斷服務函式時 � Holtek C �ompile� 會將一些常用的 暫存器的內容保存於資料記憶空間 (RA� spa�e)� 當中斷服務函式完成其工作後 � 在返回原先被 中斷處之前 � 再將這些內容還原到暫存器 . 由於要支援可重疊的中斷事件 � HoltekC �ompile� 對 於每一個中斷向量保留一組資料記憶空間 � 例如中斷向量一 (INT 1) 的中斷服務函式會將暫存器 的內容保存於 V1xx 的記憶空間 � 中斷向量二 (INT �) 的中斷服務函式會保存在 V�xx 記憶空間 � 其他的中斷以此類推 . 中斷服務函式會保存的暫存器內容包括 累加暫存器 (a��umulato�� ACC)� 狀態暫存器 (status� S)� 記憶體區塊指標 (bank pointe�� BP)� 間接位址指標 (�P0� IAR0) 以及在算術運算中使用的中間 變數 ( 以 T 開頭的 � 例如 T�� T3�…). 這些暫存器或中間變數將會保存於對應的變數 � V1A� V1S� V1BP� V1�P0� V1�P1� V1T�� V1T3�…( 中斷向量一 )� V�A�V�S�V�BP�V��P0�V��P1�V�T��V �T3�… ( 中斷向量二 )� 其他的累推 . 如果微控制器不具有 BP� �P1 的則不需要保存這些暫存器 的內容 . 雖然不同的中斷事件可以重疊發生 � 但是同一個中斷事件並不可以重疊產生 � 必須等候 前一個發生被處理完成後 � 才能認可下一個中斷事件 . 可以在中斷服務函式中將同一個中斷除能 (disable inte��upt) 如果微控制器中不同類的中斷事件不可重疊發生 � 則 Holtek C �ompile� 可以只使用一組記憶體 空間保存這些暫存器的內容 � 但是必須在定義中斷服務函式的程式中指明之 . ( 使用 #p�agma nove�to�nest) 而在中斷服務函式中要將中斷功能停止 (disable inte��upt� _emi=0) Rev. 1.30 27 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.8 在 C 語言程式中嵌入組合語言 (in-line assembly code) 如果想要讓編譯後的程式碼更為精簡 � 執行上更有效 � 可以在 C 程式中加入組合語言的指令 . 語法 : 說明 : #asm [label:] op�ode [ope�ands] ... #endasm #asm 和 #endasm 是內嵌式組合語言的前置處理程式虛擬指令。C 編譯器會將 #asm 之後的 ( 或夾在 #asm 和 #endasm 之間的 ) 組合語言指令直接寫進輸出的檔,有如直 接使用組合語言撰寫程式。 注意 : ■ 內嵌彙編只能出現在函式內 ■ 內嵌彙編只能撰寫指令 � 不能用來定義變數 �se�tion 等 . ■ 內嵌彙編中的變數 � 必須使用相應的彙編名 � 而變數與彙編名字之間的關係如下 : ● 總體變數 :va�→_va�( 超過一個位元組 : _va��_ va�[1]�_ va�[�]�_ va�[3]) 例:��a� a;long b; 對應的 AS� 名:a→_a� b→_b�_b[1]�_b[�]�_b[3] ● 局部變數:CR1�CR��CR3…( 超過一個位元組 : CR1� CR1[1]� CR1[�]� CR1[3] )� 其中 � 數位是按編譯順序來排列的 � 所以 � 如果改變了定義的順序 � 有可能彙編名 就跟著改變了 � 而且局部變數要有使用過才會有相應的彙編名 � 比如 � 上例中的 � 如果沒有先賦值 �=0; 則會找不到 CR4 而報錯 . 例:void fun() { ��a� a;long b; a = 0;b=0; // 對應的 AS� 名:a→CR1� b→CR��CR�[1]�CR�[�]�CR�[3] } ● 函數名 :name(a�b)→_name( 函數參數名 :name0�name1�name�…) 例:void fun(��a� a���a� b) {} 對應的 AS� 名:fun→_fun� a→fun0� b→fun1 ● stati� 變數不要出現在內嵌彙編內 � 因為它的彙編名是隨機的 . ● 如果不知道變數所對應的彙編名 � 可以看產出的 asm 檔中的 #p�agma debug va�iable 8 � CR1 d1 其中 d1 為變數 �CR1 為 d1 所對應的彙編名 � 當然 � 要先編譯通過才會產出 asm 檔 . 範例 : ��a� a; int b; void fun(��a� p1�int p�) { a = p1; #asm // b = p�; �OV A�fun1 �OV _b�A �OV A�fun1[1] mov _b[1]�A #endasm } void main() { int d1; Rev. 1.30 28 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ��a� d�; unsigned ��a� q� �; � = 0; q = d1 / d�; // get quotient #asm // � = q; �OV A� CR3 ; CR3 q �OV CR4�A ; CR4 � #endasm #asm // fun(d��d1) �OV A�CR� �OV fun0�A ; p1 = d� �OV A�CR1 �OV fun1�A �OV A�CR1[1] �OV fun1[1]�A ; p� = d1 CALL _fun #endasm } 4.9 前置處理指令 (Preprocessor) 前置處理指令是以 # 為字首的文字串 � 程式中所有的前置處理指令會最先被前置處理器 (p�ep�o�esso�) 處理 . 這些指令提供的功能有定義文字符號 (define)� 巨集指令 (ma��o)� 引入檔 案 (in�lude file)� 條件式編譯 (�onditional �ompile�) 與特殊選項 (p�agma) 4.9.1 定義文字符號 (#define) → #define 語法 : #define sym_name �epla�ed_text 說明 : 定義 sym_name 為 �epla�ed_text. 而 �epla�ed_text 可以是數值 � 運算式或文字串 . 前 置處理器會將程式中所有含 sym_name 的敘述 � 更換成 �epla�ed_text. 範例 : #define TOTAL_COUNT 40 #define PA0 _1�_0 → #undef 語法 : #undef sym_name 說明 : 取消先前定義的文字符號或前置處理巨集 範例 : #undef PA0 // 取消先前定義的 PA0 4.9.2 引入檔案 (#include) → #in�lude 語法 : #in�lude “file_name"或 #in�lude <file_name> 說明 : 將指定檔案的內容嵌入所在的程式處 . 當 file_name 以雙引號 “ 包含時 � 編譯器先到工作目錄中找尋此檔 � 若找不到 � 則到 目前的目錄找尋 � 若找不到就發出錯誤訊息 當 file_name 是以角號 < > 包含時 � 則只到環境參數設定的目錄中去找尋 4.9.3 內嵌組合語言 (inline assembly) → #asm #endasm 語法 : #asm Rev. 1.30 29 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 組合語言的指令 � 例如 �OV A� 1 #endasm 說明 : 在 #asm 與 #endasm 之間嵌入組合語言指令 . 範例 : #asm AND A� 0F� SUB A� 09� #endasm 4.9.4 條件式編譯 (#if/#endif) → #if #else #endif 語法 : #if exp�ession statements1 ; [#else statements� ; ] #endif 說明 : 控制編譯器體有條件地編譯程式 . 當 exp�ession 為真時 � statements1 的程式會被編 譯 � 否則 statements1 會被忽略 � 若有 #else 部分 � 則會編譯 statements�. #else 部分可有可無 � 視需要 範例 : #if �ODE > 0 #define DISP_�ODE �ODE #else #define DISP_�ODE 7 #endif → #ifdef 語法 : #ifdef symbol statements1 ; [ #else statements� ; ] #endif 說明 : 如果 symbol 在前面已被定義 ( 用 #define ), 則 statements1 被編譯 , 否則會編譯 statements2. #else 部分可有可無 → #ifndef 語法 : #ifndef symbol statements1 ; [ #else statements� ; ] #endif 說明 : 與 #ifdef 相反 , 如果 symbol 在前面未被定義 , 則 statements1 被編譯 , 否則會編譯 statements2. #else 部分可有可無 → #elif 語法 : #if exp�ession1 statements1 ; #elif exp�ession� statements� ; #endif 說明 : 如果 exp�ession 為真 � 則 statements1 被編譯 � 否則若 exp�ession� 為真 � 則會編譯 statements�. #elif 是 #else #if 的縮寫 Rev. 1.30 30 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 前置處理指令 功能 範例 #asm 指示在此行之後是“內嵌組合語言的指令” (in-line assembly) #asm add a� 1 #endasm #define 定義符號或前置處理的巨集指令 #define COUNT �0 #define OK #define add(a�b) ((a)+(b)) #elif 是 #else #if 的縮寫 參看 #ifdef #else 定義“條件不成立的"原始程式行 參看 #if #endasm 指示“內嵌組合語言指令"的結束 參看 #asm #endif 結束“條件型的原始程式行"的嵌入 參看 #if #e��o� 產出錯誤訊息 #e��o� Size too big #if 定義“條件成立的”原始程式行 如果條件成立 , 則將其後的原始程式行嵌入到程式中 #if ti�k < 10 ti�k++ ; #else ti�k = 0 ; #endif #ifdef #ifdef COUNT delay() ; 如果前置處理符號已定義 , 則將其後的原始程式行嵌入 #else not�ing() ; #endif #ifndef #ifndef COUNT 如果前置處理符號未定義 , 則將其後的原始程式行嵌入 not�ing() ; #endif #in�lude 將標頭檔的內容嵌入 #in�lude “�t46R�3.�" #p�agma 編譯器的特殊選項 參看本節說明 #undef 取消先前定義的符號或前置處理巨集 #undef COUNT 4.9.5 編譯器的特殊選項 pragma 格式 #p�agma keywo�d [ options ] 某些 keywo�d 會有 options� 表格列出 p�agma 的 keywo�ds keyword options 功能說明 bp_f�ee — 函式中不做 BP 暫存器的改變 ② bp_nof�ee — 取消函式中不做 BP 暫存器改變的功能 ② fun�tion V 設定函式被安置的 ROM 位址 nobp — 中斷服務函式中不儲存 BP 暫存器的內容 ② nolo�al V 設定函式的參數及內部變數為一般型 , 非 LOCAL ② nomp0 — 中斷服務函式中不儲存 MP0 暫存器的內容② nomp1 — 中斷服務函式中不儲存 MP1 暫存器的內容② �ambank0 no�ambank — 設定變數安置於 RAM bank 0 的功能 , 例如 , bit 型態 ③ 取消變數安置於 RAM bank 0 的功能 ③ �ombank0 no�ombank — 設定函式安置於 ROM bank 0 的功能 ① 取消函式安置於 ROM bank 0 的功能 ① �ombank V 指定函式安置於那個 ROM bank ① ve�to� V 定義中斷服務函式 nove�to�nest — 中斷服務函式的暫存器保存空間只有一組 , 中斷事件不可重疊 (no nested) ① 凡具有多個 RO� bank 記憶體的微控制器 ( 例如 HT46RU67) 此功能才有效 ② 凡具備多個 RA� bank 及只有一個 RO� bank 記憶體的微控制器 ( 例如 HT8��99AE ) 此功 能才有效 ③ 凡具備多個 RA� bank 記憶體的微控制器 ( 例如 HT46R63) 此功能才有效 Rev. 1.30 31 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 下列為各 keywo�d 的功能 → #pragma bp_free #pragma bp_nofree 對於這兩個前置處理指令所包含的敘述式 (statements)� 編譯器會將產出之指令碼中所有會 改變 BP 暫存器的指令刪除 � 也就是 �OV BP� A 指令不會產出 . 主要是縮減編譯產出的指令 碼大小 . 前題是必須確定變數所在的記憶體皆是固定的 RA�bank. 從 #p�agma bp_f�ee 之後 的敘述開始 � 一直到 #p�agma bp_nof�ee 為止可將兩個前置處理指令加在函式中的任何敘述 的前後 → #pragma function fun�tion_name @�om_add�ess 指定函式 fun�tion_name 安置於 RO� 記憶體的 �om_add�ess 位址處 . �om_add�ess 可使用十六進制 � 例如 0x100 代表指定函式放於 RO� 記憶體的 ��6 位址處 . 如果要放在 RO� bank 1 的 ��6 位址 � 則將 �om_add�ess 設為 0x�100 → #pragma nobp 只對中斷服務函式有效 . 指示編譯器不要在中斷服務函式中保存 BP 暫存器的內容 . 這個指令會對單一程式檔案內所有的中斷服務函式有效 → #pragma nolocal fun�tion_name 針對一般的函式 . 對於指定的函式 � fun�tion_name 在編譯時將函式使用到的局部變數與 內部暫存器皆設成一般型態的變數 � 而非會與其他變數共用 RA� 記憶空間的共用型態 (�ommon type). 如果中斷服務函式會呼其他函式時 � 這些被呼叫的函式中使用的局部或內部 變數若與其他函式共用 RA� 空間 � 會有資料被破壞的風險 . 這個前置處理指令將排除這種風險 . → #pragma nomp0 #pragma nomp1 只對中斷服務函式有效 . 指示編譯器不要在中斷服務函式中保存 MP0 或 MP1 暫存器的內 容 . 這個指令會對單一程式檔案內所有的中斷服務函式有效 編譯器會根據微控制器的架構決定是否在中斷服務函式內保存 BP, MP0, MP1 暫存器的內容 → #pragma rambank0 #pragma norambank 指定變數存放在 RAM bank 0 的空間內 . 所有定義在這兩個前置處理指令內的變數皆被安排 在 RAM bank 0 空間 . 如果微控制器只具備一個 RAM bank, 則不需要使用這個指令 . 如果 微控制器具有一個以上的 RAM bank 時 , 可以使用 . 若變數是 bit 資料型態時 , 則必須要將 變數定義在 RAM bank 0 之中 . → #pragma rombank0 #pragma norombank 將函式設置於 ROM bank 0 內 . 所有定義於這兩個前置處理指令中的函式皆被安置 ROM bank 0 的空間內 → #pragma rombank banknum fun�tion_name1 [� fun�tion_name� [� ..]] 這個前置處理指令會將所指定的函式安置在指定的 ROM bank 之中 . banknum 是 ROM bank 的編號 , 可依據微控制器的 ROM 架構設定之 . function_name1,function_name2 是函 式名 , 可以同時指定一個以上的函式 . → #pragma vector is�_name @ve�to�_add�ess 宣告中斷服務函式的名字及中斷向量值 . 每個中斷服務函式皆要預先宣告 . isr_name 是函式 名稱 , vector_address 是中斷向量數值 → #pragma novectornest 指示中斷服務函式中 , 保留暫存器的記憶空間只有一組 . 當微控制器的中斷事件不允許重疊 發生 , 必須處理完成前一個之後才能處理下一個時 , 使用此前置處理指令可以節省資料記憶 空間 (RAM space), 避免 RAM bank 0 的空間滿溢 (overflow) 請參閱 4.7 節 – 中斷服務函式 – 的說明 Rev. 1.30 32 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 4.10 Holtek C 編譯器的內建函式 (built-in functions) ■ 直接編譯成組合語言指令的內建函式 C 函式 組合語言指令碼 (assembly inst�u�tion) void _�l�wdt( ) CLR WDT void _�l�wdt1( ) CLR WDT1 void _�l�wdt�( ) CLR WDT� void _�alt( ) HALT void _nop( ) NOP ■ 其他內建函式 void _rr (char *p) 將指標 p 指到的位元組 (1 byte) 向右旋轉一個位元 (bit) 例如 , ch = 0xA5 ; _rr(&ch) ; 則 ch = 0xD2 void _lrr (int *pl) 將指標 pl 指到的字元組 (2 byte) 向右旋轉一個位元 (bit) 例如 , cnt = 0xA5A5 ; _lrr(&cnt) ; 則 cnt = 0xD2D2 void _rl (char *p) 將指標 p 指到的位元組 (1 byte) 向左旋轉一個位元 (bit) 例如 , ch = 0xA5 ; i_rl (&ch) ; 則 ch = 0x4B void _lrl (int *pl) 將指標 pl 指到的字元組 (2 byte) 向左旋轉一個位元 (bit) 例如 , cnt = 0xA5A5 ; _lrl (&cnt) ; 則 cnt = 0x4B4B void _swap(char *p) 將指標 p 指到的位元組 (1 byte) 四個低位元與四個高位元互換 例如 , ch = 0xA5 ; _swap (&ch) ; 則 ch = 0x5A void _delay(unsigned long tick) 延時 tick 個指令週期 (instruction cycle) tick <= 263690, 若 tick = 0, 則表示無限延時 (infinite loop) ( 請參考 �.�.� 節的說明 ) Rev. 1.30 33 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第五章 基本 C 語言程式 本章主要以範例介紹 C 語言程式的寫法 � 從最簡易的語法開始 � 配合微控制器的特性 � 撰寫易 懂並且較常用的程式 . 當熟悉這些基本的用法後 � 後面的章節將介紹進階的使用語法 . 5.1 C 語法觀念 →→ C 敘述式中的等號 ‘=’ (assignment) 具有寫入 , 指定 , 輸出的動作及功能 . 例如 Po�tAC = 0x01 ; 若 PortAC 是輸出輸入埠 A 的控制暫存器 (0x13), 上式則表示設定埠 A 位元 0 為 1 其他位 元為 0, 就是設定埠 A 位元 0 是輸入口 , 其他位元是輸出口 . 這個敘述相當於組合語言的 �OV A� 01 �OV PAC� A ;; PAC = [13H] → 如果要使用 條件語句的相等 � 則使用兩個相連的等號 == 而不相等則用 != if( �ount == 10 ) { } 代表如果變數 count 等於 10 則執行括號內的敘述 , 否則直接跳到括號之後如果上式寫成 if( �ount = 10 ) 則會先將 10 寫到變數 �ount 中 � 再檢查 �ount 是否為真 ( 不為 0) 本例一定為真 , 括號內的敘述一定會被執行 , 所以可能是一個錯誤 . → 數值的寫法與組合語言的差異 十六進位是以 0x 開頭 � 其後可以用 0 到 9 與 A 到 F� 大小寫皆可 例如 � 0x13� 0xad�� 0x�B → 在等號 `=' 右邊的變數或暫存器 � 具有讀出的動作及功能 � 例如 status = Po�tA ; 從埠 A 讀進輸入值並存入變數 status. 它的功能有如組合語言的 �OV A� Po�tA ;; Po�tA = [1�H] �OV status� A 5.2 迴圈的應用 (loop) 當程式需要重複執行相同或類似的動作時 � 可以用迴圈的語法完成 . 例如 � 要將資料記憶體的內 容清除為零 � 或是對輸出 / 輸入埠的每個位元輪流做設定或清除等 . 這些語法的關鍵字包括 fo�� w�ile� do-w�ile 等 � 下列範例將說明如何使用 5.2.1 範例一 : 8 LED 霹靂燈 功能說明 : 使用 HT48R06A-1 控制八顆 LED 構成的霹靂燈 電路說明 : 將 HT48R06A-1 埠 A 的八個引腳接上電阻及 LED� 從埠 A 控制 LED 的亮與滅 程式及說明 : 程式將對埠 A 的八個位元依序設定 � 分別點亮連結的 LED #in�lude “�t48�06A-1.�" // 引入標頭檔 �t48�06a-1.�� 需要用到其中已定義的變數 // _pa 是埠 A 資料埠的位址 � _pa� 是埠 A 的控制暫存器 void main(void) // 定義主函式 { ��a� status� �nt ; // 定義區域變數 � 當作 LED 點亮的控制碼 _pa� = 0 ; // 寫 0 到埠 A 的控制暫存器 � 設定埠 A 為輸出型態 fo�( �nt = 0 ; �nt < 8 ; �nt++ ) // 迴圈 � 共執行 8 次點亮 LED 的動作 { status = (1 << �nt) ; // 第一次是 PA0� 第二次是 PA1� … 的 LED 被點亮 _pa = status ; // 輸出到埠 A� 點亮一個 LED 及熄滅其他 LED } } 嘗試 : 將上面程式中的 fo� 迴圈改為 w�ile 迴圈 Rev. 1.30 34 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 5.2.2 延遲 ( 等候 ) 時序 (Delay) 當處理微控制器的周邊裝置時會有嚴格的時序要求 � 利用 C 語言設計一個好的時序控制程式將 會對 C 語言的應用專案有很大的幫助 � 下列的方式可以達到此目的 → 用 in-line assembly 方式將組合語言的時序控制程式嵌入或引入 C 程式中 若以呼叫函式的方式 � 則需要了解如何混合使用 C 語言及組合語言的程式 . 若以巨集指令的方式 � 則會增加程式的大小 � 不利 PRO� 空間的靈活使用 這種寫法比較適合延遲時間小於 � 個指令週期的應用上 → 以 C 語言實作時序控制程式 � 則需要計算編譯後的組合指令實際執行的時 間有些時序並無法做到精準的程度 � 需要做適度的調整 本節將以 C 語言實作一個時序控制程式 � 分析及提供一個較適合的函式 � 可讓用者輕鬆地控 制所要的時序 . 此函式是以 Holtek C �ompile� 版本編譯後的指令碼為準 . 若用其他 C 編譯 器來編譯此函式 � 則需要做類似的分析 � 結果可能會有差異 5.2.2.1 時序控制程式 ( 一 ) for 迴圈的寫法 → 定義時序控制程式 delay void delay (unsigned int �y�le) { unsigned int �ount ; fo�( �ount = �y�le ; �ount > 0 ; �ount-- ) ; } 輸入參數 �y�le 將決定延遲的指令週期 (inst�u�tion �y�le). 根據微控制器的頻率可以計算出執行 所需要的時間 . 1 代表 �6 �y�le� � 是 34 �y�le�.. 編譯後的組合語言程式如下 呼叫 delay(�ount) 的參數會存入變數 delay0 ( 低位元組 ) 及 delay0+1 ( 高位元組 ) fo�( �ount=�y�le ; �ount > 0 ; �ount-- ) 0000 0700 R �OV A�delay0 0001 0080 R �OV �ount�A 000� 0700 R �OV A�delay0[1] 0003 0080 R �OV �ount[1]�A 0004 �800 R J�P L� 000� L�: 000� 0700 R �OV A��ount 0006 0A01 SUB A�01� 0007 0080 R �OV �ount�A 0008 380A SNZ [0AH].0 0009 1�80 R DEC �ount[1] 000A L�: 000A 1080 R SZ �ount 000B �800 R J�P L� 000C 1080 R SZ �ount[1] 000D �800 R J�P L� 000E L1: 000E 0003 RET // CALL� J�P� RET 需要 � 個 inst�u�tion �y�le // SZ 如果會 skip 下一個指令時 � 也需要 � 個 inst�ut�ion �y�le 上述的範例僅供參考 � 因為編譯後的指令較難滿足實際精確的等候時間 � 主要是介紹如何撰寫 C 語言程式 Rev. 1.30 35 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 5.2.2.2 時序控制程式 ( 二 ) – while 迴圈寫法 → 定義時序控制程式 delayw void delayw (unsigned int �ount) { w�ile ( �ount != 0 ) �ount-- ; } 更換成使用 w�ile 的迴圈方式 � 編譯後的指令碼如下 0000 �800 R J�P L8 0001 L7: 0001 0700 R �OV A��ount 000� 0A01 SUB A�01� 0003 0080 R �OV �ount�A 0004 380A SNZ [0AH].0 000� 1�80 R DEC �ount[1] 0006 L8: 0006 1080 R SZ �ount 0007 �800 R J�P L7 0008 1080 R SZ �ount[1] 0009 �800 R J�P L7 000A L6: 000A 0003 RET // CALL� J�P� RET 需要 � 個 inst�u�tion �y�le // SZ� SNZ 如果會 skip 下一個指令時 � 也需要 � 個 inst�ut�ion �y�le �ount 為 1 時會等候 �� 個指令週期 � �ount 為 � 時等候 30 個週期 . 5.2.2.3 時序控制程式 ( 三 ) – 呼叫編譯器的內建函式 _delay() void _delay(unsigned long �lo�ks) 參數 �lo�ks 是指延遲的指令週期次數 (inst�u�tion �y�le �ount)� 例如若 �CU 的頻率是 4�Hz� 則每個指令週期的執行時間為 1us� 即可計算出需要的次數 . 有效的次數為 1 ~ �63690� 如果是 0 則表示要無限期的延遲 . 可以使用這種方式以等候硬體中斷的發生這個函式可以精準計算出 確切的時間並實際的執行 � 如應用上需要精確的時間 � 可以呼叫此函式 . 如果所需之時間超出 �63690 次數 � 可以重複呼叫此函式 . Rev. 1.30 36 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 5.3 撰寫 MCU 應用程式的注意事項 a) 應用程式模組化 . 將各功能以函式 (fun�tion) 獨立出來 � 分別定義於不同的檔案中 . 以功能來 命名函式及檔案名 � 容易記住及了解程式的功能 . 可減少各檔案編譯的時間 � 程式編譯後的 錯誤不致過多 . 日後的維護及增修也相對的容易 . b) 變數名要定義的有意義 � 在撰寫程式及維護時較方便使用並且不易出錯 � 節省撰寫時間 �) 盡可能不要對下列的 �CU 暫存器 (�egiste�) 做讀寫的動作 C 編譯器所編出的組合語言指令 會使用到這些暫存器 � 如果應用程式中也使用 � 會造成混亂並影響程式執行的正確性 . C 語言的變數名字 ( 定義在標頭檔 .h 中 ) 暫存器名稱 記憶體 (RAM) 位址 IAR0 0x00 _ia�0 �P0 0x01 _mp0 IAR1 0x0� _ia�1 �P1 0x03 _mp1 BP 0x04 _bp ACC 0x0� _a�� PCL 0x06 _p�l STATUS 0x0a _status 另外 � 標頭檔 (.�) 中定義的符號 _�� _a�� _z� _ov� _pdf 與 _to 皆不要使用 . 例如 � if( _� ) a = a� + b� + 1 ; 就是不好的寫法 d) 在 Holtek C �ompile� 下 � 若程式需用下列的內建函式 (built-in fun�tion) 時 � 要注意參數的資 料型態 � 而且需要做函式的宣告 . 例如 exte�n void _delay(unsigned long) ; _��(��a� *) � _���(��a� *)� _l��(int *)� _l���(int *)� _�l(��a� *)� _�l�(��a� *)� _l�l(int *)� _l�l�(int *)� _swap(��a� *)� _delay(unsigned long) 上述內建函式的返回值型態 (return type) 皆是 void. 如果需要對變數做 左移 / 右移 的動作時 � 可以使用下列的運算子 �ount = time >> 3 ; �ount = ent�y << 4 ; C 語言中沒有旋轉 (�otate) 的運算子 � 可以使用 signed 型態及左右移運算子來完成 e) 記憶體的清空 (initialize to ze�o) 用戶須給每個變量初始值,否則變量的初始值是隨機的。 f) 某些 16 bits 的資料是由兩個暫存器組成的 , 例如 ADC 的 adrl, adrh, 當讀取後可用一個 unsigned int 型態的變數儲存之 . unsigned int voltage ; voltage = (unsigned int)_ad�� ; voltage = (voltage << 4) + (unsigned int)(_ad�l >> 4) ; // 1� bit ADC g) 少用 goto 敘述 � 因為會需要定義 label� 讓程式不具模組化 . 若 label 過多 � 程式的可讀性就 降低了 . �) 避免使用 #in�lude 將 .� 的檔案加在另一個 .� 的檔案中 . #in�lude 前置指令是將標頭檔 (�eade� file� .�) 引入 � 指引 C 編譯器將標頭檔中定義的常數 � 宣告的變數與函式等讀入 � 讓 原始程式檔中的敘述有所參考 � 並產出正確的指令碼 . 如果將 .� 程式檔加另一個 .� 程式檔時 � 則等於將程式檔擴大到兩個檔案的大小 � 不但會增加編譯的時間 � 也增加閱讀 � 維護的不便 . Rev. 1.30 37 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 5.4 可供微控制器應用程式使用的範本 微控制器的應用程式中常會需要執行一些動作 � 以下的範例是以 C 語言撰寫 � 讀者可將之直接 加入到程式中或是另外建入副函式 � 再呼叫此副函式完成所需的動作 . a) 針對資料記憶體空間做初始化的動作 , 亦即清為零 . 通常在剛進入主程式時就會執行此動作 � 可以將之加入主函式 main( ) 中或是另建一個副函 式 (sub�outine) 並從主函式呼叫之 . 將資料記憶體 bank 1 的內容清為零 unsigned ��a� *mpt� ; fo�( mpt� = (unsigned ��a� *)0x140 ; mpt� < (unsigned ��a� *)0x1�3 ; mpt�++ ) *mptr = 0 ; // 將 RAM bank 1 的 0x40 到 0x53 位址清為 0 b) 延遲 ( 等候 ) 時間 應用程式中有時會需要等候 ( 或延遲 ) 一段時間再做之後的處理 � 因此等候的時間就需要做 準確的計算 . 以微控制器的頻率為 4�Hz 為例 � 一個指令週期是 1us ( 微秒 ). 如果只需要等 候數個微秒的時間 � 可以直接呼叫編譯器的內建函式 _nop(). 或是 _delay() 函式 _nop() 函式 只需一個指令週期 � 若是要等候 � 微秒 � 則只需呼叫 � 次 _nop() 即可 . 若需要等候較多的時間 , 可以直接呼叫內建函式 _delay() _delay( clocks ) 函式中的參數 clocks 是 unsigned long 的資料型態 , 當 clocks = 0 則表示 要無限的延遲 , 可以使用此函式當做等候中斷事件的發生 , 類似無限迴圈的方式 .clocks 可以 從 1 到 263690, 若需要超出此時間 , 可以連續呼叫 . �) 讀取表格資料的方式 應用程式中常有資料表格以供程式運行時參考之需 � 可以使用 C 語言的陣列 (a��ay) 或是指 標 (pointe�) 將表格內的資料讀取出 . unsigned char WordTable[16] = { 0x41, 0xE7, 0x52, 0x62, 0xE4, 0x68, 0x48, 0xE3, 0x40, 0x60, 0xC0, 0x4C, 0x59, 0x46, 0x58, 0xD8 } ; // 定義陣列 , 顯示 ‘0’, ‘1’, ‘2’, …., ‘9’,’A’, ‘b’, ‘C’, ‘d’, ‘E’, ‘F’ int k ; LedPortCtrl = 0 ; // 設定 pa 埠為輸出 LedPort = 0xff ; // 熄滅所有 for( k = 0 ; k < 16 ; k++ ) // 總共顯示 16 的字元 { LedPort = WordTable[k] ; // 點亮第 k 個字 , 從表格中讀取資料 _delay(50000) ; // 延遲 50 毫秒 (ms) } 另外一種寫法是使用指標 , 表格的最後資料 0x00 代表表格的結束 unsigned ��a� Wo�dTable[17] = { 0x41� 0xE7� 0x��� 0x6�� 0xE4� 0x68� 0x48� 0xE3� 0x40� 0x60� 0xC0� 0x4C� 0x�9� 0x46� 0x�8� 0xD8� 0x00 } ; unsigned ��a� *tpt� ; tpt� = Wo�dTable ; w�ile( *tpt� != 0 ) { LedPo�t = *tpt�++ ; _delay(�0000) ; } d) 讀寫兩個位元組資料的方式 使用 ADC 中斷方式去讀取轉換後的數位資料 � 以 HT46R6� 的 9 位元為例 unsigned int Ad�Value ; // 定義一個全域變數以儲存轉換後的數值 void ADC_ISR( ) // 定義 ADC 中斷服務函式 � 將轉換後的數值讀出及儲存 { Ad�Value = ((unsigned int)_ad�� <<1) + ((unsigned int)_ad�l >> 7) ; // 讀出高位元組資料 D8~D1 並向左移動一個位元 , // 讀出低位元組 , 向右移 7 個位元 , 合併組成一個 int 的資料型態 , // 並存入變數 } Rev. 1.30 38 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 如果需要從時鐘讀出目前的計數 (timer count), 以 HT48R70A-1 為例 , Timer 0 是 16 位元 unsigned int TimerCount ; // 儲存讀出的計數 _t0on = 0 ; // 先將 timer 0 停止計數 TimerCount = ((unsigned int)_tmr0h <<8) + (unsigned int)_tmr0l ; // 讀出高位元向左移動 8 位元 , 讀出低位元計數並加入 int 的變數儲存之 _t0on = 1 ; // 將 timer 0 打開 , 繼續計數 e) 針對 RA� bank 0 以外的變數或暫存器 � 可以利用指定變數位址的方式讀寫 � 例如 要將數值 寫入 RA� bank 1 的 0x40 位址 � unsigned char Bank0140 @ 0x140 ; // 定義變數 Bank0140, 並指定其位址 unsigned char out ; Bank0140 = 0x20 ; // 將 0x20 寫入 RAM bank 1 的 0x40 位址內 out = Bank0140 ; // 將 RAM bank 1 位址 0x40 的內容儲存到變數 out 5.5 設計微控制器應用程式的小技巧 a) 多層記憶體微控制器的程式精緻化 (code optimization of multi-ROM-bank MCU) 在具備多個程式記憶體的微控制器 (multiple RO� bank) 上開發程式時 � 常會需要加入變更 BP 暫存器的指令 (�OV BP�A) 以便切換到其他的 RO� bank 去執行 � 因而增加整個程式碼 的大小 . 為能將程式碼的大小減少 � 可以對程式做以下的安排 ( 兩個步驟 ) ■ 將所有會呼叫到的函數皆指定在同一個 ROM bank, 例如 #p�agma �ombank 1 fun�1� fun��� fun�3 // 將函數 fun�1� fun�� 與 fun�3 指定放在 RO� bank 1� 請參考 4.9.� 節 void fun�1(void) { } void fun��(void) { ...... fun�1( ) ; // 函數 fun�1 被呼叫 � 指定其與 fun�� 放在同一 RO� bank ...... swit��( ) { �ase xx : } } void fun�3(void) { fun��( ) ; // 指定與函數 fun�3 放在同一個 RO� bank 內 if( ) { } else ...... fun�1( ) ; ...... } ■ 將函數中使用到的全域變數 , 指定在 ROM bank 0, 使用 #pragma rambank0 #pragma rambank0 char count ; // 指定 變數 count 放置於 RAM bank 0 #pragma norambank Rev. 1.30 39 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第六章 程式範例 – 初級 本章將以基本的 C 語言介紹如何開發微控制器的應用程式 . 從最淺顯的程式架構及敘述並配合 微控制器的功能 , 設計適當的應用程式 . 每個範例會以較常用的語法及敘述完成之 . 從設計顯示 器的控制應用程式及鍵盤處理程式開始 , 包括 LED, 七段顯示器 , 液晶顯示器 (LCD), 4*4 點矩 陣按鍵輸入等 . 其中有些程式 ( 函式 ) 可以直接移植到其他的應用專案或只需修改輸出 / 輸入埠 的設定並重新編譯就可使用 6.1 LED 跑馬燈 6.1.1 目的 學習 fo�/w�ile 迴圈執行時間延遲及連續輸出資料到 po�t A 以達到控制 8 顆 LED 輪流點亮 � 其 他則滅的目標 . 亮滅的間隔時間為 �00 毫秒 (ms) 6.1.2 周邊元件 HT48R06A-1 的輸出輸入埠 A 連接到 8 顆 LED 的陰極 . 所以當要點亮 LED 時 � 只需對其相連 的 PA 埠輸出低電位 . 6.1.3 電路圖 PA0 ~ PA7 各連結到 LED 的陰極 , MCU 頻率為 4MHz 6.1.4 微控制器的架構設定 (configuration option) 可選擇使用 ICE 內部的振盪器 � 頻率設為 4000KHz (4�Hz) 可以設定埠 A 有內部的 pull-�ig�� 就不需要在外面加 pull-�ig� 電阻 WDT : disable WDT Inst�u�tion : one �lea� inst�u�tion PA wake up : none Pull �ig� : all WDT OSC : on ��ip RC OSC : ��ystal Sys volt : �.000V Sys f�eq : 4000kHz� Inte�nal Rev. 1.30 40 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.1.5 程式流程 開始 埠 A 設為輸出型態輸出高電位到 埠 A� 熄滅 LED 從 pa0 開始 � 一一點亮 LED 結束 6.1.6 原始程式 1 #include “ht48r06a-1.h” 2 #define LedPort _pa // pa 埠 3 #define LedPortCtrl _pac // pa 控制埠 4 void main(void) // 主函式 5 { 6 int k ; 7 LedPortCtrl = 0 ; // 設定 pa 埠為輸出 8 LedPort = 0xff ; // 熄滅所有 LED 9 for( k = 0 ; k < 8 ; k++ ) 10 { 11 LedPort = ~ (1 << k) ; // 點亮第 k 個 LED 12 _delay(250000) ; // 延遲 500 毫秒 (ms) 13 _delay(250000) ; 14} 15 16 LedPort = 0xff ; // 熄滅所有 LED } 6.1.7 程式說明 1 引入標頭檔 � 每個程式檔的最開始 � 最好加入此行 � 告訴 C 編譯器要加入此檔案中的宣 告及定義 �~3 定義變數 LedPo�t 為埠 A 的資料暫存器 � LedPo�tCt�l 為埠 A 的控制暫存器 7 寫 0 到埠 A 控制暫存器 � 以設定埠 A 為輸出型態 � 此敘述與下一行組合語言指令有相 同的功能 : CLR PAC 8 寫 0xff 到埠 A 資料暫存器 � 輸出 11111111b� 表示熄滅所有的 LED 9 fo� 迴圈 � 一次點亮一個 LED� 從 PA0 的 LED 開始 � 一直到 PA7 的 LED 11 將 1 向左移 k 位元 � k 代表埠 A 的第 k 支引腳 (k=0�1���…7). 再做 1 的補數 (1's �omplement) 將 1 改為 0 � 其他 7 個零改為 1. 之後寫到埠 A 資料暫存器對第 k 支引腳 輸出低電位 � 點亮其上的 LED. 其它引腳為高電位 � 所以不亮 . 1�~13 延遲 �00 毫秒讓 LED 維持點亮 . 再做下一個 LED 的點亮 . 相鄰兩個 LED 點亮熄滅的 間隔時間會大於 �00 毫秒 � 因為 fo� 迴圈還要執行點亮的前端工作 1� Rev. 1.30 將所有的 LED 熄滅 如果想要讓 LED 再從頭輪流點亮 � 可以在第 1� 行之處改為 goto again ; 而在第 9 行 前面加上 again: ( 要有冒號 ) 或在第 14 行加上 w�ile(1) 無限迴圈 41 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.2 LED 霹靂燈 6.2.1 目的 學習 fo� 迴圈執行時間延遲及連續輸出資料到 po�t A 以達到控制 8 顆 LED 依照設定之次序輪流 點亮 . 點亮次序是從陣列中讀出 � 按照點亮 LED0� LED0+LED1�LED0+LED1+LED� 的順序 � 直 到 8 個 LED 都點量為止 . 亮滅的間隔時間為 �00 ms 學習使用 C 的陣列變數 � 取代組合語言程 式使用讀表指令 (TABRDC� TABRDL) 讀取顯示格式資料的方式 � 增高程式可讀性 6.2.2 周邊元件 HT48R06A-1 的輸出輸入埠 A 連接到 8 顆 LED 的陰極 . 所以當要點亮 LED 時 � 只需對其相連 的 PA 埠輸出低電位 . 6.2.3 電路圖 VDD VDD VDD 10kΩ PA0 PA1 0.1µF RES PA� VSS PA3 PA4 OSC1 PA� PA6 ��Hz OSC� PA7 �40Ω �40Ω �40Ω �40Ω �40Ω �40Ω �40Ω �40Ω HT48R30A-1 PA0 ~ PA7 各連結到 LED 的陰極 , MCU 頻率為 4MHz 6.2.4 微控制器的架構設定 (configuration option) 可選擇使用 ICE 內部的振盪器 � 頻率設為 4000KHz (4�Hz) 可以設定埠 A 有內部的 pull-�ig�� 就不需要在外面加 pull-�ig� 電阻 WDT : disable WDT Inst�u�tion : one �lea� inst�u�tion PA wake up : none Pull �ig� : all WDT OSC : on ��ip RC OSC : ��ystal Sys volt : �.000V Sys f�eq : 4000kHz� Inte�nal Rev. 1.30 42 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.2.5 程式流程 開始 埠 A 設為輸出型態輸出高電位到 埠 A� 熄滅 LED 從陣列讀出顯示資料並點亮 LED 結束 6.2.6 原始程式 1 #include “�t48�06a-1.�" � #define LedPo�t _pa // pa 埠 3 #define LedPo�tCt�l _pa� // pa 控制埠 4 ��a� LedTable[8] = { 0xfe� 0xf�� 0xf8� 0xf0� 0xe0� 0x�0� 0x80� 0x00 } ; � // 陣列變數 � 設定顯示資料 � 點亮 LED0� LED0+LED1� LED0+LED1+LED�� …. 6 void main(void) // 主函式 7 { 8 int k ; 9 LedPo�tCt�l = 0 ; // 設定 pa 埠為輸出 10 LedPo�t = 0xff ; // 熄滅所有 LED 11 fo�( k = 0 ; k < 8 ; k++ ) 1� { 13 LedPo�t = LedTable[k] ; // 輸出 LED 控制碼 � 點亮 LEDx 14 _delay(��0000) ; // 間隔 ��0 ms 1� _delay(��0000) ; // 間隔 ��0 ms 16 } 17 LedPort = 0xff ; // 熄滅所有 LED 18 } 6.2.7 程式說明 除了第 13 行 � 其他與 6.1 節的程式皆相同 . 第 13 行是從陣列 LedTable 中取得顯示資料 � 再將 之輸出到 LED 埠 . 第 14� 1� 行是呼叫內建函式 _delay()� 參數 ��0000 表示要延遲的 �lo�k 數 為 ��0000� 此例 1 �lo�k = 1us� 一次呼叫則延遲 ��0 ms Rev. 1.30 43 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.3 單顆七段顯示器 6.3.1 目的 以共陽極七段顯示器顯示 0 ~ 9� A ~ F 等十六個字 � 間隔 �0 ms. 本範例與上一範例相似 � 唯一 不同處為陣列內容 � 需配合七段顯示器的排列 � 設計字型的顯示碼 . 6.3.2 周邊元件 將微控制器的輸出輸入埠 pa0 ~ pa7 連結到七段顯示器 � 各字的顯示碼如下兩表 . 輸出 low 到 某段則點亮該段 . a f g b Segment � e p d Character Code Character Code Port a b � d e f g p PA� PA3 PA4 PA7 PA� PA1 PA0 PA6 0 1 � 3 4 � 6 7 8 9 41� E7� ��� 6�� E4� 68� 48� E3� 40� 60� A b C d E F — — — — C0� 4C� �9� 46� �8� D8� — — — — 6.3.3 電路圖 6.3.4 微控制器的架構設定 (configuration options) 可選擇使用 ICE 內部的振盪器 , 頻率設為 4000KHz (4MHz) 可以設定埠 A 有內部的 pull-high, 就不需要在外面加 pull-high 電阻 WDT : disable WDT Inst�u�tion : one �lea� inst�u�tion PA wake up : none Pull �ig� : all WDT OSC : on ��ip RC OSC : ��ystal Sys volt : �.000V Sys f�eq : 4000kHz� Inte�nal Rev. 1.30 44 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.3.5 程式流程 相同於 6.�.� 程式流程 6.3.6 原始程式 1 #in�lude “�t48�06a-1.�" � #define LedPo�t _pa // pa 埠 3 #define LedPo�tCt�l _pa� // pa 控制埠 4 unsigned ��a� Wo�dTable[16] = { 0x41� 0xE7� 0x��� 0x6�� 0xE4� 0x68� 0x48� 0xE3� � 0x40� 0x60� 0xC0� 0x4C� 0x�9� 0x46� 0x�8� 0xD8 } ; 6 // 定義陣列 � 顯示 `0'� `1'� `�'� ….� `9'�'A'� `b'� `C'� ` d'� `E'� `F' 7 void main(void) // 主函式 8 { 9 int k ; 10 LedPo�tCt�l = 0 ; // 設定 pa 埠為輸出 11 LedPo�t = 0xff ; // 熄滅所有 1� fo�( k = 0 ; k < 16 ; k++ ) // 總共顯示 16 的字元 13 { 14 LedPo�t = Wo�dTable[k] ; // 點亮第 k 個字 1� _delay(�0000) ; // 延遲 �0 毫秒 (ms) 16 } 17 LedPo�t = 0xff ; // 熄滅所有 18 } 6.3.7 程式說明 14 將顯示資料從陣列 Wo�dTable 中讀出 � 再輸出到 LED 埠顯示之 6.4 5*5 點矩陣 LED 顯示 6.4.1 目的 控制 � * � 點矩陣 LED� 顯示字串 “HOLTEK"� 每字間隔 �0 毫秒 (ms)� 每字在 �4 ms 內將 � 行 (�ow) 顯示完成 . 本範例使用 C 的指標型態 (pointe�) 及陣列 (a��ay). 以指標方式讀取陣列中 的元素並顯示出 . 可以使用本範例的方式讀取二維陣列的資料 . 6.4.2 周邊元件 pa0 ~ pa4 連接點矩陣的列 (�ow0 ~ �ow 4)� pb0 ~ pb4 連接點矩陣的行 (�olumn 0~4) 使用 �� 顆 LED 排列成 �*� 的矩陣 � 如下圖 . 再將之與微控制器的埠 A� 埠 B 連接 . ●○○○●○●●●○●○○○○●●●●●●●●●●●○○○● ●○○○●●○○○●●○○○○○○●○○●○○○○●○○●○ ●●●●●●○○○●●○○○○○○●○○●●●●○●●●○○ ●○○○●●○○○●●○○○○○○●○○●○○○○●○○●○ ●○○○●○●●●○●●●●●○○●○○●●●●●●○○○● Rev. 1.30 45 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.4.3 電路圖 6.4.4 微控制器的架構設定 (configuration option) 可選擇使用 ICE 內部的振盪器 � 頻率設為 4000KHz (4�Hz) 可以設定埠 A 及埠 B 有內部的 pull-�ig�� 就不需要在外面加 pull-�ig� 電阻 WDT : disable WDT Inst�u�tion : one �lea� inst�u�tion PA wake up : none Pull �ig� : all WDT OSC : on ��ip RC OSC : ��ystal Sys volt : �.000V Sys f�eq : 4000kHz� Inte�nal 6.4.5 程式流程 開始 設定埠A, 埠B為輸出型態 熄滅 5*5 點矩陣 LED 從表格中取出一個指標 從指標讀出字型碼 從第一列開始, 共五列 點亮一列與此列的五行 NO Rev. 1.30 已顯示五 列 ? 46 YES 結束 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.4.6 原始程式 1 #in�lude “�t48�06a-1.�" � #define LedRowPo�t _pa // �ow� pa 埠 3 #define LedRowPo�tCt�l _pa� // pa 控制埠 4 #define LedColPo�t _pb // �olumn� pb 埠 � #define LedColPo�tCt�l _pb� // pb 控制埠 6 ��a� ��a�H[�] = { 0x1f� 0x4� 0x4� 0x4� 0x1f } ; // 高電位 (�ig� level) 表示點亮 7 ��a� ��a�O[�] = { 0x0e� 0x11� 0x11� 0x11� 0x0e } ; 8 ��a� ��a�L[�] = { 0x1f� 0x10� 0x10� 0x10� 0x10 } ; 9 ��a� ��a�T[�] = { 0x01� 0x01� 0x1f� 0x01� 0x01 } ; 10 ��a� ��a�E[�] = { 0x1f� 0x1�� 0x1�� 0x1�� 0x11 } ; 11 ��a� ��a�K[�] = { 0x1f� 0x4� 0x4� 0x0a� 0x11 } ; 1� ��a� *LedTable[6] = { ��a�H� ��a�O� ��a�L� ��a�T� ��a�E� ��a�K } ; // 指標陣列 13 14 void main(void) // 主函式 1� { 16 int k ; 17 ��a� *tpt� ; 18 LedRowPo�tCt�l = 0 ; // 設定 pa 埠為輸出 19 LedColPo�tCt�l = 0 ; // 設定 pb 為輸出 �0 LedColPo�t = LedRowPo�t = 0 ; // 熄滅 LED �1 fo�( k = 0 ; k < 6 ; k++ ) // 總共顯示 6 個字元 �� { �3 tpt� = LedTable[k] ; // 讀出第 k 個字的指標 pointe� �4 fo�( j = 0 ; j < � ; j++ ) �� { �6 LedColPo�t = (1 << j ) ; // 點亮第 k 個字的 �olumn j �7 LedPo�t = tpt�[j] ; // 輸出 �ow 的字型 � 點亮相對的字型 �8 _delay(�000) ; // 每列間隔 � ms �9 } 30 _delay(�0000) ; // 每字間隔 �0 ms 31 } 3� LedColPo�t = 0 ; // 熄滅所有 LED 33 } 6.4.7 程式說明 6 ~ 11 定義 “HOLTEK" 六個字元的字型碼 (patte�n) Rev. 1.30 1� 陣列 LedTable 定義上述六個陣列的指標 �1 從第一個字元開始 � 共顯示 6 個字元 �3 從陣列 LedTable 取出字元的指標 �4 每個字元有 �*� 的點矩陣 � 以每列 (�ow) 為顯示單位 �6 輸出列的顯示資料 �7 輸出此列的五個行 (�olumn) 的資料 �8 每列的間隔為 � 毫秒 (ms) 30 每字的間隔為 �0 毫秒 (ms) 47 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.5 HT48 微控制器控制 HT1621 LCD 的顯示 6.5.1 目的 本範例是使用 HT48 系列的微控制器控制 HT16�1 LCD 液晶顯示器 . 透過發送命令給 HT16�1� 控制液晶顯示器在液晶面板 (panel) 上顯示字元或圖形 . 經由 HT48 的輸出 / 輸入埠與 HT16�1 的引腳連接 � 發送 HT16�1 的命令 . 本範例是以 HT48R10A-1 微控制器來控制 HT16�1� 並介紹 如何點亮及清除 LCD 所有位元 . 6.5.2 周邊元件 HT1621 是一個 128 位元的 LCD 控制元件 , 內部 RAM 直接對應 LCD 的顯示單元 . 軟體可使它 適用於 LCD 模組或顯示系統的多功能應用上 . 微控制器與 HT1621 的介面只需 4 到 5 根線 . 內 置的省電模式極大的降低了功率耗損 . ■ 資料記憶體 HT1621 LCD 驅動器內部具備一個顯示資料的記憶體 , 大小為 32*4 位元 , 它的位置排列如下 C O M 3 C O M 2 C O M 1 C O M 0 S E G 0 0 S E G 1 1 S E G 2 2 S E G 3 3 S E G 3 1 3 1 D 3 D 2 D 1 D 0 A d d r e s s 6 b its (A 5 , A 4 , ..., A 0 ) A d d r D a ta D a ta 4 b its (D 3 , D 2 , D 1 , D 0 ) 共有 3� segments 與 4 �ommons. 當 LCD 打開後 � 將數據寫入這些記憶體便可控制各對應的 點是發亮或熄滅 . D0 控制 �ommon 0 與 segment0 ~ segment31 交叉點的發亮與否 � D1 控制 �ommon 1 與 segment0 ~ segment31 交叉點的發亮與否 . D� 控制 �ommon � 與 segment0 ~ segment31 交叉點的發亮與否 . D3 控制 �ommon 3 與 segment0 ~ segment31 交叉點的發亮 與否 . ■ 命令格式 HT16�1 LCD 驅動器支持兩類模式 � 命令模式及資料模式 . 命令模式是對 HT16�1 下達開啟或 關閉 LCD 的顯示及設定 LCD 顯示的架構等 � 以二進位的 100 開始的數字是命令模式的識別碼 (ID). 而資料模式則是從 LCD 記憶體中讀出資料或寫資料到 LCD 記憶體 � 它的識別碼是以二進 位的 110 或 101 開頭 . 下表為本範例所用的命令 � 詳細的命令請參閱 HT16�1 data s�eet. ■ 命令模式 命令名稱 識別碼 命令碼 功能 SYS DIS 100 0000-0000-x 關閉系統振盪器及 LCD 偏壓產生器 V SYS EN 100 0000-0001-x 打開系統振盪器 — LCD OFF 100 0000-0010-x 關閉 LCD 偏壓產生器 V LCD ON 100 0000-0011-x 打開 LCD 偏壓產生器 — 0010-abx�-x 設定 LCD 的偏壓及 common 選項 c=0, 偏壓為 1/2 c=1, 偏壓為 1/3 ab=00, 2 common ab=01, 3 common ab=10, 4 common — BIAS& CO� Rev. 1.30 100 48 電源重置 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 資料模式 命令名稱 識別碼 命令碼 功能 READ 110 A�A4A3A�A1A0D0D1D�D3 從 RAM 讀出資料 WRITE 101 A�A4A3A�A1A0D0D1D�D3 寫資料到 RAM READ-�ODIFYWRITE 101 A�A4A3A�A1A0D0D1D�D3 讀和寫資料到 RAM x : don’t care bit A5 ~ A0 : RAM address 記憶體位址 D3 ~ D0 : RAM 資料 在使用 LCD 之前必須要先送出兩個命令 , SYS EN 與 LCD ON 給 HT1621 以便開啟 LCD. 由 於是串列通訊 , 所有的命令皆是一個位元接著送出 , 順序是先送出識別碼 (3 個位元 ), 然後是命 令碼 (9 或 10 位元 ). 最後要將 /CS 準位拉高做為結束 . 重複此順序 , 再送出下一個命令 . 如果接下來的也是相同的命令與連續的位址時 , 可以不用將 /CS 準位拉高 , 而是直接做資料讀取或寫入 , 等全部完成後再拉到高位準 . ■ 命令時序 (command timing) ● READ �ode (Command ID Code : 110) ● READ �ode (Su��ess Add�ess Reading) C S W R R D 1 1 D A T A 0 A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 2 ) D a ta (M A + 3 ) ● WRITE �ode (Command ID Code: 1 0 1) Rev. 1.30 49 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ● WRITE �ode (Su��ess Add�ess W�iting) C S W R 1 D A T A A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 2 ) D a ta (M A + 3 ) 1 0 ● READ-�ODIFY-WRITE �ode (Command ID Code: 101) ● READ-�ODIFY-WRITE �ode (Su��ess Add�ess A��essing) C S W R R D 1 D A T A 1 0 A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 1 ) D a ta (M A + 2 ) ● Command �ode (Command �ode ID: 100) C S W R D A T A 1 0 0 C 8 C 7 C 6 C 5 C 4 C 3 C 2 C 1 C 0 C 8 C 7 C 6 C 5 C 4 C 3 C 2 C 1 C 0 C o m m a n d 1 C o m m a n d ... C o m m a n d i C o m m a n d o r D a ta M o d e ● Command �ode (Data and Command Code) Rev. 1.30 50 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.5.3 電路 連結 HT48R10A-1 的輸出 / 輸入埠到 HT1621 的 pin 腳 , 如下 HT48R10A-1 I/O port HT1621 pin PB1 DATA PB� /WR PB3 /CS PB4 /RD 6.5.4 微控制器的架構設定 (configuration option) HT48R10A-1 的 �onfigu�ation option 設定 WDT �lo�k sou��e : disable OSC : Ext. C�ystal Pull-�ig� PA : Pull-�ig� Pull-�ig� PB : Pull-�ig� Input type PA : S��mitt T�igge� BZ/BZ : Disable fSYS = 4� 6.5.5 程式流程 開始 設定 LCD 為 NORMAL 狀態 設定 LCD 為 SYS EN 致能 設定 LCD 為 4 common, 1/3 bias 設定 LCD ON (開啟) 對 LCD 做初 始動作 清除 LCD RAM 資料 (熄滅 LCD) 寫入所有 LCD RAM 的空間 將 LCD 點亮 從 LCD RAM 讀出 segment 4 的 內容. 檢查是否與上述寫入的資料相 同 (0x0f) 結束 Rev. 1.30 51 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.5.6 原始程式 1 #in�lude “�t48�10a-1.�" � #define C�D_NOR�AL 0xE3 // no�mal �ommand 3 #define C�D_SYSEN 0x01 // system enable 4 #define C�D_SYSDIS 0x00 // system disable � #define C�D_LCDOFF 0x0� // LCD off 6 #define C�D_LCDON 0x03 // LCD on 7 #define �sb _pb3 // HT16�1 的 CSB 腳 = bit 3 of po�t B 8 #define w�b _pb� // HT16�1 的 WRB 腳 = bit � of po�t B 9 #define �db _pb4 // HT16�1 的 RDB 腳 = bit 4 of po�t B 10 #define ldata _pb1 // HT16�1 的 DATA 腳 = bit 1 of po�t B 11 #define ldata_�t�l _1�_1 // bit 1 of po�t B �ont�ol �egiste� 控制 DATA 的輸出 / 輸入 1� 13 void DelayTime(unsigned int �ount) // 10 * �ount + 11� if �ount > ��6 14 { 1� w�ile( �ount != 0 ) �ount-- ; 16 } 17 void nop(void) // 呼叫此函式會用掉 � 個指令週期 18 { 19 asm(“NOP") ; �0 } �1 void SendCommand(unsigned ��a� �md) // 送出命令給 HT16�1 �� { �3 unsigned int CmdCode ; �4 int idx ; �� // 將命令識別碼 100b 及命令碼組成 1� bit �6 CmdCode = ((unsigned int)100b << 9) | ((unsigned int)�md <<1) ; �7 ldata_�t�l = 0 ; // 設定 po�t B bit 1 (DATA) 為輸出型態 �8 �sb = 0 ; // CSB 拉 low �9 fo� ( idx = 11 ; idx >= 0 ; idx-- ) 30 { 31 if( (CmdCode & (1 << idx)) != 0 ) // 檢查各位元是 1 或 0� 從 bit11 ~ bit0 3� ldata = 1 ; // 寫出 1 33 else 34 ldata = 0 ; // 寫出 0 3� 36 w�b = 0 ; // WRB -> low -> �ig� 37 _delay(�) ; // 延遲 � 毫秒 (� inst�u�tion �lo�k) 38 w�b = 1 ; 39 _delay(�) ; 40 } 41 �sb = 1 ; // CSB 拉高 � 結束送出命令 4� } 43 // 將 odata 的 bit0 ~ bit3 換位置 � 改為 bit3 ~ bit0. 例如 � 將 1110 改為 0111 44 ��a� SwapBit(��a� odata) 4� { 46 Rev. 1.30 ��a� ndata ; 52 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 47 int k ; 48 ndata = 0 ; 49 fo�( k = 0 ; k < 4 ; k++ ) �0 ndata |= ((odata >> k) & 0x1) << (3- k) ; �1 �etu�n (ndata) ; �� } �3 // 將資料 dataN 的最低 4 個位元 寫到 LCD RA� 的 add� 位址 (bit0~bit� 有效 ) �4 // bit 0� 1� �� 3 of dataN 是 �om0� �om1� �om�� �om3 的資料 �� void W�iteLCD(��a� add�� ��a� dataN) �6 { �7 unsigned int DataCode ; // 16 bits �8 ��a� NewData ; �9 int idx ; 60 DataCode = ((unsigned int)101b << 6) | (add� & 0x3f) ; // 將 add� 6 個低位元併入 DataCode 61 NewData = SwapBit(dataN) ; // 將 dataN 的 bit0~bit3 換位置為 bit3~bit0 6� DataCode = (DataCode <<4) | NewData ; // 將 dataN 四個低位元併入 63 ldata_�t�l = 0 ; // 設定 po�t B bit 1 (DATA) 為輸出型態 64 �sb = 0 ; // CSB 拉 low 6� fo� ( idx = 1� ; idx >= 0 ; idx-- ) // 送出 13 個位元 66 { 67 if( (DataCode & (1 << idx)) != 0 ) // 檢查各位元是 1 或 0� 從 bit1�~ bit0 68 ldata = 1 ; // 寫出 1 69 else 70 ldata = 0 ; // 寫出 0 71 7� w�b = 0 ; // WRB -> low -> �ig� 73 _delay(�) ; // 延遲 � 毫秒 (� inst�u�tion �lo�k) 74 w�b = 1 ; 7� _delay(�) ; 76 } 77 �sb = 1 ; // CSB 拉 �ig�� 結束送出命令 78 } 79 // 從 LCD RA� 的 add� 位址 (bit0~bit� 有效 ) 讀出一個 nibble (4 bits) 傳回 80 // 回傳值的 bit 0� 1� �� 3 是 �om0� �om1� �om�� �om3 值 81 ��a� ReadLCD(��a� add�) 8� { 83 unsigned int DataCode ; // 16 bits 84 ��a� �et�ode ; 8� int idx ; 86 87 DataCode = ((unsigned int)110b << 6) | (add� & 0x3f) ; // 將 add� 6 個低位元併入 DataCode Rev. 1.30 88 ldata_�t�l = 0 ; // 設定 po�t B bit 1 (DATA) 為輸出型態 89 �sb = 0 ; // CSB 拉 low 90 fo� ( idx = 8 ; idx >= 0 ; idx-- ) // 送出 9 個位元 91 { 53 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 9� if( (DataCode & (1 << idx)) != 0 ) // 檢查各位元是 1 或 0� 從 bit8~bit0 93 ldata = 1 ; // 寫出 1 94 else 9� ldata = 0 ; // 寫出 0 96 97 w�b = 0 ; // WRB -> low -> �ig� 98 _delay(�) ; // � us 的延遲 (� inst�u�tion �lo�k) 99 w�b = 1 ; 100 _delay(�) ; 101 } 10� // 讀出 4 個位元 103 ldata_�t�l = 1 ; // 設定 po�t B bit 1 (DATA) 為輸入型態 104 �et�ode = 0 ; 10� fo�( idx = 0 ; idx < 4 ; idx++ ) // idx=0 讀出 D0� idx=1� 讀出 D1� … 106 { 107 �db = 0 ; // RDB 拉 low 108 _delay(�) ; 109 �db = 1 ; // RDB 拉 �ig� 110 if( ldata == 1 ) �et�ode |= (1 << idx) ; // 讀一個位元 � 如果是 1 111 } 11� �sb = 1 ; // CSB 拉 �ig�� 結束 : 送出命令 113 �etu�n �et�ode ; 114 } 11� // 將 LCD RA� 的內容清為 0 116 void CleanLCD(void) 117 { 118 ��a� seg ; 119 fo�( seg = 0 ; seg < 3� ; seg++ ) 1�0 W�iteLCD(seg� 0) ; 1�1 } 1�� // 將 LCD 全部點亮 1�3 void DisplayLCD(void) 1�4 { 1�� ��a� seg ; 1�6 fo�( seg = 0 ; seg < 3� ; seg++ ) 1�7 1�8 W�iteLCD(seg� 0xf) ; // 點亮 �om0� �om1� �om�� �om3 } 1�9 130 void main(void) 131 { 13� ��a� DataR ; 133 _pb = 0 ; // 埠 B 資料為 0 134 �sb = 1 ; // CSB 拉 �ig� - disable LCD 13� _pb� = 0 ; // 設定埠 B 為輸出 136 w�ile(1) // 無限迴圈 � 只有當執行錯誤才會跳出迴圈 137 { 138 Rev. 1.30 SendCommand(C�D_NOR�AL) ; // 送出 NOR�AL 命令 54 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 139 SendCommand(C�D_SYSEN) ; // 送出 SYS EN 命令 140 SendCommand(0x�9) ; // 設定 LCD 為 4 �ommon� 1/3 bias 141 SendCommand(C�D_LCDON) ; // tu�n LCD on 14� CleanLCD( ) ; // 將 LCD RA� 清為 0� 所有點皆不亮 143 DisplayLCD( ) ; // 點亮所有點 � 寫 0x0f 到 LCD RA� 144 14� DataR = ReadLCD(4) ; // 讀出第 segment 4 的資料 146 if ( DataR != 0x0f ) // 讀出的與寫入的不同 � e��o� 147 b�eak ; // 跳出迴圈 148 SendCommand(C�D_LCDOFF) ; // 送出 LCD OFF 命令 149 1�0 } } 6.5.7 程式說明 �~6 定義 HT16�1 命令的命令碼常數 . 即命令 100 之後的 8 個位元 7 ~ 11 定義 HT48R10A-1 的輸出輸入埠 連接到 HT16�1 的引腳 17~�0 函式 nop() 會使用 � 個指令週期 � 可根據系統頻率使用此函式做時間延遲 �1~4� 函式 SendCommand 是將命令傳送到 HT16�1� 參數 �md 是命令碼 �6 100b 是命令 ID� 三個位元往左移九位到位元 11~9� 加上輸入的參數 �md 左移一位放到位元 8 ~ 1� 共八個位元 � 最後的位元 0 則不管何值皆可 � 此例存放 0 �7 設定 DATA 引腳為輸出型態 �8 CSB 引腳拉到低電位 �9~40 輸出 1� 位元的命令到 HT16�1� 從位元 11 開始 31~34 如果位元的值是 1 則輸出 1� 是 0 則輸出 0 36~39 將 WRB 控制腳拉低電位 � 再拉到高電位 � 將上述的資料寫到 HT16�1 可使用內建函式 _delay(�)� 延遲 � 毫秒 . 時間要根據微控制器的頻率決定 41 CSB 引腳拉到高電位 � 結束命令的發送 44~�� 函式 SwapBit 是將輸入參數的位元 0~3 移換到位元 3~0. 例如將 1110 改為 0111 主要 是將資料部分改成 HT16�1 的格式 � 從 D0�D1�D��D3 的順序送出 49 總共四個位元做換位 �0 位元 0 移到位元 3� 位元 1 移到位元 �� 位元 � 移到位元 1� 位元 3 移到位元 0 ��~78 將輸入參數 dataN 的最低四個位元寫到 HT1�61 RA� 的位址 add� 中 add� 只有 bit0~bit� 是有效的位址 . DataN 的位元 0~3 是 �om0� �om1� �om�� �om3 的資料 60 變數 DataCode 是要輸出的命令 . 先將命令 ID� 101b� 左移 6 個位元再加上 add� 的低 6 個位元所組成的 61 將輸入參數 dataN 的 bit0~bit3 換到位置 bit3~bit0. 按照命令的格式 6� 將第 60 行的 DataCode 左移 4 個位元 � 再加上第 61 行的資料 . 產出要寫入 HT16�1 RA� 的資料 63 設定埠 B 的位元 1 (DATA 引腳 ) 為輸出型態 64 CSB 拉為低電位 6�~78 將 13 個位元 � bit 1� 到 bit 0� 寫到 HT16�1 的 RA� 67~70 如果位元的值是 1 則輸出 1� 是 0 則輸出 0 7�~7� 將 WRB 引腳拉到低電位 � 延遲時間 � 再拉到高電位 � 以寫出此位元 77 CSB 拉到高電位 � 結束送出命令 81~114 函式 ReadLCD 是從 HT16�1 RA� 的位址 add� 中讀出一個位元組 add� 只有最低的 6 個位元 (bit 0 ~ bit �) 是有效的 . 回傳值只有最低的四個位元有效 87 Rev. 1.30 產出輸出的數值 DataCode� 由命令識別碼 110b 左移 6 位元 � 加上 add� 的最低 6 位元 55 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 88 設定 埠 B 位元 1 (DATA 引腳 ) 為輸出型態 89 CSB 拉到低電位 90 發送 9 個位元 � 包括識別碼 110 及 ___________6 個位元的位址 9�~9� 檢查各位元是 1 或 0� 將之送出 97~100 WRB 引腳拉到低電位 � 再拉到高電位 � 完成傳送的動作 103 設定 po�t B bit 1 (DATA) 為輸入型態 10� 讀出四個位元 . idx=0 讀出 D0� idx=1� 讀出 D1� … 107~109 RDB 拉到低電位 � 等候 � 再拉到高電位 110 將讀到的位元儲存到變數 et�ode |= (1 << idx) ; // 讀一個位元 � 如果是 1 11� CSB 拉到高電位 . 結束及送出命令 113 傳回 �et�ode 116~1�1 函式 CleanLCD 將 LCD RA� 的內容清除為 0� LCD 不亮 119 從第一個 segment (seg = 0) 開始清除為 0� 直到第 3� 個 segment 1�0 寫 0 到一個 segment. 清除為 0 1�3~1�8 函式 DisplayLCD 將 LCD RA� 皆寫入 1� 點亮所有 �ommon 與 segment 1�6 從第一個 segment 開始到第 3� 個 segment 1�7 寫 1 到 LCD RA� segment 的所有 �ommon 內 130~1�0 主函式 . 功能是控制 HT16�1 液晶顯示器 � 從開啟 � 熄滅 � 點亮到關閉的動作檢查對 HT16�1 RA� 的讀寫動作是否正確 133 埠 B 資料為 0 134 CSB 拉到高電位 13� 設定埠 B 為輸出型態 136 無限迴圈 � 會重複做 138 行到 148 行的動作 . 只有當執行錯誤才會跳出迴圈 138 送出 NOR�AL 命令到 HT16�1 139 送出 SYS EN 命令到 HT16�1� 系統致能 140 設定 LCD 為 4 �ommon 及 1/3 bias 141 開啟 LCD 14� 將 LCD RA� 清為 0� 所有點皆不亮 143 點亮所有點 � 寫 0x0f 到 LCD RA� 14� 讀出 segment 4 的資料 將 HT16�1 除能 146~147 若讀出的資料與寫入的不同 � e��o�. 跳出無限迴圈 148 送出 LCD OFF 命令 6.6 HT48 微控制器控制 LCD 模組的顯示 6.6.1 目的 利用 HT48 系列微控制器來控制 DV16100NRB 液晶顯示驅動器 � 此 LC� 模組由內置的 Hita��i HD44780 進行驅動及控制 . 本範例是以 C 語言的程式讓微控制器產生正確的訊號以符合 LC� 所需的時序並在 LC� 上顯示文字 . 本範例的一些函式可當作公用函式 � 凡使用此類 LC� 做顯 示的應用皆可呼叫這些函式 . 詳細的指令及時序資料請查閱 LC� 相關資料 . Rev. 1.30 56 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.6.2 周邊元件 使 用 HT48R10A-1 微 控 制 器 去 控 制 LC� 顯 示 文 字 . 它 的 輸 出 / 輸 入 埠 PB0~PB7 連 接 到 HD44780 的 D0~D7� 藉以傳送資料 . 埠 C 用來控制 LC�� PC0 連接 E 腳控制 LC� 的致能 (E=1) 與除能 (E=0). PC1 連接 R/W� 控制對 LC� 的讀 (R/W=1) 與寫 (R/W=0). PC� 連接 RS� 控制 LC� 暫存器的選擇 � 指令暫存器 (RS=0) 或資料暫存器 (RS=1). 如果應用電路使用其他的 輸出 / 輸入埠與 HD44780 相連 � 則只要修改程式中 #define 的敘述 � 其他則不用修改 HT48R10A-1 腳位 HD44780 腳位 PC0 E PC1 R/W 讀取 (=1)� 寫入 (=0) PC� RS LC� 暫存器的選取 � =0 指令暫存器 �=1 資料暫存器 PB0~PB7 D0 ~ D7 說明 致能控制 � E=1( 致能 )� =0 ( 除能 ) 資料線 → 在每次對 HD44780 做讀寫動作前 � 必須先檢查 LCD 是否仍然忙線 (busy). 可透過讀取忙碌 旗號 (BF) 和位址 (AC) 指令去讀出指令暫存器 (IR) 的內容 (RS=0� R/W=1) → LCD 讀取一個位元組 (byte) 時 � 流程如下 設定 RS = 1 如從資料暫存器讀出 , = 0 如果從指令暫存器讀出 設定 R/W = 1 設定 E = 1, 將 LCD 致能 從 D0~D7 讀出資料並存放 設定 E = 0, 將 LCD 除能 → (byte)d�11 資料寫入 LCD 時 � 流程如下 設定 RS = 1 如果是寫入資料暫存器 , = 0 如果是寫入指令暫存器 設定 R/W = 0 設定 E = 1, 將 LCD 致能 將資料放到 D0~D7 設定 E = 0, 將 LCD 除能 6.6.3 電路圖 使用微控制器去控制 LCM 顯示文字 . 它的輸出 / 輸入埠為 PB0~PB7 Rev. 1.30 57 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.6.4 微控制器的架構設定 (configuration option) WDT �lo�k sou��e : disable OSC : Ext. C�ystal Pull-�ig� PA : Non-Pull-�ig� Pull-�ig� PB : Non-Pull-�ig� Pull-�ig� PC : Non-Pull-�ig� Input type PA : S��mitt T�igge� BZ/BZ : Disable fSYS = 4� 6.6.5 程式流程 開始 1. 設定 PC0/PC1/P�� 為輸出型態 �. 設定 LCD 功能 : 8 位元控制 3. LCD ON/OFF 設定 : 顯示所有資料 4. 設定進入模式 : AC 自動加一 �. 清除顯示器 � 位址計數器為 0 LCD initialization 設定位址計數器為第一行 從`A'到`p'寫入 LCD DD RA� 設定位址計數器為第二行 從`a' 到 `p' 寫入 LCD DD RA� 結束 6.6.6 原始程式 1 #in�lude “�t48�10a-1.�" � #define L�dData _pb // LCD D0~D7 = PB0~PB7 3 #define L�dDataCt�l _pb� // PB 埠的控制暫存器 � data 為輸出或輸入 4 #define L�dE _p�0 // E 控制腳位 = PC0 � #define L�dRW _p�1 // R/W 控制腳位 = PC1 6 #define L�dRS _p�� // RS 控制腳位 = PC� 7 #define L�dPo�tCt�l _p�� // PC 埠的控制暫存器 8 #define C�D_READ 1 // R/W 常數 � 讀 9 #define C�D_WRITE 0 // R/W 常數 � 寫 10 #define REG_IR 0 // RS 常數 � 指令暫存器 IR 11 #define REG_DR 0 // RS 常數 � 資料暫存器 IR 1� #define ENABLE 1 // E 常數 � 致能 LCD 13 #define DISABLE 0 // E 常數 � 除能 LCD 14 // 檢查 LCD 是否仍然忙碌 � 若是則會一直檢查到不忙碌 1� // 若 LCD 故障是否會造成無窮迴圈 ?? 16 void C�e�kLCDBusy(void) 17 { 18 Rev. 1.30 L�dDataCt�l = 0xff ; // 設定 DATA 為輸入型態 58 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 19 L�dRS = REG_IR ; // 選定 指令暫存器 (IR) �0 L�dRw = C�D_READ ; // Read 動作 �1 L�dE = ENABLE ; // LCD enable �� w�ile( (L�dData & 0x80) != 0 ) ; // BF=1 是仍忙碌 � 一直檢查 �3 �4 // LCD 不忙碌 �� L�dE = DISABLE ; �6 } �7 // 寫資料到 LCD 指令暫存器 (IR) 或資料暫存器 (DR) �8 // 輸入參數 �egflag = 1 寫入資料暫存器 (DR)� =0 寫入指令暫存器 (IR) �9 // wdata = 寫入的資料 30 void W�iteToLCD(��a� �egflag� ��a� wdata) 31 { 3� C�e�kLCDBusy() ; // 檢查及等候 LCD 不忙碌 33 L�dRS = �egflag ; // 選定暫存器 IR/DR 34 L�dRW = C�D_WRITE ; // 寫入動作 3� L�dDataCt�l = 0 ; // 設定 DATA 為輸出型態 36 L�dData = wdata ; // 將資料放入 DATA 37 L�dE = ENABLE ; // 致能 LCD 38 L�dE = DISABLE ; // 不需做延遲 � �ig� -> low 完成寫入 39 } 40 // 主函式 41 void main(void) 4� { 43 L�dPo�tCt�l = 0 ; // 3 個 LCD 控制腳位設定為輸出型態 44 W�iteToLCD(REG_IR� 0x38) ; // 設定功能 � 8 位元控制 � 雙行顯示 � �*7 點矩陣 4� W�iteToLCD(REG_IR� 0x0f) ; // ON/OFF 控制 � 顯示所有資料及游標 � 游標閃爍 46 W�iteToLCD(REG_IR� 0x06) ; // 進入模式 � 外部讀寫資料後 � 位址記數器 AC 47 // 會自動加一 � 游標會向右移動 48 W�iteToLCD(REG_IR�0x01) ; // 清除顯示器 � 位址計數器設定為 0� I/D=1 49 W�iteToLCD(REG_IR� 0x80) ; // DD RA� 位址設定 � 位址計數器設為 0 �0 // 游標設在第一行 � 第一個位址 �1 // 開始將顯示的字寫入 LCD DD RA� 讓 LCD 顯示出 �� fo�( k = 0� ��a�1 = `A' ; k < 16 ; k++ ) // 一行可顯示 16 個字 � 從`A'開始 �3 { �4 W�iteToLCD(REG_DR� ��a�1) ; // 顯示 ��a�1 中的字 � 從`A'開始 �� ��a�1++ ; // 下一個字母 �6 } �7 W�iteToLCD(REG_IR� 0xC0) ; // DD RA� 位址設定 � 位址計數器設為 0x40 �8 // 游標設在第二行 � 第一個位址 �9 fo�( k = 0� ��a�1 = `a' ; k < 16 ; k++ ) // 一行可顯示 16 個字 � 從`a'開始 60 { 61 W�iteToLCD(REG_DR� ��a�1) ; // 顯示 ��a�1 中的字 � 從`a'開始 6� ��a�1++ ; // 下一個字母 63 } 64 6� Rev. 1.30 W�iteToLCD(REG_IR� 0x08) ; // ON/OFF 控制 � LCD 不顯示 } 59 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.6.7 程式說明 �~3 定義埠 B 為資料輸出 / 輸入埠 � 與 HD44780 引腳相連接 4~7 定義埠 C 為控制訊號線 � 與 HD44780 的控制引腳相連接 8~13 定義常數給控制指令使用 � 主要目的是讓程式容易閱讀 16~�7 函式 C�e�kLCDBusy 檢查 LC� 是否空閒 � 若仍忙碌則會等到空閒才返回 18 設定 DATA (D0 ~D7) 為輸入型態 19 選定 指令暫存器 (IR) �0 選定讀取 (Read) 動作 �1 激活 LC� 功能 (enable) �� 迴圈 : 從 LC� 讀取資料 � 檢查 BF 位元是否為 1 ( 忙碌中 ). 如果是則繼續讀取及檢查 . 直到 LC� 不再忙碌 . 若要更嚴謹的作法 � 應該要有次數或時間的限制 . 在合理的時間 或次數後 � 若仍然忙碌 (LC� 出現某種錯誤 ) 則應回覆錯誤碼 �� 停止 LC� 功能 30~39 函式 W�iteToLCD 將資料 wdata 寫入指令暫存器 (IR� �egflag=0) 或是資 料暫存器 (DR� �egflag=1) 3� 檢查 LCD 是否空閒 � 等候 LCD 不忙碌 33 選定暫存器為 IR 或 DR 34 寫入動作 3� 設定 DATA 的埠為輸出型態 36 將資料放入 DATA (D0~D7) 線 37 將 LCD 激活引腳 (E) 拉到高電位 38 將 LCD 激活引腳 (E) 拉到低電位 . 不需做延遲 � �ig� -> low 完成寫入 41~6� 主函式 Rev. 1.30 43 將 3 個 LCD 控制引腳設定為輸出型態 44 設定 LCD 功能 -> 8 位元控制 � 雙行顯示 � �*7 點矩陣 4� 設定 LCD 的 ON/OFF 控制 -> 顯示所有資料及游標 � 游標閃爍 46 設定 LCD 的進入模式 -> 外部讀寫資料後 � 位址記數器 AC 會自動加一 � 游標會向右移動 48 清除顯示器 � 位址計數器設定為 0� I/D=1 49 DD RA� 位址的設定 -> 位址計數器設為 0� 游標設在第一行 � 第一個位址 �� 開始將顯示的字元寫入 LCD DD RA� 讓 LCD 顯示出 � 一行可顯示 16 個字從`A'開始 �4 將字元碼寫入 LCD RA�� 顯示此字元 �� 下一個字元 �7 將 DD RA� 的位址設定為 0x40� 代表將游標設在第二行 � 第一個位址 �9 在第二行顯示從 `a' 開始的 16 個字元 61 將需要顯示的字元碼寫入 LCD RA� 6� 下一個字元 64 LCD ON/OFF 控制 � LCD 不顯示 60 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.7 具 LCD 驅動功能的微控制器之顯示應用程式 – HT46R63 本節將以 HT46R63 為本說明如何使用 C 語言程式控制 LCD 驅動器以便將數字顯示在面板上 . 可以使用 HT-IDE3000 的 LCD simulato� 去模擬 LCD 面板 (panel)� 不需實際製作 LCD 面板 . 請參閱 HT-IDE300 使用手冊中的 LCD Simulato� 章節 6.7.1 目的 使用 C 語言撰寫程式去控制 HT46R63 的 LCD 驅動器以便將數字顯示在 LCD 面板上 . 顯示的 數字為八位元時鐘計時的數值 � 以秒為單位 . 6.7.2 周邊元件 控制 LCD 各點 (pixel� CO�x/SEGy) 是否顯示的資料是儲存在 HT46R63 的 LCD 記憶體 . 此記 憶體是位於微控制器的 Bank 1 資料記憶體 � 位址從 40H 到 �3H.SEG�ENT� CO��ON 與位址 的關係如下圖 Display Memory 當寫 1 到位址的位元時 � 其對應的 SEGx/CO�y 的點將會顯示 在開發應用程式及偵錯的過成中 � 可以不接上真實的 LCD 面板 (panel)� 而是使用 HT-IDE3000 LCD Simulato� 提供的模擬系統 � 在電腦螢幕上模擬 LCD 面板 . 在設計程式之前 � 先設定 LCD 面板的架構 . 本範例使用 l�ddemo.l�d 當做面板的架構檔 � 直接 從 HT-LCDS 軟體開啟此檔案 � 將出現如下圖的畫面 Rev. 1.30 SEG0 SEG1 SEG� SEG3 SEG4 SEG� SEG6 SEG7 SEG8 40H 41H 4�H 43H 44H 4�H 46H 47H 48H COM0 1e 1f — �e �f — 3e 3f — COM1 1d 1� — �d �� — 3d 3� — COM2 1b 1a 1g �b �a �g 3b 3a 3g 61 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 圖的上半是 LCD 面板的圖樣 � 根據程式寫入 LCD RA� 的控制資料來顯示數字 � 從 0 到 9. 數 字最多由七段組成 � 每段則由對應的 SEG/CO� 點 (pixel) 所控制 . 面板上的圖樣是從 bmp 格 式的圖檔取得 � 在設定 LCD 架構時必須指定 . 在圖下方的表格內則是定義 SEG/CO� 與各段之間的對應關係 . 例如位址 40H� 41H� 4�H 等三 個位元組控制第一個七段圖樣的顯示 � 此範例使用 3 個 �ommon� 所以每個位元組最多只能三個 位元是有效的 . 範例將以固定的延遲時間累加數字 � 再將之顯示於面板 . 數字的個位數字顯示在面板的第三個圖 樣 (3a~3g)� 十位數字顯示於第二個圖樣 (�a~�g)� 百位數字顯示於第一個圖樣 (1a~1g). 6.7.3 電路圖 HT46R63 微控制器 6.7.4 微控制器的架構設定 (configuration option) HT46R63 的架構設定 � 需要選定的如下 � 其他可以 disable 或用 default LCD duty: 3 CO� LCD segment: �0 segments SEG7-SEG10: LCD output SEG11-SEG14: Logi�al output SEG1�-SEG18: Logi�al output 6.7.5 程式流程 開始 清除 LCD RA� ( 熄滅 LCD) 迴圈 � 從 0 到 999 針對每個數 � 將十位數字 � 百位數字及個位數字顯示於 LCD 上 結束 Rev. 1.30 62 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.7.6 原始程式 1 #in�lude “�t46�63.�" � // 定義數字 `0' ~ `9' 的 LCD RA� 資料 � 7 個位元控制各段的顯示 3 // 圖樣各段 ga�f bd� 4 ��a� digit[10] = { 0b01111101� 0b00011000� 0b01110011� 0b01111010� //`0'�`1'�`�'�`3' � 0b00011110� 0b01101110� 0b01101111� 0b00111000� // `4'�`�'�`6'�`7' 6 0b01111111� 0b01111110 } ; // `8'� `9' 7 ��a� L�dRam[�0] @ 0x140 ; // LCD RA� 記憶體 8 void DelayTime(unsigned int �ount) // 10 * �ount + 11� if �ount > ��6 9 { 10 w�ile( �ount != 0 ) �ount-- ; 11 } 1� // add� (IN) = LCD RA� 位址 = 0x140+x 13 // datal = 寫入 LCD RA� 的資料 � bit0~� 寫入 add�� bit3~� 寫入 add�+1 14 // bit 6 寫入 add�+� 的 bit� 1� void DisplayL�d(unsigned ��a� add�� unsigned ��a� datal) 16 { 17 L�dRam[add� - 0x140] = datal & 0x7 ; // 取位元 0 ~ � 18 L�dRam[add� - 0x140+1] = (data1 >> 3) & 0x7 ; // 取位元 3~� 19 L�dRam[add� – 0x140+�] = ((data >> 6) & 0x7) << � ; // 取位元 6� 在左移 � 位 �0 } �1 // 主函式 �� void main(void) �3 { �4 int k� �ount ; �� �6 fo�( k=0 ; k < �0 ; k++ ) L�dRam[k] = 0 ; // 將 LCD 記憶體清為 0� LCD 不顯示 �7 fo�( �ount=0 ; �ount < 1000 ; �ount++ ) // 顯示 數字從 0 到 999 �8 { �9 k = (�ount /100) % 10 ; // 取出百位數 30 DisplayL�d(0x146� digit[k]) ; 31 k = (�ount / 10) % 10 ; // 取出十位數 3� DisplayL�d(0x143� digit[k]) ; // 顯示數字 33 k = �ount % 10 ; // 取出個位數 34 DisplayL�d(0x140� digit[k]) ; 3� _delay(��0000) ; // 延長 1 秒 36 _delay(��0000) ; 37 _delay(��0000); 38 _delay(��0000) ; 39 40 Rev. 1.30 } } 63 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.7.7 程式說明 4~6 定義 0 到 9 的數字 ¸ 在 LCD RA� 內的控制碼 . 顯示數字 7 定義陣列 L�dRam[�0] 的位址在 RA� bank 1 的 0x40. 這是 LCD RA� 記憶體的啟始 位址 . 要顯示的控制碼必須寫入對應的位址 . L�dRam[0] 的位址是 0x140�L�dRam[1] 的位址是 0x141� …. L�dRam[19] 的位址是 0x1�3 1�~�0 函式 DisplayL�d 將控制碼 datal 各部分寫入 LCD RA� 的位址 add�� add�+1�add�+�. 其中 data1 的位元 0~� 寫入 add� 的位元 0~�� data1 的位元 3~� 寫入 add�+1 的位元 0~�� datal 的位元 6 寫入 add�+� 的位元 � 一旦寫入 LCD RA�� 對應的點就會反應 � 顯示或不顯示圖樣 ��~38 主函式 main �6 將 LCD 記憶體清為 0, LCD 不顯示 27~37 迴圈 , 在 LCD 面板上顯示數字 , 從 0 開始 , 到 999 為止 �9 取出百位數 30 從陣列 digit[] 中取得百位數字的控制碼 � 將 LCD RA� 位址 0x146 與控制碼傳給函式 DisplayL�d 將百位數的數字顯示 31 取出十位數 3� 從陣列 digit[] 中取得十位數字的控制碼 � 將 LCD RA� 位址 0x143 與控制碼傳給函式 DisplayL�d 將十位數的數字顯示 33 取出個位數 34 從陣列 digit[] 中取得個位數字的控制碼 � 將 LCD RA� 位址 0x140 與控制碼傳給函式 DisplayL�d 將個位數的數字顯示 3�~38 _delay 延長顯示時間 1 秒 6.8 顯示器之通用函式 – HD44780 LCM 本節將範例 6.6 的函式做一整理 � 以較容易使用的格式與介面方式 � 提供其他欲使用此 LCD 的 應用程式一些公用函式 6.8.1 initialLCD( ) 定義常數 � 於初始設定 LCD 的特性時 � 以這些常數的組合值傳入函式設定 LCD 如特性未被定 義如下 � 則是其固有值 � 若選定此特性 � 則不需設其值 (default=0) #define LCD_�ODE_RIGHT 0x� // ent�y mode: I/D=1 游標向右移 #define LCD_�ODE_�OVE 0x1 // ent�y mode: S=1 顯示器會跟著移動 #define LCD_FUNC_�X10 0x04 // F=1 �*10 點矩陣 #define LCD_FUNC_�LINE 0x08 // N=1 雙行顯示 #define LCD_FUNC_8 0x10 // DL=1 八位元控制 (DB7~DB0) #define LCD_DISP_B 0x01 // ON/OFF 控制 : B=1 游標閃爍 #define LCD_DISP_C 0x0� // C=1 顯示游標 #define LCD_DISP_D 0x04 // D=1 顯示所有資料 // 函式的輸入參數及其定義 // fun�set : fun�tionset 命令 � bit 4�3�� = DL� N� F // DL=1 使用四位元 (DB7~DB4) 控制模式 // =0 使用八位元 (DB7~DB0) 控制模式 // N=0 單行顯示 // =1 雙行顯示 // F=0 �*7 點矩陣字型 // =1 �*10 點矩陣字型 // dispon : Display ON/OFF 命令 � bit �� 1�0 = D� C� B // D =0 所有資料不顯示 // =1 顯示所有資料 // C =0 不顯示游標 // =1 顯示游標 // B =0 游標不閃避 Rev. 1.30 64 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 // =1 游標閃避 // ent�ymode : 進入模式命令 bit 1� 0 = I/D� S // I/D =0 當資料寫入 DD RA� 或從 DD RA� 讀出後 � 位址計數器 (AC) 會 // 減一 � 因此游標會向左移 // =1 當資料寫入 DD RA� 或從 DD RA� 讀出後 � 位址計數器 (AC) 會 // 加一 � 因此游標會向右移 // S =1 當資料寫入 DD RA� 後 � 整個顯示器會向左移 ( 若 I/D=0) 或向右移 // ( 若 I/D=1)� 但從 DD RA� 讀取資料時 � 顯示器不會移動 // =0 顯示器不會移動 void InitialLCD(��a� fun�set� ��a� dispon� ��a� ent�ymode) { char bco de ; LcdPortCtrl = 0 ; // 3 個 LCD 控制腳位設定為輸出型態 bcode = (funcset & 0x1C) | 0x20 ; // 取 funcset 的位元 2, 3, 4 (functionset 指令 ) WriteToLCD(REG_IR, bcode) ; // 功能設定 , 4/8 位元 , 單 / 雙行顯示 , 5*7/5*10 點矩陣 bcode = (dispon & 0x7) | 0x08 ; // ON/OFF 控制 , dispon 位元 0,1,2 是 ON/OFF 的 0,1,2 WriteToLCD(REG_IR, bcode) ; // ON/OFF 控制 , 顯示所有資料及游標 , 游標閃爍 bcode = (entrymode & 0x03) | 0x04 ; // entry mode, entrymode 位元 1, 0 WriteToLCD(REG_IR, bcode) ; // 進入模式 , 外部讀寫資料後 , 位址記數器 AC 會變 WriteToLCD(REG_IR,0x01) ; // 清除顯示器 , 位址計數器設定為 0, I/D=1 WriteToLCD(REG_IR, 0x80) ; // DD RAM 位址設定 , 位址計數器設為 0 // 游標設在第一行 , 第一個位址 } 6.8.2 PutChar( ) 將一個字母寫入 LCD DD RA� 做顯示 void PutC�a�(��a� bdata) { W�iteToLCD(REG_DR� bdata) ; // 寫入 LCD DD RA� } 6.8.3 MoveAt( ) 將游標移到指定的 LCD 位置 . 亦即將位址計數器 (AC) 改為新的位址 . 位置以 x� y 表示第 y 行 的第 x 位置 (x = 0~19� y=0~3) 一行最多 �0 字 � 最多可顯示四行 void �oveAt(��a� x� ��a� y) { ��a� bdata ; bdata = x + ((y & �)==0 ? 0 : �0) + ((y&1)==0 ? 0 : 0x40) + 0x80 ; W�iteToLCD(REG_IR� bdata) ; } 6.8.4 main( ) void main(void) { char funcset, disp, entrymode ; funcset = LCD_FUNC_2LINE | LCD_FUNC_8 ; // 雙行顯示 , 8 位元傳送 entrymode = LCD_MODE_RIGHT ; disp = LCD_DISP_B | LCD_DISP_C | LCD_DISP_D ; InitialLCD(funcset, disp, entrymode) ; // initialize LCD } Rev. 1.30 65 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.9 鍵盤掃瞄程式 6.9.1 目的 本範例介紹如何使用輸出 / 輸入埠支援一個 4*4 的鍵盤陣列 . 每一個按鍵皆賦予唯一的十六進 位數值 � 當一個按鍵被按下後 � LED 會顯示此按鍵所賦予的數值 � 以二進制顯示 . 如果同時有數 個按鍵按下時 � 只會顯示第一個被掃瞄到的按鍵數值 .C 語言方面使用 swit��/�ase 的敘述格式 以取代多個 if/else if 的格式 � 增加程式的可讀性 . 6.9.2 周邊元件 十六個按鍵連接到埠 A� PA0~PA7. 四個 LED 連接到埠 B 的 PB0~PB3 輸出 / 輸入埠 PA 的 PA0~PA3 設為輸出口 � 輸出掃瞄碼 (s�an �ode)� PA4~PA7 設為輸入口 � 輸 入按鍵碼 (key �ode). 當程式掃瞄到被按下的按鍵時 � 再經由連接到 PB 的 LED 顯示此按鍵值 . 掃瞄方式是從第一列第一行開始 � 先找出被按下之按鍵所在的列 � 再找出所在的行 . 經由 PA0~PA3 送出掃瞄碼 � 目標列送出 0� 其他列則送出 1� 例如先找尋第一列 � 則從 PA0~PA3 送 出 0111� 再從 PA4~PA7 讀入 � 若其值不為 1111� 則表示此列中有一個按鍵被按下 � 若 0 出現在 PA� 則表示第二行有按鍵被按下 . 因此按下的鍵是在第一列 � 第二行 . 6.9.3 電路圖 6.9.4 微控制器的架構設定 (configuration option) HT48R10A-1 的 �onfigu�ation option 設定 WDT �lo�k sou��e : disable OSC : Ext. C�ystal Pull-�ig� PA : Pull-�ig� Pull-�ig� PB : Pull-�ig� Input type PA : S��mitt T�igge� BZ/BZ : Disable fSYS = 4� Rev. 1.30 66 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.9.5 程式流程 開 始 設定PB, PA0~PA3 為輸出型態 設定 PA4~PA7 為輸入型態 迴圈: 輸出 各列的掃描碼 從第一列開始到第四列 是 超過第四列 不是 從埠A 讀入按鍵碼 沒有 某列中有按鍵 有 輸出到埠B, 將按鍵對應的 LED點亮 Rev. 1.30 67 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6.9.6 原始程式 1 #in�lude “�t48�10a-1.�" � ��a� s�an_�ode[4] = { 0xfe� 0xfd� 0xfb� 0xf7 } ; 3 void main(void) 4 { � ��a� key_�ode� pos ; 6 int �ow ; 7 _pb� = 0 ; // 設定 埠 B 為輸出型態 � 以便點亮 LED 8 _pa� = 0xf0 ; // 設定 PA0~PA3 是輸出型態 � PA4 ~ PA7 是輸入型態 9 _pa = _pb = 0 ; // 清除 PA� PB 10 s�an_l: 11 fo�( �ow = 0 ; �ow < 4 ; �ow++ ) // 從第一列開始掃描 1� { 13 _pa = s�an_�ode[�ow] ; // 輸出此列的掃瞄碼 14 key_�ode = _pa ; // 從 PA 讀入按鍵碼 1� if( (key_�ode ^ s�an_�ode[�ow]) != 0 ) // 此列有按鍵被按下 16 { 17 swit�� ( key_�ode & 0xf0 ) // 取最高的 4 位元 (PA7~PA4) 18 { 19 �ase 0xe0 : pos = �ow * 4 ; // PA4=0 �0 b�eak ; �1 �ase 0xd0 : pos = �ow * 4 + 1 ; // PA�=0 �� b�eak ; �3 �ase 0xb0 : pos = �ow * 4 + � ; // PA6=0 �4 b�eak ; �� �ase 0x70 : pos = �ow * 4 + 3 ; // PA7=0 �6 b�eak ; �7 default : // e��o� �8 goto s�an_l ; // 可用其他方式顯示錯誤 �9 } 30 pos ^= 0x0f ; // 將最低四位元轉成以 low 才點亮 31 _pb = pos ; // 點亮 LED 3� b�eak ; // 跳出 fo� 迴圈 33 } 34 } 3� // 沒有按鍵被按下或已完成一次 LED 顯示 36 37 goto s�an_l ; // 從頭開始掃瞄 } 6.9.7 程式說明 Rev. 1.30 � 定義四個掃瞄碼的陣列 s�an_�ode[4] 做為按鍵的掃瞄之用 7 設定 埠 B 為輸出型態 � 以便點亮 LED 8 設定 PA0~PA3 是輸出型態 � PA4 ~ PA7 是輸入型態 9 清除 PA� PB 10 重複執行的起點 11 迴圈 � 從第一列開始掃描 � 共有四列 13 輸出此列的掃瞄碼到埠 A 14 從埠 A 讀入按鍵碼 68 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 1� 檢查此列是否有按鍵被按下 17 有按鍵被按下 � 檢查是那一行的按鍵被按下 // 取最高的 4 位元 (PA7~PA4) 19 第一行被按下 � 計算按鍵的位置為 行數 * 4 �0 跳出 swit�� �1 第二行的按鍵被按下 � 計算其位置為 行數 * 4 + 1 �� 跳出 swit�� �3 第三行的按鍵被按下 � 計算其位置 行數 * 4 + � �4 跳出 swit�� �� 第四行的按鍵被按下 � 計算其位置 行數 * 4 + 3 �6 跳出 swit�� �7~�8 其他情況 � 正常狀況不應該執行到此處 � 但為了避免不正常的情況發生 � 此處為預防之 處 � 設定位置為不正確值 (-1) Rev. 1.30 30 將位置的最低四位元轉成以 low 才能點亮 31 輸出到不 B� 點亮 LED 3� 跳出 fo� 迴圈 . 會到第 36 行 36 沒有按鍵被按下或已完成一次 LED 顯示 . // 從頭開始掃瞄 69 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第七章 應用範例 – 中斷程式 本章介紹以 C 語言程式處理微控制器的中斷事件 . 第四章 4.7 中斷服務函式 中說明如何撰寫中 斷服務函式以及注意事項 . 必須遵照下列的規則來定義中斷服務函式 → 函式的返回值型態必須是 void inte��upt → 函式不可有參數 (a�gument) → 設定中斷服務函式的中斷向量 (inte��upt ve�to�) 使用 @ 指定中斷向量 → 最好不要從程式的其他地方呼叫中斷服務函式 → 不要在中斷服務函式內做開啟本中斷功能 (inte��upt �eent�an�e) 相同的中斷不要重複進入 格式 void interrupt ISR_Time�(void) @ 0x08 { } // 定義 : 返回值的型態 , 沒有參數 , 設定中斷向量為 0x08 C 編譯器會根據中斷服務函式對暫存器的需要 � 在進入中斷服務函式後 � 將這些暫存器的內容儲 存 . 等執行完中斷服務函式的工作後 � 再恢復先前所儲存的暫存器內容 . 最後回要被中斷的地方 繼續執行 � 同時中斷功能也打開 � 允許中斷產生 . 如果為了節省程式編碼的大小 � 在中斷服務函 式中也可不要保存某些暫存器 � 例如 BP� �P0� �P1. 可參考第四章的 4.9 節關於 #p�agma 的說 明及使用 . 在中斷服務函式中不要將自身的中斷功能再打開 � 避免重複進入同一個中斷服務函 式 . 但是可以打開其他的中斷功能 � 只是要特別注意兩個中斷服務函式有無呼叫到相同函式的狀 態 � 以致發生變數共用造成資料錯誤的風險 . 7.1 用時鐘控制 LED 的亮與滅 7.1.1 目的 本範例是使用時鐘控制 LED 亮與滅的時間 . 亮滅的時間相同並且固定 . 是以 C 語言撰寫中斷服 務函式 . 7.1.2 周邊元件 使用單顆 LED 接到微控制器 HT48R10A-1 的埠 A� PA0 引腳需要使用微控制器 HT48R10A-1 的暫存器 INTC� T�RC 與 T�R 等 � 定義如下 T�RC (Time� Cont�ol Registe�) 時鐘控制暫存器 控制時鐘的功能與啟動 � Bit No. Label Function 0~� PSC0~ PSC� To define t�e p�es�ale� stages� PSC�� PSC1� PSC0= 000: fINT=fSYS/� o� fRTC/� 001: fINT=fSYS/4 o� fRTC/4 010: fINT=fSYS/8 o� fRTC/8 011: fINT=fSYS/16 o� fRTC/16 100: fINT=fSYS/3� o� fRTC/3� 101: fINT=fSYS/64 o� fRTC/64 110: fINT=fSYS/1�8 o� fRTC/1�8 111: fINT=fSYS/��6 o� fRTC/��6 3 TE To define t�e T�R a�tive edge of time�/event �ounte� (0=a�tive on low to �ig�; 1=a�tive on �ig� to low) 4 TON � — 6 7 T�0 T�1 To enable/disable time� 0 �ounting (0=disabled; 1=enabled) Unused bit� �ead as "0" To define t�e ope�ating mode 01=Event �ount mode (exte�nal �lo�k) 10=Time� mode (inte�nal �lo�k) 11=Pulse widt� measu�ement mode 00=Unused TMRC(0EH) register Rev. 1.30 70 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Bit No. Label 0 E�I Cont�ols t�e maste� (global) inte��upt (1= enabled; 0= disabled) Function 1 EEI Cont�ols t�e exte�nal inte��upt (1= enabled; 0= disabled) � ETI Cont�ols t�e time�/event �ounte� inte��upt (1= enabled; 0= disabled) 3 — Unused bit� �ead as "0" 4 EIF Exte�nal inte��upt �equest flag (1= a�tive; 0= ina�tive) � TF Inte�nal time�/event �ounte� �equest flag (1= a�tive; 0= ina�tive) 6 — Unused bit� �ead as "0" 7 — Unused bit� �ead as "0" INTC (0BH) register T�R 暫存器是儲存時鐘啟始數值的地方 . 當寫入 T�R 暫存器時 � 也會存入 p�eload 暫存器 . 若 此時時鐘是在停止狀態中 � 則此數值會被寫入時鐘計數器 (time� �ounte�). 每當時鐘被啟動 � 它 便將時鐘計數器的數值往上加 � 一直加到 FF� 之後便發生時鐘滿溢 (time� ove�flow). 這時 � 下列 的事情會發生 → 產生中斷訊號 (inte��upt signal) → 將 p�eload 暫存器中的數值重新載入時鐘計數器 � 並且繼續往上計數 如果需要時鐘在每 �ount 個 �lo�k 時產生滿溢 � 可將 ��6 – �ount 寫入 T�R 暫存器 . 如需要正 確的時間 � 則需要設定根據系統頻率設定 T�RC 暫存器中的 PSC0~PSC�. 計算出對應的 �ount 並將 ��6 – �ount 寫入 T�R 暫存器 如需要產生時鐘中斷並處理之 � 則需要在 INTC 暫存器中打開時鐘的中斷功能 � 將 ETI 設為 1. 當時鐘計數滿溢時 � 就會產生時鐘中斷 � 並跳到中斷向量 08H 處 . 中斷函式會對埠 A� PA0 的 LED 做亮滅的控制 7.1.3 電路圖 將埠 A 的 PA0 連接到 LED 的陰極 7.1.4 微控制器的架構設定 (configuration option) HT48R10A-1 的 �onfigu�ation option 設定 WDT �lo�k sou��e : disable OSC : Ext. C�ystal Pull-�ig� PA : Pull-�ig� Input type PA : S��mitt T�igge� BZ/BZ : Disable fSYS = 4� Rev. 1.30 71 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 7.1.5 程式流程 開始 時鐘中斷處理函式 設定 PA0為輸出型態 設定 INTC 的EMI, ETI 設定 TMRC 為 fSYS/256 與 時鐘模式 毫秒變數加一 小於或等於62 毫秒變數 大於 62 設定 TON, 啟動時鐘 毫秒變數設為 0 斷函式處理 LED 亮與滅 進入無限迴圈, 讓時鐘 中 LED 點亮或熄滅 return 7.1.6 原始程式 1 #in�lude “�t48�10a-1.�" � #define _ton _0e_04 // 暫存器 T�RC 的位元 4� TON 3 ��a� se�_�ount ; // 記錄秒數 4 void inte��upt ISR_TI�ER(void) @ 0x8 // 定義中斷服務函式 ISR 在位址 0x08 � { 6 se�_�ount++ ; // 每 16 毫秒 (ms) 增加一 7 if( se�_�ount > 6� ) // 超過 16*6� = 99� 毫秒 � 一秒 8 { 9 se�_�ount = 0 ; // 重開始 10 _pa0 ^= 0x01 ; // toggle LED� 每一秒 � 輪流亮滅 11 1� } 13 void main(void) 14 { 1� _pa� = 0x01; // 設定 PA0 為輸出型態 16 _pa = 0xff ; // 熄滅 LED 17 _int� = 0x0� ; // 設定 E�I� ETI 致能 . 打開中斷功能 18 _tm�� = 0x87 ; // 設定時鐘控制暫存器 � fint=fSYS/��6� time� mode 19 _tm� = (��6 – ��0) ; // 每 ��0 �lo�k 產生一次時鐘中斷 �0 se�_�ount = 0 ; // 設定初始值 �1 _ton = 1 ; // 啟動時鐘開始計時 �� w�ile(1) ; // 無限迴圈 �3 Rev. 1.30 } } 72 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 7.1.7 程式說明 � 定義變數 _ton 為時鐘控制暫存器 T�RC 的位元 4� TON 控制時鐘的開始或停止 3 定義變數 se�_�ount 記錄秒數 4~1� 定義時鐘中斷服務函式 ISR_TI�ER(void) 放置於位址 0x08 處 6 每 16 毫秒 (ms) 增加一 7 如果累計的毫秒數超過 6� 次 ( 超過 16*6� = 99� 毫秒 � 一秒 ) 9 se�_�ount 重設為 0� 重開始計數 10 toggle LED� 每一秒會輪流為亮滅 1�~�3 主程式 main(void) 1� 將 PA0 設為為輸出型態 � PA0 接到 LED 16 設定 PA0 為 1� 熄滅 LED 17 設定中斷控制暫存器 (INTC) 的 E�I 及 ETI� 讓中斷致能 18 設定時鐘控制暫存器 (T�RC)� fint=fSYS/��6� time� mode� fSYS = 4�Hz� 1 �lo�k=64µs 時 鐘頻率 = 4� /��6� �lo�k = 1/ 時鐘頻率 = 64 µs 19 設定時鐘每隔 ��0 �lo�k� 產生一次時鐘中斷 . ��0 * 64us = 16ms �0 設定初始值 �1 設定 _ton = 1 � 啟動時鐘開始計時 �� 無限迴圈 w�ile(1) 7.2 類比 / 數位轉換 (ADC) 的應用 7.2.1 目的 本範例利用 HT46R63 微控制器的類比 / 數位轉換電路 (analog to digital �onve�te�) 將外界的類 比訊號轉換為數位 � 並從 LCD 面板顯示其值 . 以 C 語言撰寫 ADC 中斷服務函式 (ISR) 及 LCD 顯示程式 7.2.2 周邊元件 LCD 可使用盛群公司 HT-IDE3000 所附之 LCD Simulato� 的液晶模擬面板檔 L�dDemo.l�d 及 LCD 各段碼圖案以方便調試 � 參閱第六章 6.7 節的 LCD 顯示 HT46R63 的類比 / 數位轉換電路 � 包含下列的暫存器 � 使用前必須先設定 → ADR 暫存器 (��H) Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 D7 D6 D� D4 D3 D� D1 D0 類比訊號轉換為數位的數值會儲存在 ADR 暫存器中 . 轉換的數值從 0 ~ ���. → ACSR 暫存器 (�3H) 設定 A/D 轉換的速度 Bit No. Rev. 1.30 Label Functions 0 1 ADCS0 ADCS1 ADCS1�ADCS0: 選取 A/D 轉換器的時鐘來源 (�lo�k sou��e) 0�0 : fSYS/� 0�1 : fSYS/8 1�0 : fSYS/3� 1�1 : unsdefined � C�PL Compa�ato� �ont�ol (t�is bit is 0 d�uing �eset) 0 : disable 1 : enable 3-6 — 7 TEST 未使用 � 若讀此位元 � 會得到 “0" 只在測試時使用 73 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 → ADCR 暫存器 (��H) 選定 A/D 轉換的輸入通道 � 埠 B 的設定及啟動轉換的控制 Bit No. Label Functions ACS0 ACS1 ACS� ACS�� ACS1� ACS0 : 選定 A/D 的輸入通道 0�0�0 : AN0 0�0�1 : AN1 0�1�0 : AN� 0�1�1 : AN3 1�0�0 : AN4 1�0�1 : AN� 1�1�0 : AN6 1�1�1 : AN7 PCR0 PCR1 PCR� PCR�� PCR1� PCR0 : 設定 PB7~PB0 的功能 0�0�0 : PB7�PB6�PB��PB4�PB3�PB��PB1�PB0 0�0�1 : PB7�PB6�PB��PB4�PB3�PB��PB1�AN0 0�1�0 : PB7�PB6�PB��PB4�PB3�PB��AN1�AN0 0�1�1 : PB7�PB6�PB��PB4�PB3�AN��AN1�AN0 1�0�0 : PB7�PB6�PB��PB4�AN3�AN��AN1�AN0 1�0�1 : PB7�PB6�PB��AN4�AN3�AN��AN1�AN0 1�1�0 : PB7�PB6�AN��AN4�AN3�AN��AN1�AN0 1�1�1 : AN7�AN6�AN��AN4�AN3�AN��AN1�AN0 6 EOCB A/D 轉換的指標 (=0 代表轉換結束 ) 每當變更位元 3~� 的設定後 � 應該發出一個 START 訊號以 便將 A/D 轉換器重做初始化 . 否則旗標 EOCB 會在不確定的 狀態 7 START 啟動 A/D 轉換 (0->1->0 => 啟動 � 0->1 => 重置 A/D 轉換器 � 而且將 EOCB 設為 1 0 1 � 3 4 � 當要做 A/D 轉換時 , 需要先選定轉換通道 , 埠 B 的設定及時鐘選取 . 之後要將 ADCR 暫存器 (22H) 的位元 7 (START) 設為 0 -> 1 -> 0, 則 A/D 轉換器就開始做轉換的動作 . 當轉換完成時 , ADCR 暫存器的位元 6 (EOCB) 會被清為 0, A/D 轉換的中斷功能是致能的 (enable), 則同時會 產生中斷 . 如果變更轉換通或埠 B 的設定 , 則需對 A/D 做初始化的動作 , 如下 在變更埠 B 設定的 10 個指令週期內將 ADCR 暫存器的位元 7 (START) 設為 1 再清為 0 即可 為了要利用 HT46R62 A/D 轉換器的中斷功能 , 也需要設定中斷向量及中斷控制 . → INTC0 暫存器 Bit No. Label 0 E�I Cont�ol t�e maste� (global) inte��upt (1=enabled; 0=disabled) Function 1 EEI0 Cont�ol t�e exte�nal inte��upt 0 (1=enabled; 0=disabled) � EEI1 Cont�ol t�e exte�nal inte��upt 1 (1=enabled; 0=disabled) 3 ETI Cont�ol t�e Time�/Event Counte� inte��upt (1=enabled; 0=disabled) 4 EIF0 Exte�nal inte��upt 0 �equest flag (1=a�tive; 0=ina�tive) � EIF1 Exte�nal inte��upt 1 �equest flag (1=a�tive; 0=ina�tive) 6 TF Inte�nal Time�/Event Counte� �equest flag (1=a�tive; 0=ina�tive) 7 — Fo� test mode used only. �ust be w�itten as "0"; ot�e�wise may �esult in unp�edi�table ope�ation. INTC0 (0BH) Register 位元 0 (EMI) 需要設為 1, 打開中斷的總開關 Rev. 1.30 74 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 →→ INTC1 暫存器 Bit No. Label Function 0 ETBI Cont�ols t�e time base inte��upt (1=enabled; 0=disabled) 1 EADI Cont�ol t�e A/D �onve�te� inte��upt (1=enabled; 0=disabled) � ERTI Cont�ol t�e �eal time �lo�k inte��upt (1=enabled; 0=disabled) 3 — 4 TBF Time base time-out inte��upt 0 �equest flag (1=a�tive; 0=ina�tive) � ADF End of A/D �onve�sion inte��upt �equest flag (1=a�tive; 0=ina�tive) 6 RTF RTC time-out inte��upt �equest flag (1=a�tive; 0=ina�tive) 7 — Unused bit� �ead as "0" Unused bit� �ead as "0" INTC1 (1EH) Register 位元 1 需要設為 1� 將 A/D 轉換器的中斷功能致能 (enable) 7.2.3 電路圖 HT46R63/HT46C63 Rev. 1.30 75 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 7.2.4 微控制器的架構設定 (Configuration Options) HT46R63 的 �onfigu�ation option 設定 WDT �lo�k sou��e : disable OSC : Ext. C�ystal fSYS= 4� LCD duty: 3 CO� LCD segment: �0 segments SEG7-SEG10: LCD output SEG11-SEG14: Logi�al output SEG1�-SEG18: Logi�al output Compa�ato� : disable 7.2.5 程式流程 開始 LCD RAM清為 0, 不顯示 打開 ADC中斷及總中斷功能 設定 ADC 時鐘,頻率,轉換通 道, 總通道數, 啟動 AD 轉換 AD 轉換完成 ? 未完成 已完成 將轉換完成的數值顯示在 LCD面板上 7.2.6 原始程式 Rev. 1.30 1 #include “ht46r63.h” � // 標頭檔中定義有 變數 _emi = _int�0.0 � _eadi = _1e_1 (INTC1 bit 1) � 3 // A/D 轉換器的暫存器 _ad� (0x�1)� _ad�� (0x��)� _a�s� (0x�3) 4 #define ad�_sta�t _��_7 // bit 7 (START) of ADCR 暫存器 � #define fSYS8 0x01 // ADC 時鐘來源及 fSYS/8 6 #define CH_AN0 0 // ADC 轉換通道 7 #define ADC_�CH � // ADC 總通道個數 : �� AN0 (PB0)� AN1 (PB1) 8 ��a� intflag = 0 ; // ADC 中斷旗標 9 unsigned ��a� ad�value = 0 ; // ADC 轉換後的數值 10 void inte��upt ADC_ISR(void) @ 0x14 // ADC 中斷服務函式 � 中斷向量為 0x14 11 { 1� intflag = 1 ; // 設定 ADC 中斷旗標 13 ad�value = _ad� ; // 讀取轉換後的數值 76 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 14 } 1� // 啟動 ADC 轉換功能 16 // ad�_�lk = �lo�k sou��e of ADC� fSYS/�� fSYS/8� fSYS/3� (bit0~1 of ACSR) 17 // ��annel = ��annel numbe� of AN� 0 ~ 7 (bit 0~� of ADCR) 18 // po�t_�fg = po�t B setting (bit 3~� of ADCR) 19 void Sta�tADCT�ans(��a� ad�_�lk� unsigned ��a� ��annel� unsigned ��a� po�t_�fg) �0 { �1 _a�s� = ad�_�lk ; // 設定 時鐘來源及頻率 �� ad�� = (po�t_�fg << 3) | ��annel ; // 設定通道 � 埠 B 設定 �3 ad�_sta�t = 1 ; ad�_sta�t = 0 ; // 改變 ADC 通道及埠 B 設定 �4 ad�_sta�t = 0 ; ad�_sta�t = 1 ; ad�_sta�t = 0 ; // 啟動 ADC 轉換 �� } �6 // 以下函式 ( 除了主函式 main) 與 第六章 6.7 節相同 �7 // 定義數字 `0' ~ `9' 的 LCD RA� 資料 � 7 個位元控制各段的顯示 �8 // 圖樣各段 ga�f bd� �9 ��a� digit[10] = { 0b01111101� 0b00011000� 0b01110011� 0b01111010� //`0'�`1'�`�'�`3' 30 0b00011110� 0b01101110� 0b01101111� 0b00111000� // `4'�`�'�`6'�`7' 31 0b01111111� 0b01111110 } ; // `8'� `9' 3� ��a� L�dRam[�0] @ 0x140 ; // LCD RA� 記憶體 33 void DelayTime(unsigned int �ount) // 10 * �ount + 11� if �ount > ��6 34 { 3� w�ile( �ount != 0 ) �ount-- ; 36 } 37 // add� (IN) = LCD RA� 位址 = 0x140+x 38 // datal = 寫入 LCD RA� 的資料 � bit0~� 寫入 add�� bit3~� 寫入 add�+1 39 // bit 6 寫入 add�+� 的 bit� 40 void DisplayL�d(unsigned ��a� add�� unsigned ��a� datal) 41 { 4� L�dRam[add� - 0x140] = datal & 0x7 ; // 取位元 0 ~ � 43 L�dRam[add� - 0x140+1] = (data1 >> 3) & 0x7 ; // 取位元 3~� 44 L�dRam[add� – 0x140+�] = ((data >> 6) & 0x7) << � ; // 取位 元 6� 在左移 � 位 4� } 46 // 主函式 47 void main(void) 48 { 49 int k ; �0 51for( k=0 ; k < 20 ; k++ ) LcdRam[k] = 0 ; // 將 LCD 記憶體清為 0, LCD 不顯示 52 _emi = 0 ; // 停止所有中斷的發生 53 _eadi = 1 ; // ADC 中斷功能生效 54 _emi = 1 ; // 所有中斷功能生效 55 intflag = 0 ; // ADC 中斷旗標 56 StartADCTrans(fSYS8, CH_AN0, ADC_2CH) ; // 開始 ADC 轉換 57 while(1) // 無限迴圈 , 等候 ADC 中斷產生 58 { 59 Rev. 1.30 if( intflag == 1 ) break ; // ADC 中斷產生 , 跳出迴圈 77 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 60} 61 // 將轉換好的數字顯示於 LCD 面板 62 k = adcvalue / 100 ; // 取出百位數字 63 DisplayLcd(0x146, digit[k]) ; // 顯示百位數字 64 k = (adcvalue / 10) % 10 ; // 取出十位數字 65 DisplayLcd(0x143, digit[k]) ; // 顯示數字 66 k = adcvalue % 10 ; // 取出個位數 67 DisplayLcd(0x140, digit[k]) ; 68 _delay(250000) ; // 延長 1 秒 69 _delay(250000) ; 70 _delay(250000) ; 71 _delay(250000) ; 72 for( k=0 ; k < 20 ; k++ ) LcdRam[k] = 0 ; // 將 LCD 記憶體清為 0, LCD 不顯示 73 } 7.2.7 程式說明 4 定義 暫存器 ADCR 的位元 7 (START) �~7 定義本範例使用的 ADC 時鐘來源及頻率 � 轉換通道及通道總個數 8 定義 ADC 中斷旗標 � 當等於 1 時表示 ADC 轉換成功 9 定義變數 � 儲存 ADC 轉換後的數值 10~14 定義 ADC 中斷服務函式 ADC_ISR(void) 及中斷向量為 0x14 1� 設定 ADC 中斷旗標 13 從暫存器讀出轉換後的數值並存入變數 19~�� 定義函式 Sta�tADCT�ans() 執行 ADC 轉換 �1 設定 ADC 轉換時鐘及頻率 �� 選定 ADC 轉換通道及通道總數 �3 埠 B 設定更改 �4 啟動 ADC 轉換功能 �6~4� 與第六章 6.7 節相同 � LCD 顯示函式 47~73 主函式 � 設定及啟動 ADC 轉換並將轉換後的數值從 LCD 面板顯示 �1 將 LCD 記憶體清為 0� LCD 不顯示 �� 停止所有中斷的發生 �3 ADC 中斷功能生效 �4 所有中斷功能有效 �� ADC 中斷旗標清為 0 �6 開始 ADC 轉換 �7~60 無限迴圈 � 等候 ADC 中斷產生則跳出迴圈 �9 產生 ADC 中斷 � 跳出迴圈 6�~67 將轉換後的數值顯示在 LCD 面板 6�~63 取出百位數字 � 顯示百位數字 64~6� 取出十位數字 � 顯示數字 66~67 取出個位數 � 顯示數字 68~69 延長顯示時間為 1 秒 7� Rev. 1.30 將 LCD 顯示器關閉 78 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第八章 HT46R52A 應用於鎳氫 (NiMH) 電池充電器 (HA0084T) 8.1 目的 電子產品邁向小型化發展,攜帶型產品越來越普及與流行,如移動電話、數位相機、PDA、 �P3 隨身聽等,此類產品需要大量使用電池,所以可重複使用的可充電電池就顯得十分重要。 在各類可充電電池中,鎳氫電池被用來取代傳統的一次電池,具有容量大、價格低及環保考量 的優勢。Holtek 提供了鎳氫電池充電器展示板,方便讓使用者設計鎳氫電池充電器。 ■ 充電器特性 → 以定電流方式充電,慢充時電流 70mA,快充時電流 �7�mA → 提供電池放電的選擇,來改善電池的記憶效應,放電時放電電流 �0mA → 以偵測—ΔV 方式偵測電流是否充飽 → 充電時間到達 6 小時,自動停止充電 → 兩個 LED 顯示充電狀態 → 一次只能充一顆電池 ■ 充電器簡介 利用定電流方式對一顆 Ni�H 電池充電,當電池放入時,會立刻對電池充電,可選擇在充電之 前,是否要對 Ni�H 放電,放電完畢會立刻轉入充電模式;在充電之前,會判斷電壓是否大於 1.��V,若是大於 1.��V,則進入快速充電模式,否則則進入慢速充電模式,以保護電池,當慢 速充電結束會自動進入快速充電模式。 ■ 充電方法說明 利用 HT46R52A 及定電流方式做一個 NiMH 電池充電器 , HT46R52A 提供一個 PWM 輸出, 可搭配一個 ADC input 做電流控制,HT46R52A 提供 4 channel ADC 來讀取外部模擬訊號, 可用來偵測電池電壓,當偵測到電池發生 −ΔV 時,則表示電池已經充飽,12-bit ADC 可提供到 1.2mV 的解析度。 ■ 充電流程說明 → 電源未接上時,DE�O Boa�d 上的 LED 燈不亮 → 電 源 接 上 DE�O Boa�d, 綠 色 LED 持 續 亮 著, 其 他 LED 亮 一 秒 後 關 掉, 此 時 DE�O Boa�d 等待 Ni�H 電池置入 → Ni�H 電池放入後,則開始充電,兩顆 LED 顯示電池的充電狀態 → 當 Ni�H 電池達到充飽的條件時,則停止充電,兩顆 LED 顯示充飽的狀態 → Ni�H 電池充飽後,電池移走,兩顆 LED 顯示等待 Ni�H 電池置入的狀態 兩顆 LED 顯示充電狀態訊息 → 黃色及紅色 LED 都不亮:等待電池置入 → 黃色亮、紅色 LED 不亮:快速充電中 → 黃色及紅色 LED 都亮:電池已充飽電或已充 6 小時 → 黃色亮、紅色閃爍:慢速充電中 → 紅色亮、黃色閃爍:放電中 Rev. 1.30 79 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 下表為充電過程所使用的一些參考值 8.2 暫存器及周邊元件 本範例將使用到 PWM (Pulse Width Measurement) 來控制電池充電時的電流大小 . 在設定微控制器的架構時要將埠 D (po�t D) 的位元 0 (PD.0) 設為 PW� 模式 . 程式中要將 PD0 設為輸出模式 ( 利用 PDC 控制暫存器 ). 輸出 PW� 之前要先將 PD0 寫入 1� 再將數值寫入 PW� 暫存器 . 下表為 PD0 的輸出 / 輸入功能 I/O Mode I/P (Normal) O/P (Normal) I/P (PWM) O/P (PWM) PD0 Logical Input Logical Output Logical Input PWM Parameter AC (0~3) i < AC Modulation cycle i (i=0~3) i ≥ AC DC (Duty Cycle) DC+1 64 DC 64 PWM Modulation Frequency PWM Cycle Frequency PWM Cycle Duty fSYS/64 for (6+2) bits mode fSYS/256 [PWM]/256 上表為 PW� (6+�) 模式時每個模組週期 (modulation �y�le) 的 duty �u�le ACSR 暫存器是設定 AD 轉換器的時鐘頻率 Rev. 1.30 80 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Bit No. Label 0 1 Function Selects the A/D converter clock source 00=system clock/2 01=system clock/8 10=system clock/32 11=undefined ADCS0 ADCS1 2~6 — Unused bit, read as "0" 7 TEST For test mode used only ACSR (23H) Register ADCR 暫存器控制 AD 轉換器的開始執行及停止 . 設定訊號通道及 PB 輸入埠 Bit No. Label 0 1 � ACS0 ACS1 ACS� Function Defines t�e analog ��annel sele�t 3 4 � PCR0 PCR1 PCR� Defines t�e po�t B �onfigu�ation sele�t. If PCR0� PCR1 and PCR� a�e all ze�o� t�e ADC �i��uit is powe�ed off to �edu�e powe� �onsumption. 6 EOCB Indi�ates end of A/D �onve�sion. (0=end of A/D �onve�sion) Ea�� time bits 3~� ��ange state t�e A/D s�ould be initialised by issuing a START signal� ot�e�wise t�e EOCB flag may �ave an undefined �ondition. See "Impo�tant note fo� A/D initialisation". 7 START Sta�ts t�e A/D �onve�sion. A → 1 → 0=Sta�t 0 → 1=Reset A/D �onve�te� and set EOCB to "1" ADCR (22H) Register ACS2 ACS1 ACS0 Analog Channel 0 0 0 AN0 0 0 1 AN1 0 1 0 AN� 0 1 1 AN3 1 0 0 AN4 1 0 1 * 1 1 0 * 1 1 1 * Analog Input Channel Selection PCR2 PCR1 PCR0 4 3 2 1 0 0 0 0 PB4 PB3 PB� PB1 PB0 0 0 1 PB4 PB3 PB� PB1 AN0 0 1 0 PB4 PB3 PB� AN1 AN0 0 1 1 PB4 PB3 AN� AN1 AN0 1 0 0 PB4 AN3 AN� AN1 AN0 1 0 1 AN4 AN3 AN� AN1 AN0 1 1 0 1 1 1 Undefined� �annot be used Port B configuration Rev. 1.30 81 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Register Bit7 Bit6 Bit5 Bit4 BIt3 Bit2 Bit1 Bit0 ADRL (�0H) D3 D� D1 D0 0 0 0 0 ADRH (�1H) D11 D10 D9 D8 D7 D6 D� D4 Note: D0~D11 is A/D conversion result data bit LSB~MSB. AD 轉換器執行的結果會儲存在 ADRL 與 ADRH 兩個暫存器 8.3 電路圖 8.4 微控制器的架構設定 (Configuration Options) HT46R�� 的 �onfigu�ation option 設定: WDT Disable CLRWDT Two �lea� inst�u�tions WDT �lo�k sou��e WDTOSC Pull-�ig� PD0 Non-Pull-�ig� LVR Disable OSC RC PD0/PW� Enable PW� PA3/PFD Disable PFD ----------------------------------------------------------------------------------76�43�10 Wake-up PA0- 0 0 0 0 0 0 0 0 1:Wake-up� 0:Non-Wake-up Pull-�ig� PA0- 1 1 1 1 1 1 1 1 1:Pull-�ig�� 0:Non-Pull-�ig� Pull-�ig� PB0- - - - 1 1 1 1 1 1:Pull-�ig�� 0:Non-Pull-�ig� ----------------------------------------------------------------------------------- Rev. 1.30 82 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 8.5 程式流程 ■ 主流程 START Initial處理程序 N 偵測電池是否置 入? N Y 充電模式是否有 選擇放電 ? 電池放電� 並偵 測電壓是否小於 1.0V ? Y Y Y N 電池電壓是否大 於 1.��V ? N 慢速充電 N Y 快速充電 是否偵測到電池 電壓發生 ΔV ? N 偵測充電時間是 否大於 6 小時 ? Y Y 偵測電池是否移 走? 顯示電池已經充飽 N Rev. 1.30 83 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 初始化程式 Initial 處理程序 START 設定 I/O po�t PW� = 0 紅色與黃色 LED 熄滅 設定 ADCR and ACSR Clea� RA� STOP C�a�ging Enable Time� Inte��upt 設定 Time� 4ms 中斷 一次 紅色� 黃色 LED 顯示一 秒後熄滅 RET Rev. 1.30 84 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 快速充電處理程式 快速充電 START 量測目前充電電 流是否大於 �6�mA ? N 增加 PW� 值 Y 減少 PW� 值 N 量測目前充電電 流是否小於 �8�mA ? Y 充電時間是否大 於 6 小時 ? Y N 電池電壓是否大 於 1.��V ? Y 充電完成 N 快速充電 EXIT Rev. 1.30 85 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 慢速充電處理程式 慢速充電 START 量測目前充電電 流是否大於 60mA ? N 增加 PW� 值 Y 減少 PW� 值 N 量測目前充電電 流是否小於 80mA ? Y 充電時間是否大 於 6 小時 ? Y N 電池電壓是否大 於 1.��V ? Y 充電完成 N 慢速充電 EXIT Rev. 1.30 86 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 8.6 原始程式與程式說明 ■ 檔案 main.c 1 � void main() 3 { 4 � �initial(); // initial all RA� & IO� tu�n LED (g�een) on 6 ��0_old = 0 ; // a new �un 7 8 w�ile(1) // infinite loop 9 { 10 w�ile(1) // ��e�k batte�y status : exist/not exist 11 { 1� bat_main_sub(); // get batte�y's status� t�e batte�y is pla�ed o� not 13 // set t�e batte�y's LED flag 14 if( bat0_on_flag && !��0_old ) // batte�y is pla�ed� and it's a new �un 1� b�eak ; 16 17 ��0_old = 0 ; // flag : t�is is new �un 18 } 19 �0 // bat0_on_flag=1 batte�y is pla�ed �1 // 1. ��e�k dis��a�ge swit��� PB3 �� if( !_pb3 ) �3 C�0_Dis��a�ging(); // sta�t to dis��a�ge �4 �� // ��0_batte�y_��a�ging_sub_lp: �6 // �. do batte�y ��a�ging �7 �8 C�0_DoC�a�ging() ; �9 30 } } ■ 程式說明 2 主程式 5 呼叫函式 cinitial() 對 RAM 記憶體做清理 , 設定 I/O 口的狀態 , 點亮綠燈 6 設定旗標 ch0_old=0 : 新一輪處理 8~29 外層無限迴圈 . 程式永遠在此迴圈中執行 10~18 內層回圈 , 檢測是否有電池放置於充電座 , 若有則跳出迴圈 , 否則就繼續檢測 12 呼叫函式 bat_main_sub() 檢測電池的狀況 14~15 如果有電池放置 , 並且不是前一次的電池 , 則跳出此迴圈做適當的處理 Rev. 1.30 17 否則 , 設定旗標 , 代表等候下一顆電池 , 並繼續檢測 22 執行到此 , 代表已有電池放於電池座等待處理 , 檢查埠 B 的 pb3 是否為低電壓 ( 代表要先做放電 ) 23 是的 , 則呼叫函式 Ch0_Discharging() 執行放電功能 28 呼叫函式 Ch0_DoCharging() 執行充電功能 87 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 //------------------------------------// C�0_DoC�a�ging() // 1. batte�y ��a�ging wit� slow speed // �. batte�y ��a�ging wit� fast speed //------------------------------------1 void C�0_DoC�a�ging() �{ 3 w�ile(1) 4 { � bat0_flag_initial(); // initialize flags� set PW� 6 mad�_on_sub(); // ��e�k batte�y's status 7 8 if( !��0_old ) s�ow_bat0_led(); //s�ow led w�i�� flag fo� batte�y 9 10 _delay(��0000) ; // delay 0.��s 11 _delay(��0000) ; 1� _delay(��0000) ; 13 _delay(��0000) ; // total delay 1s 14 1� if( !bat0_on_flag ) // w�en �ave no batte�y in�jump 16 { 17 led0_on_flag = 0 ; 18 led1_on_flag = 0 ; // �lea� led s�ow 19 ��0_old = 0 ; // t�e batte�y ��a�ging finis�ed flag �lea� �0 �1 �etu�n ; // jump ba�k } �� �3 if( bat0_voltage_ove�_flag ) // w�en batte�y ��a�ging finis��jump �4 { �� led0_on_flag = 1 ; // ��a�ging finis�ed�led0&led1 on �6 led1_on_flag = 1 ; // RED� Yellow a�e lit �7 ��0_old=1; // old batte�y �8 �9 �etu�n ; // ��e�k batte�y status again } 30 31 if( bat0_1�_��a�ging_flag ) // fast ��a�ging 3� { 33 C�0_FastC�a�ging() ; 34 �etu�n ; 3� } 36 37 // slow ��a�ging mode 38 bat0_flag_initial(); // initial ��a�ging. 39 bat0_��a�ging_initial(); // load initial batte�y's ave�age value 40 Rev. 1.30 41 // bat0_01�_��a�ging_ap: 4� w�ile(1) 88 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 43 { 44 bat0_�0ma_��a�ging(); // ��a�ging batte�y by �0ma �u��ent 4� stop_��a�ging(); // stop ��a�ging 46 _delay(��0000) ; // delay 0.��s 47 _delay(��0000) ; // total delay 0.�s 48 49 mad�_on_sub(); // ��e�k batte�y status �0 led0_on_flag = 1 ; �1 led1_on_flag ^= 1 ; // toggle flag (Red LED flas�) �� �3 ad_4_data(); // load batte�y's ave�age �4 get_Vbat0_in�80mv(); �� get_bat0_Vpeak(); �6 �7 if(!bat0_on_flag) // if no battte�y in �8 { �9 led0_on_flag=0; // batte�y �emove�led0&led1 off 60 led1_on_flag=0; 61 ��0_old=0; // flag fo� batte�y �emove al�eady 6� �etu�n ; // ��e�k batte�y status again 63 } 64 6� if( time_1min >= ��a�ging_time ) // batte�y ��a�ging's time ove� 66 { 67 led0_on_flag=1; //��a�ging finis�ed�led0&led1 on 68 led1_on_flag=1; 69 ��0_old=1; // flag fo� batte�y : no ��ange of batte�y 70 �etu�n ; // ��e�k batte�y status again 71 } 7� 73 if( bat0_1�_��a�ging_flag ) 74 b�eak ; 7� } 76 77 led0_on_flag=1; 78 79 led1_on_flag=0; } 80 } Rev. 1.30 89 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 1 此函式為無限迴圈 , 執行電池的充電動作 . 完成後根據不同的狀態設定 LED 點亮或熄 滅的旗標 , 再回到主程式 3 無限迴圈 5 呼叫函式 bat0_flag_initial() 設定參數的初始值 , 設定 PWM 6 呼叫函式 madc_on_sub(), 從 AD 轉換器讀出此電池的電壓 , 並設定相對的旗標 8 如果這是剛才放入的電池 , 則以紅色 LED 顯示其現狀 10~13 延遲 1 秒 , 讓 LED 閃爍 以下則根據電池的電壓決定要如何進行 15~21 如果電池的電壓小於 0.1V, 代表沒有電池 , 設定熄滅紅 , 黃 LED 的旗標 , 並返回 23~29 如果電池的電壓超過 1.55V, 代表已完成充電 , 即設定 LED 旗標 , 並返回 31~35 如果電池的電壓介於 1.25V 與 1.55V 之間 , 表示正在充電過程中 , 則呼叫函式 Ch0_ FastCharging() 做快速充電 . 完成後返回 38~79 電池的電壓在 0.1V 與 1.25V 之間 , 則用慢速充電方式 38 bat0_flag_initial() 設定 LED 旗標 , PWM 值 . 39 bat0_charging_initial() 設定充電的初始值 42~75 迴圈 , 執行慢速充電 44 bat0_50ma_charging() 利用調整 PWM 的方式控制電流對電池充電 6 小時 45~47 停止充電並等候 0.5 秒 49 madc_on_sub() 檢查電池充電後的電壓狀態 50~51 設定 LED 顯示旗標 ( 紅閃 , 黃亮 ) 53ad_4_data() 計算電壓平均值 54~55 調整電壓值與鋒值 57~63 再次檢查電池狀況 , 若是電池被移走 , 則設定旗標並返回 65 如果充電時間已達 6 小時 , 設定完成的 LED 旗標並返回 73 如果電壓仍在未飽滿的狀態下 , 將回到第 3 行的迴圈 , 重新開始充電的步驟 //--------------------------// bat0_1c_charging_ap_lp: // fast charging mode //---------------------------1 void Ch0_FastCharging() 2{ 3 bat0_flag_initial(); // initial charging. 4 bat0_charging_initial(); // load initial battery's average value 5 6 while(1) 7 { 8 bat0_500ma_charging(); // charging battery by 500ma current 9 stop_charging(); // stop charging 10 madc_on_sub(); // check battery status 11 12 if( bat0_voltage_over_flag ) // charging has been finished 13{ 14 led0_on_flag=1; // charging finished, Yellow and Red LED on 1� led1_on_flag=1; 16 ��0_old=1; // flag fo� batte�y 17 18 Rev. 1.30 �etu�n ; // ��e�k batte�y status again } 90 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 19 �0 // set LED status �1 led0_on_flag=1; // Yellow LED is lit �� led1_on_flag=0; // Red LED extinguis� �3 �4 ad_4_data(); // load batte�y's ave�age �� get_Vbat0_in�80mv(); �6 get_bat0_Vpeak(); �7 �8 if(!bat0_on_flag) // batte�y is �emoved �9 { 30 led0_on_flag=0; // batte�y �emove�led0&led1 off 31 led1_on_flag=0; 3� ��0_old=0; // flag fo� batte�y �emove al�eady 33 �etu�n ; // ��e�k batte�y status again 34 } 3� 36 if(time_1min >= ��a�ging_time || // batte�y ��a�ging's time ove� 37 bat0_Vpeak_��a�ging_ok ) // batte�y ��a�ging ok 38 { 39 led0_on_flag=1; //charging finished,led0&led1 on 40 led1_on_flag=1; 41 ch0_old=1; // flag for battery 42return ; //check battery status again 43} 44 } //charging go on 45 } ■ 程式說明 快速充電 3~4 設定充電的初始值 // initial charging. 6~44 無限迴圈 8 使用電流充電 , 並根據電池電壓調整電流大小充電 , 持續 6 小時 9~10 完成後 , 檢查充電後電池的電壓 12~18 如果電池已充滿 , 則設定完成的 LED 旗標 , 並返回 21~22 設定 LED 旗標為黃色 LED 亮 , 紅色 LED 熄滅 ( 表示快速充電狀態 ) 24~26 計算及保存平均電壓值 , 電壓峰值 28~34 檢查若電池已移走 , 則設定 LED 旗標為熄滅狀態 , 並返回 36~42 如果稱電時間已達 6 小時或是已充電到電壓峰值 , 則設定完成的 LED 旗標並返回 44 如果不是上述狀態 , 則回到第 6 行重做快速充電的動作 //-------------------------------// timer_isr //-------------------------------1 void interrupt timer_isr(void) @ 0x08 2{ 3 unsigned char tmp ; 4 5 Rev. 1.30 tmp = _pa ^ 0x40 ; // read from PA and XOR PA6 91 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6 _pa = tmp ; // output PA6 7 8 // turn on/off LED 9 if( (tmp & 0x40) != 0 ) // current PA6=1 10{ 11 _pa5 = 1 ; 1� if( led�_on_flag ) _pa� = 0 ; 13 14 _pa4 = 1 ; // Red LED extinguis� 1� if( led1_on_flag ) 16 17 _pa4 = 0 ; // Red LED lit } 18 else // PA6=0 19 { �0 _pa� = 0 ; �1 if( led3_on_flag ) _pa� =1 ; �� �3 _pa4 = 0 ; // Yellow LED extinguis� �4 if( led0_on_flag ) �� 26 _pa4 = 1 ; // Yellow LED is lit } 27 28 // calculate the time 29 time_4ms++; // count 30 if(time_4ms==250) // 1 second 31{ 32 time_4ms=0; // clear ms 33 time_1s++; // 1s count add 34 Vpeak_cx++; // Vpeak_cx add 35 if(time_1s==90) // 90 seconds 36{ 37 time_1s=0; // time for 90s 38 time_1min++; 39charging_cx++; // total charging time, increase 1 for every 1.5 minutes 40} 41 } 42 } ■ 程式說明 時鐘中斷服務函式 time�_is� 主要是控制紅 � 黃兩色 LED 的點量與熄滅 � 另外一個功能是累計充 電時間 5 將目前的 LED 控制碼 (PA6) 讀出 , 再反轉之 6 設定反轉後的控制狀態 9~17 如果此次輪到處理紅色 LED (PA6 為高電位 ) 將紅色 LED 先熄滅 , 若 LED 旗標為要點亮時 , 再將紅色 LED 點亮 18~26 如果此次輪到處理黃色 LED (PA6 為低電位 ) Rev. 1.30 將黃色 LED 先熄滅 , 若 LED 旗標為要點亮時 , 再將黃色 LED 點亮 28 以下將累計充電時間 92 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 29 每 4 個毫秒中斷一次 , 累加毫秒次數 30~41 若已累加到 1 秒時 , 繼續處理 , 累加秒次數 , 電壓峰值計數 35~40 若以累加到 90 秒 , 則累計 90 秒次數與充電次數 原始程式 sub.c #define RAM_START 0x28 #define RAM_END 0x7F //------------------------------------// cinitial() // tu�n LED (g�een) on fo� 1 se�ond //------------------------------------1 void �initial() �{ 3 ��a� *add� ; 4 � _pa=0x03; 6 _pa�=0x80; // PA7:I/P� ot�e�s a�e O/P 7 _pb�=0xff; // PB a�e In 8 _pb=0x00; 9 _pd�=0x00; // PD a�e Out 10 _pd=0x00; 11 1� _a�s� = 0x0�; // AD �lo�k sou��e = system �lo�k / 3� 13 _ad�� = ��3+a0; // set AD ��annel numbe� and PB 14 _pwm = 0x00; // pwm output 0 1� 16 fo�( add�=(��a� *)RA�_START ; add� <= (��a� *)RA�_END ; add�++ ) 17 *add� = 0 ; // �lea� RA� 18 19 stop_��a�ging(); // stop PW� �0 _int� = 0x0�; // enable Time�/Counte� 0 21 _tmr = (256-250); // set tmr’s value for 4ms tmr (250*16us = 4ms) 22 23 _tmrc = timer_mode+timer_on+T1_64; // set TMR0 : timer mode, timer on and // fSYS/64, fSYS=4MHz 24 led0_on_flag=1; // set LED flag 25 led1_on_flag=1; 26 led2_on_flag=0; 27 led3_on_flag=0; 28 29 _delay(250000) ; // delay 0.25s 30 _delay(250000) ; // total delay 0.5s for LED on 31 32 led0_on_flag=0; 33 led1_on_flag=0; 34 led2_on_flag=0; 35 led3_on_flag=0; // clear all led flag, turn all LEDs off 36 _delay(250000) ; // delay 0.25s 37 _delay(250000) ; // delay 0.25s 38 } Rev. 1.30 93 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 此函式針對暫存器及記憶空間做初始化 , 設定所需要的架構 5~10 設定埠 A: PA7 為輸入 , 其它為輸出型態 ; 埠 B 為輸入型態 埠 D 為輸出型態 , 並且停止 PWM 動作 選擇 ( 系統頻率 /32) 為 AD 轉換器的時鐘來源 12 選擇 AD 轉換器的通道 0 與埠 B 的 PB0, PB1, PB2 做為訊號輸入通道 13 14 PWM 輸出為零 16~17 將資料記憶空間 , 位址從 28H 到 7FH, 清為零 停止充電動作 19 打開時鐘中斷功能 20 設定時鐘的中斷相隔時間為 4 毫秒 (250 次 ) (250*16us = 4ms) 21 設定時鐘為計時模式 , 時鐘頻率為系統頻率除以 16, 並啟動時鐘開始計數 22 24~27 設定 LED 狀態為紅 , 黃色皆點亮 29~30 LED 狀態持續 0.5 秒 32~35 改變 LED 狀態為紅黃色 LED 熄滅 36~37 LED 狀態持續 0.5 秒 //---------------------------// bat_main_sub() // get battery status //---------------------------1 void bat_main_sub() 2{ 3 stop_charging(); // stop PWM 4 madc_on_sub(); // get battery's voltage and check status 5 6 if( !ch0_old ) show_bat0_led(); // show led 7 8 _delay(250000) ; // delay 0.25s 9 _delay(250000) ; 10 _delay(250000) ; 11 _delay(250000) ; // total delay 1s 12 } ■ 程式說明 此函式檢測座子上是否有電池等候處理 3 停止 PWM 輸出 4 從 AD 轉換器讀取是否放置有電池 , 並設定對應的旗標 6 如先前沒有電池被處理或是已移走 , 則以 LED 顯示目前的狀態 8~11 持續 1 秒 LED 的現狀 //-------------------------------// bat0_flag_initial() //------------------------------1 void bat0_flag_initial() 2{ 3 Vpeak_cx=0; 4 bat0_Vpeak_charging_ok=0; 5 time_1min=0; 6 _pwm = 0x10 ; 7 time_4ms = 0 ; 8 time_1s = 0 ; 9} Rev. 1.30 94 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 此函式電池充電旗標及時間計數 3 設定固定之電壓峰值的持續時間為 0, 當充電電壓在一定的時間內沒有變化 則會設定 旗標代表已充飽 4 設定峰值旗標為 0, 此旗標為 1 時 , 代表電池已充飽 5~8 時間計數的初值歸零 , 及 PWM 為 0 //--------------------------------// bat0_charging_initial() //--------------------------------1 void bat0_charging_initial() 2{ 3 int i ; 4 � fo�( i = 0 ; i < 8 ; i++ ) 6 ad_4_data(); 7 8 Vbat0_peak = Vbat0_old = Vbat0 ; 9} ■ 程式說明 此函式設定電池充電前的電壓值與電壓峰值 , 做比較之用 5~6 計算之前 8 個電壓平均值做參考之用 8 記錄參考值 //--------------------------------------// madc_on_sub() // get current battery voltage and status 1 void madc_on_sub() 2{ 3 Vbat0_old = Vbat0 ; // save old battery value 4 GetBatteryVoltage(); // get battery voltage from ADC channel 0 5 check_battery0_status(); //check battery’s status, set/reset flags 6} ■ 程式說明 此函式讀取電池的電壓值及設定相對的旗標 3 儲存前一電壓值 4 讀取電池的電壓值 5 根據電壓值設定相關的旗標 //---------------------------------------// GetBatte�yVoltage() ad�_on() // �ead batte�y's voltage f�om ��annel 0 //---------------------------------------1 void GetBatte�yVoltage() �{ Rev. 1.30 3 stop_��a�ging(); // stop pwm output 4 _delay(10000) ; // delay 10ms (4�Hz) � // sta�t to �onve�t by using ADC 6 _ad�� = ��3+a0; // enable ADC CH0 7 _emi=0 ; // disable all inte��upts 95 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 8 _sta�t=0; 9 _sta�t=1; 10 _sta�t=0; // sta�t ADC 11 w�ile(_eo�); // wait fo� ADC �onve�sion �ompletion 1� _emi=1; // enable inte��upt 13 14 // read the conversion result 12 bits, adrl = (D3,D2,D1,D0,0,0,0,0), 15 // adrh=(D11,D10,D9,D8,D7,D6,D5,D4) 16 17 bat0 = (unsigned int)_adrh ; 19 bat0_voltage = bat0 ; 20 } ■ 程式說明 此函式會讀出電池的電壓值並儲存 3 暫停 PWM 的輸出 4 等候 10 毫秒 6 打開 AD 轉換器的通道 0 7 禁止所有中斷的發生 8~10 啟動 AD 轉換器 , 開始動作 11 等候 AD 傳換器完成轉換動作 12 打開控制中斷發生的開闢 17~19 讀出轉換值 , 合成一個 16 位元的電壓值 , 並儲存 //-----------------------------// a0_discharging() //--------------------------void a0_discharging() // 此函式是執行 電池的放電 { _pd0 = 0 ; // 停止 PWM 功能 _pa = (_pa & 0x0f3) | 0x07 ; // PA2=1 : 將 PA2 設為 1, 啟動放電動作 } void a0_charging() // 此函式為開啟電池的充電功能 { _pa=(_pa&0x0f1)|0x01; // 停止放電 , 打開充電電路 _pd0=1; // 開啟 PWM 的輸出 } //-------------------// stop_charging() //-------------------void stop_charging() // 此函式是停止充電 { _pd0 = 0 ; // 停止 PWM 的輸出機制 _pa = (_pa & 0x0f3)|0x03; // 停止充電 } //----------------------------------------------// ��e�k_batte�y0_status() // ��e�k t�e status of batte�y 0� set/�eset flags // I/P: bat0_voltage //------------------------------------------------ Rev. 1.30 96 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 1 void check_battery0_status() 2{ 3 if( bat0_voltage < V01 ) // battery < 0.1V 4 bat0_on_flag = bat0_1c_charging_flag = bat0_voltage_over_flag = 0 ; 5 else if( bat0_voltage < V125 ) // 0.1V <= battery < 1.25V 6 { 7 bat0_on_flag = 1 ; 8 bat0_1c_charging_flag=0; 9 bat0_voltage_over_flag=0; 10} 11 else if( bat0_voltage < V155 ) // 1.25 <= battery <1.55 12{ 13 bat0_on_flag = 1 ; 14 bat0_1c_charging_flag=1; 15 bat0_voltage_over_flag=0; 16 } 17 else 18 { 19 bat0_on_flag=1; 20 bat0_1c_charging_flag=0; 21 bat0_voltage_over_flag=1; // battery >= 1.55 22 } 23 } ■ 程式說明 此函式將根據所讀到的電池電壓值設定相關之旗標 , 做為之後如何進行的依據 3~4 如果電池電壓小於 0.1V, 則設定旗標為 0 ( 電池不存在 ) 5~10 如果電池電壓介於 0.1V 到 1.25V 之間 , 代表有電池放置 , 但尚未充電 11~16 如果電池電壓介於 1.25V 到 1.55V 之間 , 代表有電池放置並且在充電過程中 如果電池電壓大於 1.55V, 代表此電池已完成充電 17 //-----------------------------------// show_bat0_led() // set the battery 0 LED flag //-----------------------------------void show_bat0_led() // 根據電池電壓的現狀 , 設定紅色 LED 的點亮與否 { led0_on_flag = 0 ; // 黃色 LED 熄滅 led1_on_flag = bat0_on_flag ; // 紅色 LED, 視電池是否放置與座子 } //-------------------------------------------------------// ad_4_data() : �ompute t�e ave�age voltage of batte�y //-------------------------------------------------------void ad_4_data() { int i ; fo�( i = 0 ; i < 7 ; i++ ) // move batte�y value fo�wa�d Vbatte�y[i] = Vbatte�y[i+1] ; Vbatte�y[i] = bat0_voltage ; // sto�e �u��ent voltage Vbat0_old = Vbat0 ; // save old Vbat0 fo�( i = 0 � Vbat0 = 4 ; i < 8 ; i++ ) Vbat0 += Vbatte�y[i] ; Vbat0 >>= 3 ; // div 8 fo� ave�age value save to Vbat0 } Rev. 1.30 97 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 此函式是將前面讀出之電池電壓值計算出其平均值 //-------------------------------------// get_Vbat0_inc80mv() //-------------------------------------void get_Vbat0_inc80mv() { if( Vbat0 >= (Vbat0_old + V80mv) ) // 如果電壓超過前次 80mV Vbat0 = Vbat0_old + V80mv ; // 變更目前的電壓值 } //----------------------------------// get_bat0_Vpeak() //-------------------------------void get_bat0_Vpeak() { if( Vbat0 < Vbat0_peak ) // 如果電壓小於電壓峰值 { if( Vpeak_cx >= Vpk_time ) // 此電壓峰值已持續 64 秒 { // 在 64 秒內充電之電壓未高於此峰值 bat0_Vpeak_��a�ging_ok = 1 ; // ��a�ging is ok w�en time>64 Vpeak_�x = 0 ; } } else { Vbat0_peak = Vbat0 ; // 更新電壓峰值 Vpeak_cx = 0 ; // 峰值計時歸零 bat0_Vpeak_charging_ok = 0 ; // 峰值旗標歸零 } } //--------------------------------------------// SetDis��a�gingLEDflag -- bat0_dis��a�ging() //--------------------------------------------void SetDischargingLEDflag() // 設定放電時的 LED 旗標 { led1_on_flag = 1 ; // 紅色 LED 點亮 led0_on_flag ^= 1; // 黃色 LED 閃爍 } //--------------------------------// bat0_�00ma_��a�ging() //--------------------------------1 void bat0_�00ma_��a�ging() �{ 3 unsigned int temp; 4 � ��a�ging_�x = 0; 6 a0_��a�ging(); 7 _ad�� = a�+��3; // ADC ��annel � 8 9 Rev. 1.30 while(1) 98 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 10 { 11_emi=0; 12 _start=0; 13 _start=1; 14 _start=0; // start AD conversion 15 while(_eoc); 16 _emi=1; 17 18 temp = (unsigned int)_adrh ; 19 temp = (temp << 8)+(unsigned int)_adrl ; // read AD converted value 20 if((temp >= V260mv) && (_pwm != 0)) _pwm-- ; 21 if((temp < V250mv) && (_pwm != 0)) _pwm++ ; // if battery 22 // current<566ma,add pwm 23 if( charging_cx >= 240 ) return ; // if charging time >= 6 hours, return 24 } 25 } ■ 程式說明 此函式執行快速充電 , 過成中會隨者電池電壓調整電流 5 充電時間歸零 6 開啟充電機制 7 設定 AD 轉換器的通道 2 為電池的電壓輸入通道 9~20 迴圈 , 將電池充電過程中的電壓讀入 , 並依此而做電流的調整 11 禁止任何中斷的發生 12~14 開啟 AD 轉換器的運作 15 等候 AD 轉換器完成運作 16 允許中斷可以發生 18~19 讀出電壓值 20 如果電池電壓超過 293mv 而且 PWM 不為零 , 則將 PWM 值減一 21 如果電池電壓小於 281mv 而且 PWM 不為零 , 則將 PWM 值加一 23 如果充電時間已達 6 小時 , 則停止並返回 24 繼續充電 //---------------------------------// bat0_50ma_charging() //---------------------------------1 void bat0_50ma_charging() 2{ 3 unsigned int temp; 4 5 charging_cx=0; 6 a0_charging(); //set led status's flag 7 _adcr = a2+ch3; //set AD channel 2 8 9 while(1) 10 { 11_emi=0; Rev. 1.30 12 _start=0; 13 _start=1; 99 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 14 _start=0; // start ADC conversion 15 while(_eoc); 16 _emi=1; 17 18 temp = (unsigned int)_ad�� ; 19 temp = (temp << 8) + (unsigned int)_ad�l ; // �ead AD �onve�sion value �0 if( temp < V�0mv ) �1 { �� if(_pwm) _pwm++; // batte�y's �u��ent<�8 mA�pwm add �3 else _pwm = 0x10; // minimum �4 } �� else if( temp >= V30mv ) �6 { �7 �8 if( _pwm ) _pwm--; // batte�y's �u��ent>=78 mA�pwm de� else _pwm=0x10; 29} 30 31 if( charging_cx >= 240 ) return ; // charging's time out 6 hours 32} 33 } ■ 程式說明 此函式處理慢速充電 5 設定充電時間為 0 6 開啟 PWM 及充電電路 7 設定使用 AD 轉換器的通道 2 9~32 迴圈 . 執行電池的充電流程 11 禁止所有中斷的發生 12~14 開動 AD 轉換器的運作 15 等候 AD 轉換運作的完成 16 允許中斷可以發生 18~19 讀出 AD 傳換後的資料 20~24 如果電池電壓小於 29mv, 則將 PWM 加一 25~29 如果電池電壓大於或等於 39mv, 則將 PWM 減一 31 如果充電時間已達 6 小時 , 則停止並返回 32 若還未達到 , 則繼續充電 //-------------------------------------// Ch0_Discharging() //-------------------------------------1 void Ch0_Discharging() �{ 3 mad�_on_sub(); // get batte�y's voltage and status 4 led0_on_flag = 0; // set LED flags � led1_on_flag = 1; // tu�n RED LED on� Yellow flas� 6 bat0_��a�ging_initial(); // initial 7 w�ile(1) 8 { 9 Rev. 1.30 a0_dis��a�ging(); // to dis��a�ge (PA�=1) 100 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 10 11 _delay(��0000) ; // delay 0.��s 1� _delay(��0000) ; 13 _delay(��0000) ; 14 _delay(250000) ; // total delay 1s 15 16 madc_on_sub(); // get battery’s voltage 17 SetDischargingLEDflag(); // set LED0 flag 18 19 ad_4_data(); // get battery’s average voltage 20 if( Vbat0 >= (Vbat0_old + V80mv) ) 21 Vbat0 = Vbat0_old + V80mv ; 22 23 get_bat0_Vpeak(); // check peak value 24 bat0_discharging_ok_flag = (Vbat0 < V155) ? 1 : 0 ; // if battery < 25 // 1.15,discharging end 26 �7 // if no batte�y in o� batte�y ��a�ging is ove� o� batte�y dis��a�ging is ok� t�en �etu�n �8 if( !bat0_on_flag || bat0_voltage_ove�_flag || bat0_dis��a�ging_ok_flag ) �9 31 �etu�n ; // batte�y dis��a�ging ok�jump } 3� } ■ 程式說明 此函式將對座上的電池做放電動作 , 紅色 LED 點亮 , 黃色 LED 閃爍 3 讀取電池的電壓並設定相關的旗標 4~5 設定紅色 LED 為點亮 , 黃色 LED 熄滅 6 計算放電過程中的參考電壓值 7~31 迴圈 . 放電流程 9 開啟放電開關 11~14 等候 1 秒 16 啟動 AD 轉換器 , 讀取電池電壓 , 並設定相關旗標 17 設定 LED 旗標 , 紅色 LED 點亮 , 黃色 LED 閃爍 ( 交互亮滅 ) 19 計算放電後的電壓平均值 20~21 如果此電壓比參考電壓還低 80mv, 則將電壓更新為參考電壓加 80mV Rev. 1.30 23 與電壓峰值做比較或更新 24 檢查 , 若電壓小於 1.55V 則設定放電完成旗標 27 若電持已移出 , 或已完成電池放電 , 則返回 31 繼續放電流程 101 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 程式範例 – HT46R74D-1 胎壓計 (HA0105T) 第九章 9.1 目的 本裝置是一個以 HT46R74D-1 為主晶片,配合傳感器組成的汽車輪胎氣壓檢測裝置,最終取 高三位有效值顯示於 LCD,有四種顯示單位供切換(Psi、Ba�、Kpa、Kgf/�m�)。本裝置僅 為參考範例,使用時,為提高準確度,建議多量測些組數據來建成 Senso� OutputVoltage Dis��a�ge Time (V-T) 表格,並作相應校準。 9.2 功能說明 ■ 按鍵 一個單位切換設置按鍵: SELECT 一個控制 I/O 口:高電位執行的 IO 口(驅動工作指示燈) ■ 功能描述 9.2.1 �CU 未上電或休眠 (�alt) 狀態下,接通電源或按`SELECT'鍵,叫醒 �CU,系統會 點亮工作指示燈,同時測量空載氣壓值,並保存此校準值(校準值以固定充電時間 Ti 下的放電時間 T� 值保存) 9.2.2 若無按鍵,則取 10 次放電時間的平均值減去校準值作爲此次測量的 T� 值(放電時間 T� 一秒鐘刷新一次),查表後,依所選單位進行運算,取運算結果的高三位有效值顯 示於 LCD;若檢測到按鍵發生,則進入相應的按鍵處理流程。 9.2.3 若顯示數據大於 999,則顯示“---",表示測試值超出顯示的數值範圍。 9.2.4 �CU 在工作狀態下,每按一次`SELECT'鍵,則會變換一種顯示單位。顯示單位按 Psi => Ba� => Kpa => Kgf/�m� 的順序循環變換。單位變換同時,顯示數值也作相應改 變(單位之間的轉換關係請參閲附錄)。 9.2.5 �CU 在工作狀態下,若在 60 秒內沒有檢測到按鍵,則關閉 LCD 顯示,將驅動 LCD 的 IO 口設為 0,�CU 進入睡眠狀態。 9.2.6 某 些 參 數 的 定 義, 要 根 據 Senso� 和 所 建 的 Senso� Output Voltage - Dis��a�ge Time(V-T) 表格作相應修改,如下説明: #DEFINE PER 100 #DEFINE PSI_FULL 10000 #DEFINE BAR_FULL 689 #DEFINE KPA_FULL 0D�4H #DEFINE KPA_FULL_H16 01H #DEFINE KGFC��_FULL 703 #DEFINE TABLE_NU� 13 #DEFINE TABLE_PER 1� Note: PER: Senso� 滿量程值放大 100 倍 PSI_FULL: Senso� 滿量程值 100(PSI)*100=10000 BAR_FULL: Senso� 滿量程值 6.89(PSI)*100=689 KPA_FULL: Senso� 滿量程值 689.48(PSI)*100=68948 的低 16 位 KPA_FULL_H16: Senso� 滿量程值 689.48(PSI)*100=68948 的高 16 位 KGFC��_FULL: Senso� 滿量程值 7.03(PSI)*100=703 TABLE_NU�: V-T 表格數據個數 TABLE_PER:本裝置 V-T 表格中將 Senso� 輸出分為 1� 份,即每隔 �mV 建立一組數據 Rev. 1.30 102 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 9.3 電路圖 (圖一) Rev. 1.30 103 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 電路說明 HT46R74D-1 為雙積分類比數位轉換型 (ADC) 微控制器,晶片內含有放大器,電壓跟隨器, 積分器和比較器。充電時,內部多路開關切換到放大器輸出端,經由積分電路為充放電電容 V� 充電;放電時,多路開關切換到 VDSO,V� 即開始放電,當其電壓降至 1/6VDSO 時,比較器 即輸出低電平,視爲放電結束。 HT46R74D-1 的內建 3.3V 基準電壓可作爲 Senso� 的電壓源,VOBGP PIN 可為 Senso� 提供 1.�V 的參考電壓。Senso� 的輸出電壓需經 Amplifie� 放大,Amplifie� 模塊的連接方式具體如下 (圖二): ( 圖二) 其中,VDOPAO=VOBGP+(VA-VB)*(R�/R1)=1.�V+10*(VA-VB) (and R1=R3�R�=R4�R�=10R1) 本裝置所選用的 Senso� 規格為:3.3V 工作電壓下滿量程(100PSI)輸出 60mV。 V-T 表格以 (Senso� 輸出電壓 /�mV+10)作爲偏移位址,共包含 13 組數據。在量測輪胎氣壓時,與所製 表格數據相同充電時間下,每量測一組放電時間,即可查表來確定與其值相近的表格偏移位址 和數值,計算後,Senso� 輸出電壓值需取至小數點後兩位,以增加準確度。再通過單位轉換, 取運算結果的高三位有效值顯示於 LCD。 ■ 注意事項 9.3.1 LCD 顯示介面(如圖三): 設其自左往右的排佈為:數據位 1,Dot1,數據位 �,Dot�,數據位三,並列的四個轉換單位。 則其與程式相對應的分佈如下表: (圖三) Rev. 1.30 104 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Pin Seg0 Seg1 Seg2 Seg3 Seg4 Seg5 Seg6 Seg7 Seg8 Com0 1f 1a Ba� �f �a Kpa 3f 3a Kgf/�m² Com1 1e 1g 1b �e �g �b 3e 3g 3b Com� 1d 1� Dot1 �d �� Dot� 3d 3� Psi 9.3.2 請注意要根據所選用 Senso� 的規格來設置 Amplifie� 的放大倍數,Senso� 輸出信號經 Amplifie� 放大後,應不超過 4/6VDSO。 9.3.3 要選用合適的充放電電阻 V�,電容 V� 和充電時間 Ti,使 V� 電壓介於 1/6VDSO 和 �/6VDSO 之間。 9.3.4 為增加準確度,建議使用時多量測些組數據來建成表格。 9.3.5 充電時間改變,Regula� 3.3V 穩壓輸出不同等因素都會影響到放電時間值,應注意 Senso� Output Voltage - Dis��a�ge Time (V-T) 表格作相應校準。 有兩個中斷控制與狀態暫存器 � INTC0� INTC1� 可用來產生 ADC 完成後的中斷事件其他有關 的暫存器及定義列於後面 � 包括 CHPRC (C�a�ge Pump/Regulato� Cont�ol) 暫存器用來控制 ��a�ge pump 及電壓調整器的開關功能 . Bit No. Label Function 0 E�I Cont�ol t�e maste� (global) inte��upt (1=enabled; 0=disabled) 1 EEI0 Cont�ol t�e exte�nal inte��upt 0 (1=enabled; 0=disabled) � EEI1 Cont�ol t�e exte�nal inte��upt 1 (1=enabled; 0=disabled) 3 ET0I Cont�ol t�e Time�/Event Counte� 0 inte��upt (1=enabled; 0=disabled) 4 EIF0 Exte�nal inte��upt 0 �equest flag (1=a�tive; 0=ina�tive) � EIF1 Exte�nal inte��upt 1 �equest flag (1=a�tive; 0=ina�tive) 6 T0F Inte�nal Time�/Event Counte� 0 �equest flag (1=a�tive; 0=ina�tive) 7 — Fo� test mode used only. �ust be w�itten as "0"; ot�e�wise may �esult in unp�edi�table ope�ation. INTC0 (0BH) Register Bit No. Label 0 ET1I Cont�ol t�e Time�/Event Counte� 1 inte��upt (1=enabled; 0=disabled) 1 EADI Cont�ol t�e ADC inte��upt (1=enabled; 0:disabled) � ERTI Cont�ol t�e �eal time �lo�k inte��upt (1=enabled; 0:disabled) 3� 7 Function Unused bit� �ead as "0" 4 T1F Inte�nal Time�/Event Counte� 1 �equest flag (1=a�tive; 0=ina�tive) � ADF ADC �equest flag (1=a�tive; 0=ina�tive) 6 RTF Real time �lo�k �equest flag (1=a�tive; 0=ina�tive) INTC1 (1EH) Register Bit No. Label 0 REGCEN 1 CHPEN � 3~7 BGPQST Function Enable/disable Regulato�/C�a�ge-Pump module. (1=enable; 0=disable) C�a�ge Pump Enable/disable setting. (1=enable; 0=disable) Note: t�is bit will be igno�ed if t�e REGCEN bit is disabled Bandgap qui�k sta�t-up fun�tion 0: R s�o�t� qui�k sta�t up 1: R �onne�ted� no�mal RC filte� mode Ea�� time REGCEN ��anges f�om 0 to 1� t�at is w�en t�e �egulato� tu�ns on� t�is bit s�ould be set to 0 and t�en set to 1 to ensu�e a qui�k sta�t up. T�e minimum time to keep t�e bit low s�ould be about �ms. CHPCKD0~ C�a�ge pump �lo�k divide�. T�ese � bits fo�m a �lo�k divide� wit� a division CHPCKD4 �atio �ange of 1 to 3�. C�a�ge Pump �lo�k = (fSYS/16) / (CHPCKD+1) CHPRC (1FH) Register Rev. 1.30 105 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 REGCEN CHPEN Charge VOCHP Regulator Pump Pin VOREG Pin OPA ADC Description Complete module is HiDisable disabled� OPA/ADC will �ave Impedan�e no Powe� 0 X OFF VDD OFF 1 0 OFF VDD ON 3.3V A�tive Used w�en V DD is g�eate� t�an 3.6V 1 1 ON �xVDD ON 3.3V A�tive Use w�efo� VDD is less t�an 3.6V (VDD=�.�V~3.6V) Bit No. 0 1~� 3 4~� 6 7 Bit No. Label Function Dual slope blo�k (in�luding input OP) powe� on/off swit��. ADPWREN 0: disable Powe� 1: Powe� sou��e �omes f�om t�e �egulato�. Defines t�e ADC dis��a�ge/��a�ge. 00: �ese�ved ADDISCH0~ 01: ��a�ging - Integ�ato� input �onne�t to buffe� output ADDISCH1 10: dis��a�ging - Integ�ato� input �onne�t to VDSO) 11: �ese�ved ADC�PO Dual Slope ADC - last stage �ompa�ato� output. Read only bit� w�ite data inst�u�tions will be igno�ed. Du�ing t�e dis��a�ging state� w�en t�e integ�ato� output is less t�an t�e �efe�en�e voltage� t�e ADC�PO will ��ange f�om �ig� to low. ADC integ�ato� inte��upt mode definition. T�ese two bits define t�e ADC�PO data inte��upt t�igge� mode: ADINT�0~ 00: no inte��upt ADINT�1 01: �ising edge 10: falling edge 11: bot� edge ADC OP ��oppe� �lo�k sou��e on/off swit��. ADCCKEN 0: disable 1: enable (�lo�k value is defined by ADCD �egiste�) ADRR0 ADC �esisto� sele�tion 0: (VINT� VC�P)= (4/6 VOREG� 1/6 VOREG) 1: (VINT� VC�P)= (4.4/6 VOREG� 1/6 VOREG) Label 0 1 � ADCD0 ADCD1 ADCD� 3~7 — Function Defines t�e ��oppe� �lo�k. ADCCKEN s�ould be enabled. T�e suggested �lo�k value s�ould be a�ound 10kHz.T�e ��oppe� �lo�k definitions a�e: 0: �lo�k= (fSYS/3�)/1 1: �lo�k= (fSYS/3�)/� �: �lo�k= (fSYS/3�)/4 3: �lo�k= (fSYS/3�)/8 4: �lo�k= (fSYS/3�)/16 �: �lo�k= (fSYS/3�)/3� 6: �lo�k= (fSYS/3�)/64 7: �lo�k= (fSYS/3�)/1�8 Rese�ved ADCD (1AH) Register 暫存器 ADCR( 位址 18H) 與 ADCD( 位址 1AH) 則是控制 Dual Slope A/D 轉換器的開關 � 充放 電的設定 � ADC 電阻的選定以及 ��oppe� 的頻率設定 . Rev. 1.30 106 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 9.4 微控制器的架構設定 (Configuration Options) //-----------------------------------------------------------// Configu�ation Options: //----------------------------------------------------------//Os�: RC //fSYS: 4�Hz //Wake_Up PA.0-7: PA.0 Wake_Up // PA.1-PA.7 Non Wake_Up //Pull-�ig� PA: PULL-HIGH //Pull-�ig� PB: PULL-HIGH //Pull-�ig� PC: PULL-HIGH //Pull-�ig� PD: PULL-HIGH //PA Buzze� Fun�tion: BZ/BZB Disable //Clo�k Sou��e: T1 //Wdt: Disable //WDT Clo�k Sou��e: T1 //CLR WDT: One �lea� inst�u�tion //WDT time-out pe�iod: �^1�/fs-�^13/fs //LVR: Disable //LVD: Disable //Int Fun�tion: Disable //LCD D�ive� Clo�k: IRCOSC/3 //LCD ON/OFF At HALT: LCD OFF At HALT //LCD Duty: 1/3 duty(3 �om) //LCD Bias: 1/� bias //----------------------------------------------------------- Rev. 1.30 107 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 9.5 程式流程圖 Rev. 1.30 108 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Rev. 1.30 109 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Rev. 1.30 110 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Rev. 1.30 111 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Rev. 1.30 112 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 9.6 原始程式與程式說明 main.� 1 #in�lude <�t46�74d-1.�> � #in�lude “pubdef.�" 3 4 void Initial�CU(); // initial �CU � unsigned int SBR_ADC(); 6 void SBR_CALT_LOOP(); 7 void SBR_TABLE(); 8 unsigned long SBR_Cal�ulate();//�al�ulate t�e p�essu�e value wit� diffe�ent unit 9 void SBR_Display(unsigned int); //display 10 void SBR_KEY_SCAN(); //dete�t key 11 void SBR_KEY_JU�P(); //deal wit� key 1� 13 unsigned ��a� Cu�State ; // �u��ent state 14 unsigned int Tm�1Count ; // T�R1 �ount 1� unsigned int Dis��a�geTime ; // dis��a�ge time 16 17 #p�agma �ambank0 18 bit bHalt ; // �alt flag 19 exte�n bit bKeyP�ev ; �0 #p�agma no�ambank �1 �� void main(void) �3 { �4 unsigned ��a� Se�onds ; �� unsigned int TotalTime� num ; �6 unsigned long p�essu�e ; �7 ��a� i ; �8 �9 Initial�CU() ; 30 Cu�State |= POWER_ON ; //set powe� on o� wake up state 31 3� w�ile(1) 33 { 34 Se�onds = 0 ; 3� TotalTime = 0L ; 36 Dis��a�geTime = 0 ; 37 38 // �al�ulate t�e ave�age value of dis��a�ge time - ave�age of 10 times 39 fo�( i = 0 ; i < 10 ; i++ ) 40 { 41 // get t�e dis��a�ge time T� du�ing t�e ��a�ge time Ti be fixed 4� TotalTime += SBR_ADC() ; 43 } 44 4� Rev. 1.30 Dis��a�geTime = (TotalTime / 10) ; // get t�e ave�age time 113 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 46 w�ile(1) 47 { 48 SBR_TABLE(); // to get t�e offset add�ess of table wit� di��otomy 49 p�essu�e = SBR_Cal�ulate(); // �al�ulate t�e p�essu�e value wit� �0 // diffe�ent unit �1 num = (unsigned int)p�essu�e ; �� SBR_Display(num); // display �3 �4 if( bHalt ) //if at t�e mode of �alt�jump to t�e �alt mode �� { �6 LED_IO = 0 ; // PA1 �7 _�alt(); //�alt �8 bHalt=0; //wake up �9 bKeyP�ev = 1; //set t�e p�evious key flag 60 _��p�� = 0x63; //set 3.3V �egula� output 61 _ad��=0x41; //set t�e �elated �egisto� 6� _ad�d=0x07; 63 _tm�1�=0x88; 64 Cu�State = (PSI_STATE|POWER_ON) ; // set to 'psi' unit & powe� 6� // on/wake up 66 LED_IO=1; 67 b�eak ; 68 } 69 70 SBR_KEY_SCAN(); //dete�t key 71 SBR_KEY_JU�P(); //deal wit� key 7� if(++Se�onds == �0 ) //1S timing�jump to test t�e dis��a�ge time 73break ; 74 } 75} 76} Rev. 1.30 114 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 main.c 1 引入標頭檔 ht46r74d-1.h 其中定義微控制器的暫存器位址 2 引入標頭檔 public.h, 定義常數 , 宣告全域變數 (global variables) 4~11 宣告各函式及參數的資料型態 13~15 定義全域變數 17~20 bit 型態的變數必須定義在 RAM bank 0, 因此定義或宣告的前後需要加上前置詞 #pragma rambank0 與 #pragma norambank 22 主程式的開始 24~27 宣告區域變數 (local variables) 29 呼叫 InitialMCU 函式 , 執行 RAM 記憶體空間的清理及 I/O 輸出入的設定 30 設定目前的狀態為 POWER ON 33~75 主迴圈 . 程式進入此迴圈後 , 則一直在此迴圈中執行 , 不會跳出來 34~36 設定變數的初始值 , Seconds, TotalTime 與 DischargeTime 歸零 39~43 迴圈 10 次 , 在固定之充電時間的條件下 , 取得 10 次放電所需的時間 Tc, 並加總 45 將 10 次放電總時間除以 10, 取得平均放電時間 46~74 內迴圈 48 根據上述計算出的平均放電時間 , 到 VT 表格中找到對應的時間 ( 及電壓 ) 49 呼叫函式 SBR_Calculate() 計算出此放電時間對應的胎壓值 , 並根據當時所選定的單 位 , 將胎壓值劃分成三個十進位數字 52 呼叫函式 SBR_Display(num) 將此數字顯示於 LCD 上 54 在 60 秒內若無按鍵被按下時 , 則系統會進入休眠狀態 bHalt 旗標會被設為 1 55 系統將進入休眠狀態 56 將工作指示燈 (LED) 熄滅 57 系統進入休眠暫停 . 一直等到有按鍵被按下後 , 系統才被叫醒 , 並且回到此處之後 58~59 系統被叫醒後 , 回到此處 . 將暫停的旗標清為零 , 按鍵旗標設為 1 60~63 重設系統架構為 3.3V 的電壓調整輸出 , AD dual slop 的初始值 , 時鐘初始值 64 設定目前的狀態為 胎壓單位是 PSI 而且系統是 POWER ON 66 將工作指示燈 LED 點亮 67 跳回主迴圈 , 再次讀取按鍵或放電時間 70 如果系統並非於休眠狀態 , 則繼續檢查是否有按鍵輸入 . 呼叫函式 SBR_KEY_SCAN 71 呼叫函式 SBR_KEY_JUMP 檢查及處理輸入按鍵 , 並設定相關旗標 72 如已經過一秒的時間 , 則重回主迴圈再次檢測放電時間及狀態 . 否則即繼續內迴圈顯示 上回的壓力值及檢測輸入按鍵 subroutine.c 原始程式 1 #include <ht46r74d-1.h> 2 #include “pubdef.h” 3 unsigned long PresUnit ; 4 unsigned char DischargeIdx ; 5 unsigned char AdjustValue ; 6 unsigned char AdjustIdx ; 7 unsigned char LcdBuf[0x10] @ 0x140 ; 8 9 #pragma rambank0 10 bit bLED_Dot1 ; 11 bit bLED_Dot2 ; 12 bit bKeyPrev ; 13 bit bKeyFlag ; Rev. 1.30 115 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 14 bit bRepeat ; 15 #pragma norambank 16 17 extern unsigned char CurState ; // current state 18 extern unsigned int Tmr1Count ; // TMR1 count 19 extern unsigned int DischargeTime ; // discharge time 20 21 const unsigned char LEDDigitalPattern[11] = 22 { 23 0x5f,0x48,0x3e,0x7c,0x69, /*0~4*/ 24 0x75,0x77,0x58,0x7f,0x7d, /*5~9*/ 25 0x20 /*’-’*/ 26 } ; 27 28 const unsigned int VT_Table[TABLE_NUM] = { 29 /* ************* V-T table *************** */ 30 31 32 0x197E, 0x180b, 0x16ce, 0x1590, 0x1453, /*0,5,10,15,20;;0~4mv*/ 0x1315, 0x11d8, 0x109a, 0x0f5d, 0x0E1f, /*25,30,35,40,45;;5~9mv*/ 0x0ce2, 0x0ba4, 0x0a66 /*50,55,60;;10~14mv*/ 33 }; 34 void InitialMCU() 35 { 36 char i ; 37 char *mptr ; 38 39 ClearMemory() ; // 清除 RAM bank 40 41 42 _intc0 = 0; 43 _intc1 = 0 ; // interrupt disable 44 _chprc = 0x63; // set 3.3V regular output, clock divider=12 45 _adcr = 0x41; // set the related registor 46 _adcd = 0x07; 47 _tmr1c = 0x88; // set TMR1 : Timer mode, prescale stages: f(int1)=f(t1) 48 49 _pac=0xff; // set PA to input mode 50 _pbc=0xff; // set PB to input mode 51 LED_IO_C=0; // set PA.1 to output mode 52 LED_IO=1; // turn LED off 53 54 CurState = PSI_STATE ; // initial state : PSI unit 55} Rev. 1.30 116 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 InitialMCU 此函式是對系統做初始設定 3~6 義 全 域 變 數 PresUnit ( 單 位 壓 力 ), DischargeIdx ( 放 電 時 間 所 在 的 表 格 所 引 數 ), AdjustValue ( 調整值 ), AdjustIdx ( 調整值的表格索引數 ) 7 定義 LCD 的記憶體緩衝區 , 從位址 0x140 到 0x14F 9~15 使用前置詞定義 bit 型態的全域變數 , 此類型態的變數必須定義在 RAM 的 bank 0 如 果使用 HI-TECH C 編譯器 , 則必須將第 9 與第 15 行刪除 17~19 宣告使用的外部變數 CurState, Tmr1Count, DischargeTime �1~�6 定義常數型的 LED 字型表格 LEDDigitalPatte�n� 代表數字 `0' ~ `9' 與 `-' 28~33 定義常數型的 VT 表格 , Tc 時間從大到小 , 代表氣壓從小到大 34~44 函式 InitialMCU, 將 RAM 記憶體 bank 0 與 bank 1 設定為 0 設定其他暫存器的初始值 . 關閉硬體中斷功能設定為 3.3V regular output 與 clock divider 為 12 45 設定 ADCR 暫存器 (0x18) 為放電狀況 , power source 是從 regulator 提供 46 設定 ADCD 暫存器 (0x1A), 設定 chopper 的頻率為 ((fSYS/32)/128) 47 設定時鐘 1 為計時模式 (Timer mode) 與 prescale stages 為 f(int1)=f(t1) 49~50 設定 I/O 的輸出 / 輸入 , 埠 A 及埠 B 皆為輸入模式 51 工作指示燈 ( 由埠 A 的第一位元控制 ), 設為輸出模式 52 將工作指示燈熄滅 54 設定現狀為使用 PSI 做為氣壓單位 原始程式 SBR_ADC() //----------------------------------------------//--- SBR_ADC --// get the discharge time Tc during the charge time Ti be fixed // O/P: time //----------------------------------------------1 unsigned int SBR_ADC() 2{ 3 unsigned int T�Time ; 4 � _t1on=0; // tu�n T�R1 off 6 _tm�0�=0x8f; // 3�us(fSYS=4�Hz)� T�R0 -> time� mode� fSYS 7 _tm�0=0; 8 _ad��=0x4b; // at ��a�ge mode� ADC OP ��oppe� �lo�k sou��e 9 _t0on=1; // T�R0 on 10 11 1� w�ile(!_t0f) ; //��a�ge and let V� wo�k ove� 4/6 VDSO� T�R0 //inte��upt (delay) 13 _t0f=0; 14 _t0on=0; 1� _ad��=0x4d; // set : dis��a�ge mode 16 w�ile(_ad�mpo) ; // ADC�PO �ig�->low if integ�ato� output is less 17 // t�an t�e �efe�en�e voltage Rev. 1.30 18 _tm�0=0x83; 19 _ad��=_ad��^0x06; // set : ��a�ge mode �0 _t0on=1; // t�un T�R0 on �1 _t1on=0; 22 _tmr1l=0; // clear TMR1 117 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 23 _tmr1h=0; 24 while(!_t0f) ; // delay for charge 25 26 _adcr=_adcr^0x06; // set: discharge mode 27 _t1on=1; // TMR1 on, TMR0 off 28_t0on=0; 29_t0f=0; 30 while(_adcmpo) ; //discharge 31 _t1on=0; // TMR1 off 32 33 // return discharge time 34 TcTime = (unsigned int)(_tmr1h) ; // read TMR1 high byte 35 TcTime = (TcTime<<8) | _tmr1l ; // read TMR1 low byte and append to int 36 // variable 37 return( TcTime ) ; // get discharge time 38 } ■ 程式說明 此函式是從 AD dual slope 取得在固定充電時間下的放電時間 , 以轉換成氣壓數值 5 將時鐘 1 停止 6 設定時鐘 0 為計時模式 (timer mode), 系統 (fSYS) 提供時鐘振盪來源 , 預除比率為 128 ( 當系統頻率為 4MHz 時 , 時鐘 0 的 clock tick 為 32us) 7 設定時鐘 0 的計數暫存器為 0 8 啟動 ADC OP chopper, 進入充電模式 9 啟動時鐘 0, 開始計時 (256*32us=8ms) 11 以輪詢方式 (polling) 等候時鐘 0 產生溢位中斷 ( 等候充電完成 ) 13 清除中斷旗標 14 將時鐘 0 停止計時 15 設定 ADC OP chopper 為放電模式 16 檢查 ADC dual slope 的 last stage comparator 的輸出是否低於參考電壓 以迴圈等候比較器的輸出低於參考電壓時即離開迴圈 18 設定時鐘 0 的計數暫存器 (0x83) 19 啟動 ADC OP chopper, 進入充電模式 20 啟動時鐘 0 的計時功能 21~23 關閉時鐘 1, 將時鐘 1 的計數暫存器歸零 24 等候時鐘 0 產生溢位中斷 (0xff-0x83+1)*32= 4ms) 26 啟動 ADC OP chopper, 進入放電模式 27 打開時鐘 1 28 關閉時鐘 0 29 清除時鐘 0 的中斷旗標 30 檢查 ADC dual slope 的 last stage comparator 的輸出是否低於參考電壓 以迴圈等候比較器的輸出低於參考電壓時即離開迴圈 31 關閉時鐘 1 34~35 將時鐘 1 的計數暫存器內容讀出 ( 即為放電時間 ) 37 Rev. 1.30 回傳放電時間 118 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 原始程式 SBR_TABLE //---------------------------------------------------------//--- SBR_TABLE --// get t�e opposite offset add�ess of table wit� di��otomy // if Dis��a�geTime >= VT_TABLE[i]� t�en i-1 is t�e �anditate // t�e o�de� in VT_TABLE[] is de��easing o�de� //----------------------------------------------------------1 void SBR_TABLE() �{ 3 ��a� i ; 4 � AdjustValue = 0 ; // adjust value 6 AdjustIdx = 0 ; // adjust index 7 8 fo�( i = 0 ; i < TABLE_NU� ; i++ ) 9 { 10 if( Dis��a�geTime >= VT_Table[i] ) // find t�e p�ope� dis��a�ge time 11 { 1� Dis��a�geIdx = !i ? i : i-1 ; 13 Cu�State &= ~KEY_TE�P ; // �eset key temp flag 14 Cu�State &= ~FULL_OUTPUT ; // �lea� FULL output state 1� if( !i ) 16 Cu�State |= ZERO_OUTPUT ; // set ze�o output 17 18 if( (Cu�State & POWER_ON) != 0 ) // powe� on state 19 Cu�State &= ~POWER_ON ; // �lea� bit �0 �1 �� �etu�n; } �3 } �4 �� // i = TABLE_NU� �6 Dis��a�geIdx = TABLE_NU� -1 ; �7 Cu�State &= ~( KEY_TE�P | ZERO_OUTPUT) ; // �eset key temp flag & ze�o output �8 Cu�State |= FULL_OUTPUT ; // set FULL output �9 if( (Cu�State & POWER_ON) != 0 ) 30 { 31 Cu�State &= ~POWER_ON ; 3� AdjustIdx = Dis��a�geIdx ; 33 } 34 } Rev. 1.30 119 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ■ 程式說明 此函式是從 VT 表格中找出與實際放電時間相等的時間或是介於兩相鄰時間之間 . VT 表格共有 13 個時間參考值 � 相對應於不同的氣壓 . 將找到的時間存入參考時間變數 � 以及將此時間位於 表格的索引值 (index) 存入參考指標 �~6 將校正值及校正指標設為 0 8~�3 迴圈 � 從 VT 表格中找尋與實際放電時間相等或相近的數值 此表格共有 13 個預先測量好的壓力 - 放電時間對應值 � 放電時間從大排列到小檢查的 順序是從表格的第一個數值開始比對 � 直到最後一個 . 如果中間有符合條件的數值 � 則 會停止比較 � 並返回原呼叫處 10~�� 如果放電時間大於或等於表格中的數值 � 則表示已找到記錄參考指標 (Dis��a�geIdx) 為此數值所在的表格索引值 (index) 刪除按鍵旗標放電時間不會小於表格的最後一個數 值 � 所以氣壓不會超出表格的最高 � 清除旗標 FULL_OUTPUT ( 超過最高氣壓 ) 如果放 電時間大於或等於表格中第一個數值 � 代表氣壓值低於表格內的 13 個有效值所以設定 旗標 ZERO_OUTPUT� 代表氣壓小於最低標準 18~19 若目前的工作狀態是 POWER ON, 則清除 POWER ON 旗標 21 返回原呼叫處 25 若執行到此處 , 則代表放電時間低於表格內的數值 ( 氣壓超出 ) 26 設定參考指標 (DischargeIdx) 為表格的最後一個索引值 27 清除 KEY_TEMP 與 ZERO_OUTPUT 旗標 28 設定 FULL_OUTPUT 旗標 29~33 若目前的工作狀態是 POWER ON, 清除 POWER ON 旗標 , 設定校正指標 34 返回原呼叫處 ■ 原始程式 SBR_CALCULATE //----------------------------------------------//--- SBR_Calculate --// calculate pressure with different unit and pressure number // O/P: pressure number //----------------------------------------------1 unsigned long SBR_Calculate() 2{ 3 unsigned long rtmp, rtmp1, Tslice ; 4 unsigned char rate ; 5 6 if( (CurState & ZERO_OUTPUT) == ZERO_OUTPUT ) // it's Zero output 7 { //for zero output of sensor 8 bLED_Dot1 = 1 ; //to display 0.00 9 bLED_Dot2 = 0 ; 10return(0L) ; 11 } 12 13 if( (CurState & FULL_OUTPUT) == FULL_OUTPUT ) 14 { 15 DischargeIdx -= AdjustIdx ; //for full output of sensor 16rtmp = 0 ; 17} 18 else 19 { �0 Rev. 1.30 Tsli�e = (unsigned long)((VT_Table[Dis��a�geIdx] – Dis��a�geTime) * 120 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 �1 100) ; �� �ate = (unsigned ��a�)(Tsli�e / (VT_Table[Dis��a�geIdx] - �3 VT_Table[Dis��a�geIdx+1])) ; �4 �� if( Cu�State & POWER_ON ) �6 { �7 Cu�State &= ~POWER_ON ; // �lea� flag �8 AdjustValue = �ate ; �9 AdjustIdx = Dis��a�geIdx ; 30 } 31 3� if( �ate < AdjustValue ) 33 { 34 if( Dis��a�geIdx >= AdjustIdx ) 3� { 36 bLED_Dot1 = 1 ; //to display 0.00 37 bLED_Dot� = 0 ; 38 �etu�n(0L) ; 39 } 40 Dis��a�geIdx--; 41 } 4� else �ate -= AdjustValue ; 43 44 if( Dis��a�geIdx < AdjustIdx ) // Dis��a�geIdx<R_ADJUST_H 4� { 46 bLED_Dot1 = 1 ; //to display 0.00 47 bLED_Dot� = 0 ; 48 �etu�n(0L) ; 49 } �0 �1 Dis��a�geIdx -= AdjustIdx ; // Dis��a�geIdx > R_ADJUST_H �� SBR_CALT_LOOP(); // O/P: P�esUnit �3 �tmp = (P�esUnit * �ate) / 100 ; �4 } �� �6 SBR_CALT_LOOP(); �7 �tmp += P�esUnit * Dis��a�geIdx ; �8 �tmp1 = �tmp / (PER * TABLE_PER) ; // 1� * 100 R_TO0� R_TO1 = �tmp1 �9 60 if( �tmp1 > 100 ) // �tmp1 > 100 61 { 6� bLED_Dot1 = bLED_Dot� = 0 ; 63 �etu�n(�tmp1) ; 64 } 6� if( �tmp1 > 10 ) // 100>= �tmp > 10 66 { 67 Rev. 1.30 bLED_Dot1 = 0 ; //>10 & <100 121 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 68 bLED_Dot� = 1 ; 69 70 �etu�n( �tmp / (10 * TABLE_PER) ) ; } 71 7� bLED_Dot1=1; 73 bLED_Dot�=0; 74 �etu�n( �tmp / TABLE_PER ) ; 7� } ■ 程式說明 SBR_CALCULATE 此函式將根據上述找到的參考時間 � 參考指標及實際放電時間 � 配合所選定的氣壓單位計算出氣 壓值 . 計算原理是 VT 表格共有 13 個時間值 � 代表從氣壓 0.00 到最高氣壓值的 1� 個相等間隔 � 各氣壓單位有其滿量值 . 因此第 n 個間隔的氣壓值則是 (n=0�1�..1�)n * ( 滿量值 / 1�) 而實際放 電時間不一定剛好等於某個參考時間 ( 會大於某個參考時間 ) 所以要將此小部分的氣壓加入 � 先 計算它佔此間隔的百分比 � 再乘以間隔的平均氣壓值 . 由於計算時使用 ��a�� long 等整數型態的 變數 ( 可考慮使用 float 型態 ) 所以會先做乘法運算 � 將數值擴大 100 倍 . 其公式如下 rate = [( 參考時間 – 實際放電時間 ) * 100] / [ 參考時間 – 下一個參考時間 ] pressure1 = [rate * ( 滿量值 /12)] / 100 (rate 與滿量值皆已乘上 100) [ 參考時間 – 下一個參考時間 ] 是指實際放電時間介於的範圍 , 也就是 參考時間 > 實際放電時間 >= 下一個參考時間 程式中的滿量值已經乘過 100, 以便與 presure1 相加 . 所以最後總值要除以 100 最後的總氣壓 值 = (n * ( 滿量值 /12) + pressure1) / 100 程式中的計算順序有些不同 , 原理則相同 7~11 如果旗標 ZERO_OUTPUT 被設定 , 代表放電時間超過 VT 表格的最高值 ( 氣壓低於 有效值 ). 無法顯示壓力數值 , 所以設定顯示 0.00 第一個小數點要顯示返回 0 數值 13~17 如果旗標 FULL_OUTPUT 被設定 , 代表放電時間低於 VT 表格的最低值 ( 氣壓高於表 格中的有效值 ) 將參考指標 減去 校正值指標設定氣壓偏量為 0, 並跳到第 57 行 , 計 算總氣壓值 18~54 不是 ZERO, 也不是 FULL. 表示放電時間介於表格中的數值 , 執行下列 20~23 計算 氣壓偏量比率 rate = (( 參考時間 – 實際放電時間 ) * 100) / 參考時間差參考時 間差 = VT_Table[ 參考指標 ] - VT_Table[ 參考指標 +1] 25~30 如果目前之工作狀態為 POWER_ON, 則清除 POWER_ON 旗標 , 設定校正值 = 氣壓偏量比率 . 設定校正指標 32~41 如果氣壓偏量比率小於校正值 , 則檢查參考指標 如果參考指標小於或等於校正指標 , 則氣壓顯示值設為 0.00 設定要顯示第一個小數點 , 返回呼叫處否則 , 將參考指標減一 42 如果氣壓偏量比率大於校正值 , 則將氣壓偏量比率減去校正值 44~49 檢查參考指標 , 如果小於校正指標 , 則顯示值為 0.00 設定要顯示第一個小數點 , 返回呼叫處 51 參考指標減去校正指標 52 取得目前的氣壓單位的滿量值 53 計算 氣壓偏量值 = ( 氣壓偏量 * 滿量值 ) / 100 ( 此時並未除以 12) 56 SBR_CALT_LOOP() 取得氣壓單位的滿量值 57~58 氣壓總值 = [ 氣壓偏量值 + ( 滿量值 * 參考指標 )] / (12*100) 除以 12 是滿量值分成均等的 12 間隔 , 100 則是計算過程中放大 , 此時要改回 60 根據氣壓總值 , 決定是否要顯示小數點 60~64 如果氣壓值大於或等於 100 ( 有三位數字 )� 則不需顯示任一小數點返回此氣壓值 Rev. 1.30 122 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 6�~70 如果氣壓值介於 99 與 10 之間 ( 兩位數 )� 則需要顯示第二個小數點 同時將氣壓總值重計算為 只除以 10 ( 取三位數字 )� 在返回原呼叫處 氣壓總值 = [ 氣壓偏量值 + ( 滿量值 * 參考指標 )] / (1�*10) 7�~74 如果氣壓總值介於 9 與 1 之間 � 則需要顯示第一個小數點 Dot1=1 同時氣壓總值不需要除以 100� 而改為 氣壓總值 = ( 氣壓偏量值 + ( 滿量值 * 參考指標 )) / 1� 返回原呼叫處 ■ 原始程式 SBR_CALT_LOOP //----------------------------------------------//--- SBR_CALT_LOOP --//SBR fo� SBR_CALT get t�e p�essu�e of �u��ent unit //----------------------------------------------1 void SBR_CALT_LOOP(void) �{ 3 swit��( Cu�State & 0x0f ) // ��e�k bit0~4 4 { � �ase PSI_STATE : // if unit is Psi 6 P�esUnit = PSI_FULL ; // unsigned long 7 b�eak ; 8 �ase BAR_STATE : //if Ba� 9 P�esUnit = BAR_FULL ; 10 b�eak ; 11 �ase KPA_STATE : //if Kpa 1� P�esUnit = KPA_FULL_H16 ; 13 P�esUnit = KPA_FULL | (P�esUnit << 16) ; 14 b�eak ; 1� �ase KGFC��_STATE : //if Kgf/�m� 16 P�esUnit = KGFC��_FULL ; 17 b�eak ; 18 } 19 } ■ 程式說明 SBR_CALT_LOOP 此函式將依照目前指定之氣壓單位 , 給予此單位的滿量程數值 3 分析目前所指定的氣壓單位 5~7 單位是 PSI, 將 PSI 的滿量程數值給予變數 PresUnit 8~10 單位是 Bar, 將 Bar 的滿量程數值給予變數 PresUnit 11~14 單位是 Kpa, 將 Kpa 的滿量程數值給予變數 PresUnit 1�~17 單位是 Kgf/�m²� 將 Kgf/�m² 的滿量程數值給予變數 P�esUnit 原始程式 SBR_KEY_SCAN //----------------------------------------------//--- SBR_KEY_SCAN --//--- detect key --//----------------------------------------------1 void SBR_KEY_SCAN() �{ 3 unsigned ��a� Deboun�e_�ount ; 4 Rev. 1.30 123 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 � Deboun�e_�ount = R_�0�S ; // dete�t key fo� �0mS 6 Cu�State &= ~KEY_TE�P ; // �lea� KEY_TE�P flag 7 _tm�0� = 0x0A0; // set T�R0 to Time� mode� inte�nal �lo�k (Intl. 8 // RC)� disable 9 w�ile(1) 10 { // wait T�R0 time out 11 _tmr0 = 208 ; // 4mS 12 _t0on = 1 ; // turn TMR0 on 13 while( !_t0f ) ; // wait internal timer/event counter 0 14 // interrupt flag 15 _t0f = 0 ; // clear internal timer/event counter 0 request flag (0b.�) 16 _t0on = 0 ; // tu�n T�R0 off 17 // ��e�k input key 18 bKeyFlag = KEY_IO ? 0 : 1 ; // ��e�k if key p�ess o� not(key no)� PA.0 19 // set flag �0 if( (bKeyFlag && (Cu�State & KEY_TE�P)==0) || �1 (!bKeyFlag && (Cu�State & KEY_TE�P)!=0) ) �� { �3 Cu�State ^= KEY_TE�P ; // toggle flag KEY_TE�P �4 Deboun�e_�ount = R_�0�S; // �enew �� �ontinue ; �6 } �7 �8 if( --Deboun�e_�ount == 0 ) �9 30 b�eak ; } 31 3� Deboun�e_�ount = R_�0�S; 33 if( bKeyFlag ) // input key 34 { 3� if( bKeyP�ev ) // �as p�essed p�eviously 36 bRepeat = 1; // set �epeat flag 37 else 38 { 39 bRepeat =0; 40 bKeyP�ev = 1; 41 } 4� } 43 else // no input key 44 { 4� if( bKeyP�ev ) 46 bRepeat = bKeyP�ev = 0 ; 47 else 48 49 bRepeat = 1; } �0 �1 Rev. 1.30 Cu�State &= ~(ZERO_OUTPUT | FULL_OUTPUT | KEY_TE�P) ; // �lea� flags 124 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 52 } ■ 程式說明 SBR_KEY_SCAN 檢測是否有按鍵輸入 5 設 定 檢 測 時 間 為 20ms ( 時 鐘 0 每 隔 4ms 中 斷 一 次 , Debounce_count=5 共 5 次 ,20ms) 檢測在此時間內按鍵確實被按下 ( 消除按鍵彈跳的誤認 ) 6 清除目前工作狀態的 KEY_TEMP 旗標 7 設定時鐘 0 為計時模式 , 使用內部時鐘振盪 (12KHz), prescaler 1:1 暫停計時 9~30 迴圈 , 等候按鍵輸入 11 設定時鐘 0 的中斷時間間隔為 4ms ( TMR0 = 208) (256-208)*(1/12KHz)=4ms 12 啟動時鐘 0 13 迴圈 , 等候時鐘 0 產生溢位中斷 15 清除時鐘 0 的中斷要求旗標 16 停止時鐘 0 的計時 18 檢查是否有按鍵輸入 ( 埠 A 位元 0) 並設定旗標 bKeyFlag 20~26 如果此次有按鍵輸入但前次並無按鍵 或是前次有按鍵輸入而此次無輸入代表沒有確實 有效的按鍵輸入則將目前工作狀態變更為此次的按鍵狀態 , 重設檢測時間回到迴圈開 始 , 繼續檢查按鍵狀態 28~29 如果檢測時間已減到零 , 則跳離迴圈 30 否則就繼續檢測按鍵 32 設定檢測為 5 次 33~42 如果按鍵輸入被確認有效 , 分析輸入按鍵的狀態 35~36 如果此鍵先前已按過 , 則設定 bRepeat 旗標 ( 重複 ) 37~41 否則 , 清除 bRepeat 旗標 , 設定 bKeyPrev 旗標 ( 已有按鍵輸入 ) 43~49 若沒有按鍵輸入 , 分析狀態如下 45~46 如果前次有按鍵輸入 , 則清除旗標 bRepeat 與 bKeyPrev 47~48 否則 , 設定旗標 bRepeat 51 清除目前工作狀態中的旗標 ZERO_OUTPUT, FULL_OUTPUT 與 KEY_TEMP 52 返回原呼叫處 ■ 原始程式 SBR_KEY_JUMP //----------------------------------------------//--- SBR_KEY_JUMP --//--- deal with key --//----------------------------------------------1 unsigned int ElapseTime ; 2 3 void SBR_KEY_JUMP() 4{ 5 unsigned char tmp ; 6 7 if( !bKeyFlag ) // no input key 8 { 9 ElapseTime++ ; 10 if( ElapseTime >= 0x0bb8 ) // 60 seconds 11 bHalt = 1 ; // set halt flag 12return ; 13} Rev. 1.30 14 // has input key 15 ElapseTime = 0 ; 125 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 16 if( !bRepeat ) // if no repeat key, advance to next unit 17{ 18 tmp = CurState & 0x0f; // current unit 19 tmp = (tmp == KGFCM2_STATE) ? PSI_STATE : (tmp << 1) ; 20 // change to next unit state 21 CurState &= 0xf0 ; // clear bit0 ~ 3 22 CurState |= tmp ; // set to next unit 23} 24 } ■ 程式說明 SBR_KEY_JUMP 1 定義 ElapseTime 為經過的時間 7~13 當沒有按鍵輸入時 , 則累計時間並檢查是否已歷經 60 秒還沒有按鍵輸入 10~11 如果是已過 60 秒 (0x0bb8=3000, 3000*20ms=60s) 沒有按鍵輸入 , 則設定旗標 bHalt = 1 13 返回呼叫處 14 有按鍵輸入時 , 清除變數 ElapseTime, 分析按鍵 16~23 如果按鍵並非重複的 , 則執行下列之工作 將氣壓單位變換成下一種計數單位 , 依照 Psi => Bar => Kpa => Kgf/cm2 的順序循環 變換 . 將新的單位設定在目前工作狀態的變數中 24 若是重複的按鍵 , 則不做處理 ■ 原始程式 SBR_DISPLAY //----------------------------------------------------------------//--- SBR_Display --//--- display the presure value( 3 decimal digit and unit scaler) --// I/P: value = number to be displayed (decimal) // write digit pattaern to LCD RAM buffer, 140H ~ 148H //----------------------------------------------------------------1 const char divi[3] = { 100, 10, 1 } ; 2 3 void SBR_Display(unsigned int value) 4{ 5 char i, num, pattern, flag ; 6 // display digit 7 for( i = flag = 0 ; i < 3 ; i++ ) // one digit uses 3 LCD RAM bytes 8 { 9 num = value / divi[i] ; // separate the input value 10 value = value % divi[i] ; 11 if( (num > 9) || (flag==1) ) 12 { 13 flag = 1 ; 14 num = 10 ; 15 } 16 pattern = LEDDigitalPattern[num] ; // map the pattern of digit numbe� 17 L�dBuf[3*i] = patte�n & 0x7 ; // low nibble fo� d/e/f segment 18 L�dBuf[3*i+1] = (patte�n >> 4) & 0x07 ; // �ig� nibble fo� �/g/a segment 19 Rev. 1.30 �0 num = ((patte�n & 0x08)!=0) ? 0x0� : 0 ; // b segment �1 swit��( i ) 126 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 �� { �3 �ase 0 : // fi�st digit (left) �4 if( (Cu�State & BAR_STATE) != 0 ) num |= 0x01 ; �� if( bLED_Dot1 ) num |= 0x04 ; �6 b�eak ; �7 �ase 1 : // se�ond digit �8 if( (Cu�State & KPA_STATE) != 0 ) num |= 0x01 ; �9 if( bLED_Dot� ) num |= 0x04 ; 30 b�eak ; 31 �ase � : // t�i�d digit (�ig�t) 3� if( (Cu�State & KGFC��_STATE) != 0 ) num |= 0x01; 33 if( (Cu�State & PSI_STATE) != 0 ) num |= 0x04 ; 34 b�eak ; 3� } 36 L�dBuf[3*i+�] = num ; // w�ite (display) t�e 3�d byte 37 } 38 } ■ 程式說明 SBR_DISPLAY 1 定義除數 , 分別為 100, 10 與 1 3 此函式會將輸入的數值分成十進位數字顯示於 LCD 上 7~37 迴圈 , 顯示三個數字及小數點 9 輪流計算出百位數字 , 十位數字及個位數字 ( 從百位開始 ) 10 保留剩下的數值 11~15 如果數字超過 9 或是輸入數值大於 999, 則會轉成顯示`---' flag = 1 記錄數值大於 999; num = 顯示的數 16 從表格 LEDDigitalPattern 中取出對應的數字字型 17~18 將此字型寫入 LCD 的記憶體位址 , 顯示出此數字 . 每個數字的字型分別寫入 LCD 記 憶體的三個位元組 . 可參考前面 LCD 數字圖形 . 第一個位元組的位元 0,1,2 存放 segment f,e,d 第二個位元組的位元 0,1,2 存放 segment a,g,c. 第三個位元組的位元 1 存放 segment b, 其他兩個位元則存放第一個小數及第二個小數點及氣壓的單位名稱 20 從字型取出 segment b 的值 21~35 根據數值 , 決定 dot1, dot2 及氣壓單位是否要顯示 如果是百位 (i=0) 數字 , 則分析是否顯示第一個小數點及是否顯示 BAR 單位如果是十 位 (i=1) 數字 , 則分析是否顯示第二個小數點及是否顯示 KPA 單位如果是個位 (i=2) 數字 , 則分析是否顯示單位 KGF/CM 或 PSI 36 Rev. 1.30 寫入 LCD 記憶體的第三個位元組資料 127 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Rev. 1.30 128 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 第十章 組合語言與 C 語言的互用 (Mixed Language) 微控制器 (mi��o-�ont�olle�� �CU) 周邊裝置的控制及狀態讀取 � 有時使用組合語言編寫程式時 � 會產出更佳的執行效果 . 一種是在 C 語言程式中使用 inline assembly 的方式 � 另外則是使用函 式呼叫 (fun�tion �all) 的方式 . 前者在第四章的 4.9.3 節介紹 . 本章將介紹第二種方式 . 此方式 包含從 C 語言程式中呼叫組合語言程式以及從組合語言程式中呼叫 C 語言程式等兩種 . 下列的 項目及其規則必須遵守 � 否則無法達到預期的功能 . → 變數 � 函式與參數的命名規則 (naming of t�e va�iables� fun�tions and pa�amete�s) → 參數的傳遞 (pa�amete�s passing) → 返回值的設定 (�etu�n value) 10.1 變數 , 函式與參數的命名規則 ■ 名字的差異 →→ C 語言的英文字母大小寫被視為不同的字 (case-sensitive), 例如 count 與 Count 是兩個不 同的名字 → 組合語言的英文字母則是不分大小寫皆被視為相同的字 � 例如 Lengt� 與 lengt� 代表同一個 的名字 . ■ 編譯後的全域變數與函式名字 →→ C 編譯器在編譯全域變數 (global variable) 及函式時 , 會在原名之前附加一個底線字元 (underscore), 例如 全域變數 count 編譯後改為 _count 全域變數 Length 變譯後改為 _Length 函式 GetTotalSize( ) 編譯後改為 _GetTotalSize( ) →→ (Assembler) 在編譯組合語言程式後 , 會將名字轉換成大寫字母 (upper case) 例如 , 變數 �ount 變譯後改為 COUNT 變數 Lengt� 變譯後改為 LENGTH 函式 GetTotalSize( ) 編譯後改為 GETTOTALSIZE( ) ■ 編譯後的 C 函式的參數名字 (Function Parameters Name) 函式的參數名字在編譯後改為與函式名字相同 , 但要附加索引值 (index number). 例如 函式 ��a� GetSum(��a� x� ��a� y) 的參數 x 將改為 GetSum0� 而參數 y 將改為 GetSum1. 當 C 程式呼叫函式時 � 例如 z = GetSum(1� 4) ; C 編譯器會將第一個參數的值 1 存放到 GetSum0 變數 � 將第二個參數的值 4 存放到 GetSum1 中 . 10.2 參數的傳遞 由於 �CU 的堆疊 (sta�k) 不能被應用程式使用 � 所以 C 編譯器會將函式的參數存放於資料記憶 體 (RA�) 中做為參數傳遞的媒介 . 不論參數的資料型態為何 � 皆是以位元組的格式定義 � 例如 void SetPos(int xpos, int ypos) ; 其中參數 xpos 與 ypos 的資料型態為 int (2 個位元組 ), 經編 譯後 , xpos 與 ypos 分別改為 SetPos0 與 SetPos1, 其定義在 RAM 的格式為 SetPos0 DB � dup(?) ; xpos SetPos1 DB � sup(?) ; ypos 函式 SetPos 將從 SetPos0 與 SetPos1 變數中讀取參數值 . 10.3 返回值的設定 C 函式的返回值需視返回資料型態 (�etu�n type) 來決定使用那些資源做傳遞的媒介 . 下表為返 回值存放的暫存器或變數 Rev. 1.30 返回的資料型態 返回值大小 LBLW HBLW LBHW HBHW void 0 — — — — ��a� 1 ( 位元組 ) ACC — — — int/s�o�t 2 ( 位元組 ) ACC RH — — long/float 4 ( 位元組 ) ACC RH R� RU 129 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 ACC → A 暫存器 RH� R�� RU →變數 低位字低位元組→ Low Byte of Low Wo�d (LBLW) of �etu�n value 低位字高位元組→ Hig� Byte of Low Wo�d (HBLW) of �etu�n value 高位字低位元組→ Low Byte of Hig� Wo�d (LBHW) of �etu�n value 高位字高位元組→ Hig� Byte of Hig� Wo�d (HBHW) of �etu�n value 10.4 在 C 語言程式中呼叫組合語言函式 (Calling Assembly Function From C Program) 跨越不同語言程式的函式呼叫 � 在 C 程式中執行函式的呼叫 � 而在組合語言程式中定義被呼叫 的函式及變數 . 其撰寫規則如下 組合語言程式的函式定義規則 →→將底線字母 (underscore) 加入函式名字之前 , 並且宣告為公用變數 (public variable) (A-1) →→如果函式具有參數 , 則將所對應的變數設定於 RAM bank 0, 並宣告為公用變數 (A-2) →→如 果 函 式 具 有 返 回 值 , 且 其 資 料 型 態 是 int/short, 則 宣 告 變 數 RH 為 外 部 變 數 (externalvariable), 如果資料型態為 long/float, 則宣告 RH, RM 及 RU 為外部變數 (A-3) →→視函式返回值的資料型態 , 在函式返回前設定相對的變數 , A, RH, RM 或 RU (A-4) 範例 : EXTERN RH:byte ;; 宣告返回值的高位元組為外部變數 (A-3) PUBLIC _DISPLACE ;; 函式名字前加上底線字母 , 並宣告為公用 (A-1) PUBLIC DISPLACE0, DISPLACE1 ;; 宣告參數為公用變數 (A-2) RAMBANK0 DISPDATA ;; 定義參數存放的記憶體為 RAM bank 0 (A-2) DISPDATA .section ‘data’ ;; (A-2) DISPLACE0 DB ? ;; 定義第一個參數 row (char , 1 位元組 ) (A-2) DISPLACE1 DB 2 dup(?) ;; 定義第二個參數 col (int, 2 位元組 ) (A-2) RESULT DB ? ;; 執行函式的結果 , 暫時變數的低位元組 ;; 定義函式 _DISPLACE CODE .section ‘code’ _DISPLACE: CLR [0Ah].0 ;; 清除 CF 旗標 RLA DISPLACE0 ;; 讀取第一個參數 row, 向左旋轉一位元 , 存入 A ADD A, DISPLACE1 ;; 讀取第二個參數 col 的低位元組 , 並加到 A MOV RESULT, A ;; 將結果的低位元存入暫時變數 MOV A, 0 ADC A, DISPLACE1[1] ;; 將第二個參數的高位元組加入 MOV RH, A ;; 將返回值的高位元組存入 RH (A-4) MOV A, RESULT ;; 返回值的低位元組存入 A (A-4) RET ■ C 程式的呼叫規則 → 以大寫字母定義並宣告要呼叫的函式名字 (C-1) →→呼叫此函式 (C-2) 範例 : exte�n int DISPLACE(��a� �ow� int �ol) ; // 以大寫字母定義函式名字 (C-1) void main( ) { int disp ; // 返回值 disp = DISPLACE(10� �0) ; // 呼叫函式 (C-�) } 上例 � 在 C 程式中呼叫 DISPLAY(10��0) 後 � 返回值為 40 Rev. 1.30 130 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 10.5 在組合語言程式中呼叫 C 語言函式 (Calling C Funftion from Assembly Progam) ■ C 程式的呼叫規則 → 以大寫字母定義並宣告被呼叫的函式名字 (C-1) 範例 : int DISPLAY(char row, int col) ; // 宣告函式的類型 (C-1) int DISPLAY(char row, int col) // 定義函式 (C-1) { int �etval ; �etval = (int)(�ow << 1) + �ol ; �etu�n �etval ; } ■ 組合語言程式的函式定義規則 →→將以底線字母 (underscore) 為首的函式名字宣告為外部名字 (external function) (A-1) →→如果函式具有參數 , 則將所對應的變數宣告為外部變數 (external variable) (A-2) →→如 果 函 式 具 有 返 回 值 , 且 其 資 料 型 態 是 int/short, 則 宣 告 變 數 RH 為 外 部 變 數 (externalvariable), 如果資料型態為 long/float, 則宣告 RH, RM 及 RU 為外部變數 (A-3) →→呼叫 C 函式後 , 從 A, RH, RM 或 RU 讀出返回值 (A-4) 範例 : EXTERN RH:byte ;; 宣告返回值的高位元組為外部變數 (A-3) EXTERN _DISPLACE : near ;; 函式名字前加上底線字母 , 並宣告為外部 (A-1) EXTERN DISPLACE0 : byte ;; 宣告參數為外部變數 (A-2) EXTERN DISPLACE1 : byte ;; 宣告參數為外部變數 (A-2) ;; 呼叫函式 _DISPLACE CODE .section ‘code’ Start : MOV A, 10h MOV DISPLACE0, A ;; 存值到第一個參數 row MOV A, 20h MOV DISPLACE1, A ;; 存值到第二個參數 col 的低位元組 CLR DISPLACE1[1] ;; 第二個參數的高位元組設為 0 CALL _DISPLACE ;; 呼叫 C 函式 _DISPLACE ;; C 函式返回值 40 會存放於 A, RH 內容為 0 (A-4) …… RET__ Rev. 1.30 131 March 15, 2011 Holtek 微控制器應用範例 – 使用 Enhanced Holtek C 語言 Holtek Semiconductor Inc. (Headquarters) No.3, Creation Rd. II, Science Park, Hsinchu, Taiwan Tel: 886-3-563-1999 Fax: 886-3-563-1189 http://www.holtek.com.tw Holtek Semiconductor Inc. (Taipei Sales Office) 4F-2, No. 3-2, YuanQu St., Nankang Software Park, Taipei 115, Taiwan Tel: 886-2-2655-7070 Fax: 886-2-2655-7373 Fax: 886-2-2655-7383 (International sales hotline) Holtek Semiconductor Inc. (Shenzhen Sales Office) 5F, Unit A, Productivity Building, No.5 Gaoxin M 2nd Road, Nanshan District, Shenzhen, China 518057 Tel: 86-755-8616-9908, 86-755-8616-9308 Fax: 86-755-8616-9722 Holtek Semiconductor (USA), Inc. (North America Sales Office) 46729 Fremont Blvd., Fremont, CA 94538, USA Tel: 1-510-252-9880 Fax: 1-510-252-9885 http://www.holtek.com Copyright© 2011 by HOLTEK SEMICONDUCTOR INC. The information appearing in this Data Sheet is believed to be accurate at the time of publication. However, Holtek assumes no responsibility arising from the use of the specifications described. The applications mentioned herein are used solely for the purpose of illustration and Holtek makes no warranty or representation that such applications will be suitable without further modification, nor recommends the use of its products for application that may present a risk to human life due to malfunction or otherwise. Holtek's products are not authorized for use as critical components in life support devices or systems. Holtek reserves the right to alter its products without prior notification. For the most up-to-date information, please visit our web site at http://www.holtek.com.tw. Rev. 1.30 132 March 15, 2011