註: 本文為中文翻譯,原文請見底下網址:
http://www.mikroe.com/eng/chapters/view/12/appendix-b-examples/
TOC Introduction Ch. 1 Ch. 2 Ch. 3 Ch 4. Ch. 5 Ch. 6 Ch. 7 Ch. 8 Ch. 9 App. A App. B App. C
Appendix B: Examples
本章的目的是提供有關微控制器的基本資訊,為了能實際成功地使用微控制器所需知道的資訊。因此,本章並不包含超級有趣的程式或驚人的解決方案的設備電路圖。相反的,底下只是一些例證,說明撰寫程式既不是特權也不是天才的專利,而只是單純利用指令拼圖的能力而已。後續的例子確保設備的設計與開發主要由 "測試-矯正-重複 (test-correct-repeat)" 的方法組成。當然,你涉足的愈深,東西就愈複雜,這是因為這些是由孩子和一流的設計師想出來的題目。
基本連接方式
如下圖所見,為了讓微控制器正確地運作,必須提供:
● 電源供應;
● 重置訊號(Reset Signal); 及
● 時脈訊號(Clock Signal)。
顯然,它只是簡單的電路,不過不必永遠如此。如果是用來控制昂貴的機器或極其重要的功能的目標設備,一切會變得愈來愈複雜!然而,就目前而言,這個解決方案就足夠了…
電源供應
雖然 PIC16F887 可以在不同電壓下運作,但何必測試 "墨菲法則 (Murph’s low)" 呢? 上圖顯示有一個 5V 直流電壓。這個電路使用一顆便宜的三端穩壓 IC (three-terminal positive regulator) LM7805,提供高品質的穩壓功能,以及可以讓微控制器和周邊正常運作的足夠的電流 (在目前的情況下,足夠代表 1Amp)。
重置訊號 (RESET SIGNAL)
為了讓微控制器可以正常運作,必須提供邏輯 1(VCC) 的訊號給重置腳位 (Reset PIN) VCC 必須供應電壓到 reset pin,透過 10K-Vcc 這個線路提供。連接 MCLR 重置腳位到 GND 的按鈕是不必要的。然而,大部份還是會提供按鈕,因為它讓微控制器得以安全的恢復正常運作,假如發生了什麼錯誤的話,按下這個按鈕,MCLR 腳位會得到 0V,微控制器便會重置,而且程式會從頭開始執行。其中的 10K 電阻讓 0V 得以經由按鈕在 MCLR 腳位上產生作用 (MCLR 腳位變成 0V),而不會造成 5VDC 短路。
時脈訊號 (CLOCK SIGNAL)
雖然內建了振盪器,但是若缺少穩定運作與決定頻率 (微控制器的運作速度) 的外部元件,微控制器還是不能運作。根據所用的元件和頻率,振盪器可以在四種模式下運作:
● LP – 低功耗晶體 (Low Power Crystal);
● XT – 晶體 / 諧振器 (Crystal / Resonator);
● HS – 高速晶體 / 諧振器 (High speed Crystal / Resonator); 及
● RC – 電阻 / 電容 (Resistor / Capacitor)。
這些模式為什麼這麼重要?因為要製作一個可以在很廣泛範圍的頻率下都可以運作的穩定振盪器幾乎是不可能的,由於這樣的事實,微控制器必須知道連接的是哪一種晶體以調整其內部電子的運作。這就是為什麼為所有用來燒錄晶片韌體的程式都有一個振盪器模式的選項。見左圖。
石英諧振器 (Quartz resonator)
當使用石英晶體來穩定頻率時,內建的振盪器會以非常精確的頻率運作,不受溫度和電壓變化的影響。正常來說,頻率標示於石英諧振器的包裝上。
除了石英晶體,C1 和 C2 電容也必須按照下圖連接。電容的容量不是很重要,因此,下表提供的電容值應當視為建議值而非絕對的規則。
陶瓷諧振器 (Ceramic resonator)
陶瓷諧振器 (Ceramic resonator) 比較便宜,不過功能和運作方式很像石英晶體。這就是為什麼電路圖中它們與微控制器的連接方式是一致的。然而,由於不同的電子特性,在這個情況下電容值有點不一樣,參考下表。
當沒有必要使用非常精確的頻率的時候,就會使用這種振盪器。
RC 振盪器 (RC oscillator)
如果運作頻率不是很重要,就不太需要穩定頻率用的額外昂貴元件。相反的,如下圖所示,一個簡單的 RC 網路(RC network) 就足夠了。由於這邊使用的為本地端的振盪器輸入 (local oscillator input),OSC2 腳位的輸出時脈訊號將是 RC 振盪器 4 倍的除頻結果。再者,這個頻率會是微控制器的運作頻率,即指令執行的速度。
外部振盪器 (External oscillator)
如果有需要同步多台微控制器運作,或者是因為某些理由不能使用前述的電路,時脈訊號可能要由外部振盪器產生 (external oscillator)。參考下圖。
額外的元件
微控制器是現代科技的產品,無論如何,假如沒有連接到額外的元件,也是英雄無用武之地。簡單地說,如果不用來執行某些運作 (開啟/關閉某個東西、移位、顯示資料等),在微控制器腳位上出現的電壓就沒有意義了。
本節故意只涵蓋實際上最常用的額外元件,例如電阻 (resistor)、電晶體 (transistor), LED 二極體, LED 顯示器, LCD 顯示器以及 RS232 通訊電路。
開關和按鈕 (SWITCHES AND PUSH-BUTTONS)
沒有什麼可以比開關 (switches) 和按鈕 (push buttons) 更簡單了。這絕對是偵測微控制器輸入腳位的電壓訊號最簡單的方法,而且不需要對這些元件作額外的解釋。然而,事實不是那麼簡單…那麼,這一切是關於什麼呢?
這與接觸彈跳 (contact bounce) 有關 – 一個機械開關常見的問題。當接觸點碰撞在一起的時候,它們的動力和彈性會造成彈跳,其結果是快速的電流脈衝,而不是很乾淨的直接從滿格到零的電流轉換現象。一般來說,會發生接觸彈跳是因為接觸點之間有抖動、表面粗糙不平或者是有灰塵。在使用這些接觸開關時,在日常生活中接觸彈跳的作用是不明顯的,因為它們發生得太快了所以不會影響大部份的設備,不過對於反應快速的類比和邏輯電路就會產生問題,會導致將這些 on-off 脈衝被誤解為資料串流。總之,整個過程持續的時間不長 (幾微秒或毫秒),不過卻已經長到足以被微控制器登記起來。當單純把按鈕當成脈衝計數器使用時,幾乎百分之百會發生錯誤!
透過連接一個簡單的 RC 電路以抑制電壓的快速變化,可以很容易解決這個問題。由於彈跳的期間沒有定義,這些元件的值決定得不是很準確。在大部份情況下,建議使用如下圖所顯示的值。
如果需要完整的穩定性,那就得採取徹底的措施!下圖顯示的電路輸出 (RS 正反器, RS flip-flop) 將維持其狀態直到輸入改變。這個解法比較昂貴 (單刀雙擲開關, SPDT switch),不過肯定解決了問題!
除了這些硬體的解法外,也有個簡單的軟體解法。當程式在檢查輸入腳位的狀態並且偵測到變化時,應當在一定時間的延遲後再進行一次檢查工作。如果程式確認狀態有變化,代表開關/按鈕已經改變了位置。這個解法的優點是顯而易見的: 它是免費的、消除了干擾的影響而且適用低品質的接觸開關。
繼電器 (RELAY)
繼電器 (relay) 是一種機械式開關的電路控制元件,它連接到微控制器的輸出腳位,用來控制馬達 (motors)、變壓器 (transformers)、電暖爐 (heaters)、燈泡 (bulbs) 等大電流設備的開關。這些設備幾乎都放置在遠離感測元件 (boards sensitive components)的地方。繼電器有很多種,不過它們的運作方式都是一樣的。當電流流經線圈的時候,線圈產生的磁力就會使繼電器的接點切換開關。類似光耗耦合器 (optocouplers),繼電器在輸入與輸出電路之間沒有通電 (電氣接點)。繼電器通常需要較高的電壓和電流來驅動,不過也有小型的繼電器,可以直接使用微控制器腳位輸出的電流來驅動。
當電流流經線圈時,線圈會產生逆向的高電壓,因此線圈需要並聯一顆反向二極體 (inverted polarized diode)。這顆二極體的目的是切斷反電動勢。
LED DIODES
你可能已經知道 LED 二極體的所有知識,不過我們得為年輕的一代著想…要如何摧毀一顆 LED?!嗯…很簡單。
快速燒壞法 (Quick burning)
和其它二極體 (diode) 一樣,LEDs 有陽極 (anode) 和陰極 (cathode) 兩端。適當地把二極體連接到電壓源,二極體將很愉快地發光,把它倒過來再接到同一個電壓源 (即使只是一瞬間),它將永遠不再發光!
慢速燒壞法 (Slow burning)
有個象徵性的作法,即給 LED 最大的電流。如果發生這樣的事,二極體將發出較強的光,不過不會很久。
要記得的一些事
類似前一個例子,你所須做的只是摒棄限流電阻,如下圖。取決於供應電壓,效果可能會很壯觀!
LED 顯示器
基本上,LED 顯示器不過是同一個塑膠模具內由多顆 LED 組合而成的元件。世上有很多種 LED 顯示器,有的內建數以打計的二極體,可以顯示不同的符號。最常用的是所謂的七段顯示器 (7-segment display)。它是由 8 個 LEDs 組成的- 7 段 (筆劃段) 被安排成長方形用來顯示符號,另外額外的一段用來顯示小數點。為了簡化線路的連接,所有二極體的陽極或陰極會被連到共同的一支腳位,所以可分成共陽極和共陰極兩種顯示器。每一段都有標記,使用英文字母 a 到 g,加上 dp,如下圖。在連接的時候,每個二極體都要分開處理,這意味著每個二極體都必須有其專屬的限流電阻。
底下有幾件重要的事情,在購買 LED 顯示器時應當特別注意:
● 陽極腳位或是陰極腳位,根據共同腳位的連接方式,顯示器分成共陽極和共陰極兩種類型。上圖顯示共陽極的顯示器。看其硬體特徵,這些顯示器並沒有什麼差異,建議安裝前仔細檢查所用的為何種類型的顯示器;
● 微控制器的每支腳位都有能夠接收與提供的最大電流限制。由於這個原因,如果有多個顯示器連接到微控制器,建議使用 2mA 工作電壓所謂低電流的 LED。
● 顯示器的節段 (segment) 通常以 a 到 g 的字母作為標記,但是它們應該連接到微控制器的哪些腳位並沒有硬性規定 (no fast rule)。由於這個原因,在開始著手撰寫程式或設計設備前,事先檢查腳位的連接是非常重要的。
連接到微控制器的顯示器通常佔用大量寶貴的 I/O 腳位,這可能會是個大問題,尤其是需要顯示多位數數字的時候。最明顯不過的例子是,當需要顯示兩個 6 位數的數字時 (簡單計算,這個例子就需要 96 支輸出腳位)!這個問題有個解法,叫做多工 (MULTIPLEXING)。
跟電影攝影機的運作原理一樣,基於視覺暫留的原理,同一個時間只有一個數字是作用中的,不過因為它們改變的速度很快,以致於人們以為所有數字都同時在作用中。
上圖的解釋為:首先代表個位數的位元組輸出到微控制器的 port,同時間 T1 電晶體會被打開。過了一段時間,T1電晶體會被關閉,而代表十位數的位元組輸出到微控制器的 port,同時間 T2電 晶體會被打開。對於所有的位數和對應的電晶體,這個過程會這樣循環重複。
在顯示任何位數的數字時,便可充分看出這個令人掃興的事實 --- 微控制器不過是設計用來理解 0 和 1 的語言的小型電腦。也就是說,微控制器並不認識個位數、十位數和百位數,也不曉得我們所用的十位數長什麼樣子。因此,每個要顯示的數字都必須經過下列的程序:
首先,必須在一個特定的副程式裏把多位數的數字分成個位數、十位數等。然後,這些數字必須分別儲存在特殊的位元組裏。透過執行遮罩運算 (masking),數字變成可以辨識的格式。換句話說,使用一個簡單的副程式,每個位數的二進制格式會被一個不同的位元組合取代。例如,數字 8 (0000 1000) 會被二進制數字 01111111取代,以啟用所有的 LED 來顯示數字 8。在這個情況下,唯一仍然保持非作用中的二極體是保留給小數點用。
如果微控制器的某個 port 與顯示器的連接方式是 bit 0 啟動節段 "a",bit 1 啟動節段 "b",bit 2 啟動節段 "c" 等,那麼下表顯示了每個數字的遮罩 (mask)。
除了 0 到 9 的數字,透過適當的遮罩也可以顯示一些英文字母 – 如 A, C, E, J, F, U, H, L, b, c, d, o , r, t等。
如果使用的是共陽極的顯示器,前面表格中所有的 1 都應該換成 0,而 0 要換成 1。此外,也應當使用 NPN 電晶體當作驅動器 (driver)。
光耦器 (OPTOCOUPLER)
光耦器是一個設備,通常用來隔離微控制器周圍潛在的危險電流或電壓的電氣訊號。光耦器通常在其輸入端有一個、兩個或四個燈源 (LED二極體),而在其輸出端,二極體的對面,則有相同數目的感光元件 (光電晶體 (phototransistors), 光閘流體 (photo-thyristors) 或光交流矽控器 (photo-triacs))。重點是光耦器使用一個短的光傳輸路徑在電路之間傳輸訊號,以隔離電氣訊號。只有在二極體和感測元件是個別供電的時候,這個隔離才有道理。以這種方式,微控制器和昂貴的附加電子得以受到完全的保護,遠離高電壓和干擾,這些高電壓和干擾是實際上損毀、破壞或造成電子設備運作不穩定最常見的原因。最常用的光耦器是輸出端為光電晶體者。此外,??? 光耦器內部有 6 支腳位 (也有沒有腳位的光耦合器),這 6 支腳位也可能沒有接通 ???。
上圖中虛線表示的 R/C 網路代表選擇性的連線,該R/C網路排除很短的脈衝以減輕干擾的影響。
LCD 顯示器
這個元件是為了與微控制器一起使用而專門製造的,這意味著它不能透標準 IC 電路來啟動。它用來在小型液晶顯示器 (Liquid crystal display) 上顯示不同的訊息。這邊討論的模型是因為它實務上低價而且能力強。這個模型基於 HD44780 微控制器 (日立) 而且可以顯示兩行訊息,每行 16 個字元。它可以顯示所有的英文字母、希臘字母、標點符號和數學符號等。此外,它也可以顯示使用者建立的符號。其它有用的特性包括訊息移動 (左移和右移)、顯示游標和 LED 背光等。
沿著電路板的這一側有用來連接微控制器的腳位。總共有 14 支以數字標記的腳位 (如果內建背光的話是 16 支腳位)。這些腳位的功能描述於下表:
LCD 螢幕
LCD 螢幕由兩行組成,每行 16 個字元。每個字元由 5x8 或 5x10 的點矩陣組成。本書涵蓋 5x8 的螢幕,5x8 是最常用的螢幕。
顯示對比取決於供應電壓和訊息是一行或兩行顯示。由於這個原因,變動電壓會供應到標記為 Vee 的腳位上。通常會使用微調可變電阻 (Trimmer potentiometer) 來供應變動電壓。有些 LCD 顯示器內建背光 (藍或綠色二極體)。當使用背光時,應該將限流電阻串連到背光的腳位 (類似 LED 二極體的做法)。
如果字元沒有顯示在螢幕上,或者當螢幕打開的時候字元是模糊朦朧的,首先應該做的事是檢查可變電阻以調整對比。對比是否有適當調整?如果運作模式改變了 (一行或兩行寫入),也是一樣要檢查對比。
LCD 記憶體
LCD 顯示器包含三個記憶體區塊:
● DDRAM – 顯示資料的 RAM (Display Data RAM);
● CGRAM – 字元產生器 RAM (Character Generator RAM); 及
● CGROM – 字元產生器 ROM (Character Generator ROM)。
DDRAM 記憶體
DDRAM 記憶體用來儲存要顯示的字元。這個記憶體的大小可以儲存 80 個字元便足夠了。有些記憶體位置會直接連接到顯示器上的字元。
它的運作非常簡單:只要設定顯示器讓它自動遞增位址 (右移) 並且設定應當顯示的訊息的起始位址 (例如 00h)就足夠了。
之後,所有經過 D0-D7 傳送的字元將以我們習慣的方式由左至右顯示訊息。在這個例子中,會從第一行第一欄開始顯示,因為起始位址是 00h。如果傳送超過 16 個字元,那這些字元會全部被記憶起來,不過只有前 16 個字元才看得見。為了顯示其餘的字元,必須使用 shift 指令。差不多,這一切看起來的效果,LCD 顯示器就像是個在包含不同字元的記憶體位置上由左而右移動的視窗。實際上,這就是顯示器上訊息的移動效果 (message shifting) 的製作方法。
如果有打開游標,它會出現在目前定址到的位置上 (addressed location)。換句話說,當一個字元出現在游標的位置上時,游標將自動移到下一個定址到的位置上。
這是一種可以讀寫資料的 RAM 記憶體,不過在電腦關閉的時候它的內容將逝去而無法挽回。
CGROM 記憶體
CGROM 記憶體包含可以顯示在螢幕上的所有預設字型 (default character map)。每個字元均被指定到一個記憶體位置:
CGROM 記憶體的位址正好與 ASCII 字元匹配。如果正在執行的程式遇到 "傳送字元 P 到 port" 的指令,那麼二進制值 0101 0000 就會出現在 port 上,這個值與 P 的 ASCII 代碼相等。它會被寫到 LCD,結果是顯示從 GCROM 的 0101 0000 位置上取出的符號。換句話說,就會顯示 "P" 字元。這個方法套用在所有字母上 (大寫和小寫字母),不過不套用在數字上!
如前圖所見,所有的數字都往前推移了48 (數字 0 的位址是 48,數字 1 的位址是 49,數字 2 的位址是 50等)。因此,為了正確顯示數字,在傳送數字到 LCD 前必須要加上十進位數字 48 到每個數字上。
什麼是ASCII?從它們的開始以至於今天,電腦只可以辨識數字而非字母。這意味著電腦與周邊交換的所有資料為二進位的格式,雖然同樣的資料人類是以字母來辨識 (鍵盤是一個很好的例子)!它是如此的簡單 – 每個字母匹配一個由 0 與 1 組成的唯一組合。ASCII 是基於英文字母的字元編碼。ASCII 碼明確指定了標準字元符號和其數值間的對應關係。
LCD 基本命令
根據 RS 腳位的邏輯狀態,所有經過 D0-D7 輸出腳位傳送到 LCD 的資料將被解釋為指令或者是資料:
RS = 1 – D0 - D7 位元的資料是要顯示的字元的位址。LCD 處理器會從字元對應表 (character map) 定址出一個字元並顯示在螢幕上。DDRAM 的位址指明該字元將顯示於哪個位置。DDRAM 的位址是在傳送字元前先定義好的,或者是先前已經傳送的字元的位址自動遞增的。
RS = 0 – D0-D7 位元的資料為指令,用來決定顯示模式。
LCD 認得的指令條列於下表:
什麼是忙錄旗號 (Busy flag)?
相較於微控制器,LCD 是非常慢的元件。由於這個原因,在指令執行之後,必須提供一個訊號以表示顯示器已準備好接受下一個資料。這個訊號稱為忙碌旗號 (busy flag),可以從 D7 線路讀出。當這條線路的電壓為 0V(BF=0),代表顯示器已準備好接收新的資料。
LCD 的連接
根據連接 LCD 與微控制器線路的多寡,LCD 分成 8-bit 和 4-bit 兩種模式。適當的模式是在運作開始時選擇的,這個程序稱為 "初始化 (initialization)"。如前述,8-bit LCD 模式使用 D0-D7 輸出腳位傳送資料。
4-bit LCD 模式的主要目的是節省微控制器寶貴的 I/O 腳位。只有 4 個高位元 (D4-D7) 用來通訊,而其它腳位可能沒有連接。每個送到 LCD 的資料會被分成兩個步驟 – 先送 4 個高位元 (正常來說經由 D4-D7 線路),然後才送 4 個低位元。初始化 (initialization) 讓 LCD得 以連接並正確的解釋收到的位元資料。
資料很少從 LCD 這端讀取 (主要是從微控制器傳送到 LCD),所以往往可以把 R/W 腳位接地以節省 I/O 腳位。這樣做是有代價的。訊息將正常顯示,不過因為不能讀取顯示器的資料,所以便不能讀取忙碌旗號。幸運的是,有個簡單的解決方法。在傳送一個字元或指令後,給 LCD 足夠的時間做它的事是很重要的。最慢的指令執行時間大約是 1.64mS,由於這個事實,花大約 2mS 的時間等待 LCD 就緒應該是足夠的。
LCD 起始化
上電的時候 LCD 會自動清除。大約持續 15mS 後,顯示器就準備就緒。運作模式會使用預設的設定,這意味著:
1. 顯示器會清除。
2. 模式
● DL = 1 經由 8-bit 介面通訊
● N = 0 訊息顯示在一行
● F = 0 字型為 5x8 的點矩陣
3. 顯示器/游標 開/關
● D = 0 顯示器關閉
● U = 0 游標關閉
● B = 0 游標閃爍關閉
4. 字元進入點 (Character entry)
● ID = 1 顯示的位址自動遞增 1
● S = 0 顯示移位關閉 (Display shift off)
自動重置 (Automatic reset) 大多數是沒有問題的。大部份,但不是永遠如此!如果因為某些原因供應電壓沒辦法在 10mS 之內達到滿值時 (full value),顯示器將開始運作,但是它的行為完全無法預料。如果電源供應器無法達到這個條件,或必須提供完整的安全運作,就會啟動初始化的程序。除了其它別的因素外,初始化會引發重置 (reset) 以使顯示器正常運作。
關於 8-bit 初始化的程序,參考下圖:
沒弄錯!在這個演算法中,同樣的值傳送了三次。
在 4-bit 初始化的情況下,程序如下:
EXAMPLE 1
撰寫標頭 (header) 和設置 I/O 腳位
這支程式的目的只是打開 port B 上的一些 LED 二極體。沒有什麼特別。無論如何,用這個例子來學習以知道真實的程式長得像怎樣。下圖顯示連接的電路圖,程式在下一頁。
當打開的時候,port B 上的 LED 二極體會每隔一個發光。這足以表示微控制器有正確連接而且運作正常。
這個範例說明了一個正確書寫的標頭 (header) 和一些初始的組譯指示 (initial directives) 的寫法。它們代表本書所有程式中的一部份程式。為免重覆,這些東西將不會寫在底下的範例中,雖然沒有明說,不過它們會放在每支程式的開頭 (以 "Header" 標記起來)。
標頭 (header) 和初始的組譯指示簡短說明於下。
標頭:
標頭位於程式的開頭,並且以註解的形式給了一些基本資訊 (程式名稱、發佈日期等)。不要輕信以為幾個月後你還能記得程式是做什麼的而且為什麼它會儲存在你的電腦裏。
Initial 組譯指示:
這個組譯指示定義了執行程式的處理器。
這行讓編譯器得以存取 p16f887.inc 這份文件 (如果你有安裝 MPLAB,這份文件預設位於 C:\Program files\Microchip\MPASM Suite 資料夾下)。在這份文件裏,每個 SFRs 暫存器和每個位元都有其名稱和位址的定義。舉個例子,如果程式這樣做:
這代表 INTCON 暫存器的 GIE 位元應該被設定。像這樣的指令對編譯器而言是沒有意義的。編譯器必須存取 ".inc" 文件以得知在位址 000B 的 SFR 的第七個位元應該被設定。
這只是一個表面性 (cosmetic) 的組譯指示,用來關閉在每次編譯後會出現的 "Register in operand not in…" 這類令人煩躁的訊息。它不是必要的,不過有用。
這個組譯指示在編譯之後會將 Config Word 放進程式中。這不是必要的,因為相同的動作在載入程式到晶片的時候軟體就會執行。不過,你曉得最終用戶 (end user) 會用哪套軟體嗎?哪些是預設的選項?你是最終的用戶嗎?你知道下個年度你將使用哪個軟體燒錄 MCU 程式嗎?讓你自己的生活輕鬆點,把這個組譯指示當成必要的並且把它放到程式裏吧。
EXAMPLE 2
使用迴圈與LFINTOSC內部振盪器
這是前一個例子的延續,不過涉及稍微複雜一點的問題…基本想法是讓 port B 上的 LED 二極體閃爍。第一眼所見最簡單的事情!週期性的改變 port B 的邏輯狀態就足夠了。在這個案例下,選用 01010101 和10101010 這兩個數字以下列方式變化:
1. 在 port B 上設定二進制的組合 01010101;
2. 停留在 loop1 中;
3. 在 portB上 以二進制的組合 10101010 取而代之;
4. 停留在 loop2 中; 及
5. 回到步驟 1 並重覆整個程序。
你知道這個程序應該以多快的速度進行嗎?除了 loop1 和 loop2 提供的延遲外,只有在整個過程減慢將近 250倍或更多的時候,才有可能觀察到 port B 上的變化。由於這個原因,因此微控制器以 31kHz 的 LFINTOSC 內部振盪器取而代之,而非使用石英晶體 (8MHz)。
你已經注意到時脈來源在進行中 (on the fly) 改變了。如果你想確認這件事,在打開微控制器的電源前先移除石英晶體。會發生什麼事?微控制器將不會運作,因為跟程式一起載入的 Config Word 在打開電源的時候需要用到石英晶體。如果你在運作期間移除石英晶體,對微控制器就一點影響也沒有!
Sorce Code
EXAMPLE 3
使用巢狀迴圈
硬體連接方式還是跟前面的例子一樣。為了讓事情有趣一點,port B 的位元組合做了一些改變,當然這並不是全部。如前面兩個例子所見,微控制器速度非常快,所以必須讓它慢下來。如範例 2,使用內建振盪器應當成是最後的一招。解決這類問題方法通常是在程式裏使用巢狀迴圈。在這個例子,"counter1" 變數在 loop1 中遞減 1 遞減了 255 次,在離開迴圈之前,程式會從 255 到 0 倒數 255 次。這意味著,在 LED 二極體閃爍之間,來自石英晶體的脈衝總共有 255x255 個。準確地說,由於執行跳躍指令和遞減指令也需要時間,所以脈衝總數大約是 196000 個。是的,沒錯,微控制器大部份時間只是等待,沒做什麼事。
Source Code
Example 4
使用 TMR0 計時器和中斷
如果你看過前面的例子,你就會注意到使用迴圈提供延遲的缺點。在所有這些情況下,微控制器是被監禁的 (captive),什麼事也做不了,它只是等待時間的消逝。這類時間的浪費是一種不能接受的奢侈行為,應該要用些方法。
你還記得計時器和中斷嗎?這個例子實際聯繫了這兩者。硬體電路仍然和前例相同。必須提供足夠的延遲以觀察 port 上的變化,為此這一次改用 TMR0 和指定的 prescaler。中斷發生於每次暫存器溢位的時候,而且中斷服務常式會讓 port B 上的數字遞增1。這整個程序都在幕後執行的,這使得微控制器可以做其它事情。
注意一些細節:
● 雖然對這個案例而言是不必要的,但最重要的暫存器 (W, STATUS和PCLATH) 必須在中斷服務常式開頭保存起來;
● 中斷會觸使恰當的旗號自動設定起來,而且會 GIE 位元也會自動清除,在中斷服務常式末端,別忘了把這些位元還原到發生中斷前的狀態;及
● 在中斷服常式的末端,必須還原重要暫存器的值。
譯註:RETFIE 除了離開中斷服務常式外,也會自動設定 GIE 位元,所以手動還原 GIE 位元的狀態這一步是可以省略的。
Source Code
Example 5
使用副程式和按鈕
在前面的例子中,微控制器執行程式,不受周圍環境的任何影響。實際上,以這種方式運作的設備很少 (例如,霓虹燈)。你猜,在其它元件之中,本例將使用到輸入腳位。下圖是電路圖,而程式碼在下一頁。一切還是非常的簡單。
在程式的開頭,緊接在定義變數之後,透過 TRISA 和 TRISB 兩個暫存器設置微控制器的腳位[1]。
[1] 對 PORTA 腳位而言其實是不必要的,因為 PORTA 在每次重開機後會被自動設置成輸入。
在主程式中,首先設定 port B 其中一個位元,然後不斷地把暫存器的內容往左移一個位置 (指令 rlf PORTB)。這給了我們發光的 LED 二極體在移動的印象。為了讓移動的現象看得見,整個行程必須夠慢。按下 "STOP" 按鈕會停止燈號移動,而且程式會停留在 loop3 迴圈裏。延遲是透過巢狀迴圈的方法做到的,這次擺在 "DELAY" 這個副程式中。
Source Code
EXAMPLE 6
TMR0 作計數器用,定義新的變數,以及使用繼電器
這一次,TMR0 當作計數器使用。程式的構想是把計數器連接到一個按鈕,每當按下按鈕時就讓計數器計算 1 個脈衝累加計數值。當計數值與 TEST 暫存器裏的數值相等時,就會在 PORTD, 3 腳位上輸出 5V 電壓,由於這個電壓會啟動機電繼電器,所以這個位元稱作 "Relay"。
本例中,TEST 暫存器的內容為 5。當然,它可以是任何數值,可以利用計算而得,也可以透過鍵盤輸入。微控制器可以啟動其它設備而非繼電器,也可以使用感測器而非按鈕。本例說明在工業界中微控制器最常見的用法,當某件事情根據需要執行了多少次後,某個東西就應該打開或關掉…
Source Code
EXAMPLE 7
使用巨集和去抖動程序 (debounce routine)
你可能已經注意到,在前個例子中微控制器並未永遠如預期運作,也就是說,當按下按鈕時,port B 上的數字並不會永遠加 1。當機械式的按鈕被按下時,會產生連續好幾次的接觸。你猜發生什麼事?微控制器登記而且加總了所有的接觸次數…
這個問題的解法很多,這支程式使用的方法是程式延遲 (program delay),又稱為去抖動 (debounce)。基本上,其實只是一個簡單的程序 (procedure),在偵測到輸入訊號變化的時候 (按鈕按下),讓程式延遲一段短暫的時間,然後等待另一個變化 (按鈕放開),在這之後,程式才視為按鈕確實被觸動了。
在這個案例中,利用一個叫做 "button" 的巨集按鈕狀態的檢查。此外,在 "button" 巨集中還包含了一個程式延遲,程式的延遲則是利用 "pausems" 巨集。
主程式相對較為簡單,使用兩個按鈕讓 cnt 變數得以遞增或遞減,之後 cnt 變數會被複製到 port B 上以影響 LED 二極體的變化 (邏輯 1 是打開 LED 二極體,而邏輯 0 則是關閉 LED 二極體)。
Source Code
Macro "pausems"
Macro "button"
EXAMPLE 8
使用 TMR1 計時器和中斷
本例使用 16 位元計時器TMR1。透過使用 TMR1 的 TMR1L 和 TMR1H 暫存器,當中斷發生時便遞增 port B上的數字。這在前面的例子已經看過,差別是這次程式延遲比較長,因為預除器的比例是 1:8。
Source Code
EXAMPLE 9
使用 TMR2 計時器,設置石英振盪器
本例說明 TMR2 計時器的使用方法。微控制器使用頻率 500kHz 的內部振盪器 HFINTOSC。整個程式的工作流程為:當定義在 PR 暫存器和預除器 (prescaler) 以及後除器 (postscaler) 的延遲時間過期後 (expired),會產生一個中斷,中斷服務常式會遞減 PR 暫存器的內容,同時遞增 port B 的內容。由於用來決定中斷發生與否的 PR 暫存器不斷的遞減,所以中斷發生的時間將越來越短。換句話說,計數將進行的更快,每當 PR 暫存器溢位 (overflow) 一次後,就開始一個加速的新計數循環。
Source Code
EXAMPLE 10
把 CCP1 模組當作 PWM 訊號產生器用
由於有很廣泛的可能性,CCP 模組很實用。本例說明 CCP1 模組 PWM模 式的使用方法。CCP1CON 暫存器決定 CCP1 模組以單一輸出 PWM 運作 (single-output PWM),同時也決定了 PWM 頻率為 4.88 kHz。為了讓事情更有趣,可以透過叫作 "DARK" 和 "BRIGHT" 的按鈕改變 P1A (PORTC, 2) 的脈衝輸出週期。按鈕狀態的檢查是在由 TMR1 觸發的中斷服務常式裏進行的。按鈕狀態的改變影響 LED 二極體以改變光線強度。注意 port B 並沒有使用外部電阻,因為已經啟用了內部電阻。產生 PWM 訊號的整個過程是在幕後 (behind the scenes) 進行的,這允許微控制器得以進行其它工作。
Source Code
EXAMPLE 11
使用 A/D 轉換器
本例使用 PIC16F887 的 A/D 轉換器。一切相當地簡單,一個變量的類比訊號輸入到 AN2 腳位,而轉換結果以二進制數字顯示在 port B上。為了儘可能簡化,程式只有顯示轉換結果的最低 8 個位元。GND 用作負參考電壓 Vref-,而正參考電壓則輸入至 AN3 腳位。正負參考電壓讓電壓的量測刻度得以 "放大與縮小 (stretch and shrink)"。
說的更清楚點,A/D 轉換器永遠產生 10 位元的二進制結果,這意味著它總共可以偵測 1024 個刻度的電壓 (210=1024)。兩個電壓水平之間不會永遠相同,Vref+ 和 Vref 的差距愈小,1024 階的兩個刻度間的差距就愈小,也就是說 A/D 轉換器能夠偵測很微小的電壓變化。
Source Code
EXAMPLE 12
使用 EEPROM 記憶體
這個例子示範內建記憶體 EEPROM 的讀寫方法。程式的工作流程如下:主程式迴圈不斷地讀取 EEPROM 位址 5 (十進制) 的內容並顯示在 port D上,在同一個迴圈中會測試連接到 port A 的三個按鈕的狀態。"INCREMENT" 和 "DECREMENT" 兩個按鈕的用途和範例 7 一樣 – 分別負責遞增和遞減 cnt 變數,之後將 cnt 變數的內容顯示在 port B 上,"MEMO" 按鈕讓 cnt 變數的內容得以寫到 EEPROM 記憶體。為了確認這點,只要按下 "MEMO" 按鈕並關閉設備,在下一次打開設備時,程式就會把這個變數的值顯示在 port D 上(在寫到 EEPROM 的那一刻,cnt 變數值顯示在 port B 上)。
Source Code
EXAMPLE 13
兩位數 LED 計數器,多工
在這個例子中,微控制器當作一個兩位數的計數器使用。具體地說,Dval 變數會遞減 (速度夠慢才得以看得見),而且其值會顯示在兩位數的 LED 顯示器上 (99-0)。程式的挑戰是轉換二進制數字為十進制數字,並且把它分成兩個位數 (十位數和個位數)。此外,由於 LED 七段顯示器是並聯的,為了讓觀者有同時看見兩個位數的印象,必須確保 LED 七段顯示器的切換速度夠快 (分時多工, time-division multiplexing)。記得,在電子產品中,多工允許一個 A/D 轉換器可以處理多個類比訊號。在這個案例中,分時多工是由 TMR0 執行的,而二進制轉十進制數是則是在 "digbyte" 巨集中執行的。可以按下 "COUNTER RESET" 以隨時重設計數器回到起始值 (99)。
Source Code
Macro "digbyte":
Digbte 巨集用來把二進制數字轉成十進制格式,此外,為了顯示在 LED 顯示器上,這類十進制的數字會儲存在特別的暫存器中。
EXAMPLE 14
產生聲音,使用巨集
產生聲音是微控制器常見的工作。基本上,說穿了它只是在一支輸出腳位上產生一串脈衝。產生脈衝的時候,邏輯 0 和邏輯 1 期間的比例決定了聲調,而改變聲調就可以產生旋律。
在這個例子中,按下 T1 和 T2 按鈕其中一個按鈕都可以產生聲音,帶有兩個參數的 "beep" 巨集裏有用來產生聲音的適當的指令。
Source Code
Macro "beep":
EXAMPLE 15
使用 LCD 顯示器
這個例子說明文字型 LCD 顯示器 (alphanumberic LCD display) 的使用方法。由於使用了巨集,所以程式本身非常的簡單 (努力地建立巨集通常在最後才有收獲)。
顯示器上會顯示兩行訊息,第二行訊息本來打算顯示目前的溫度,由於沒有安裝感測器,所以沒有真正完成量測的工作,顯示器上顯示的是 temp 變數的內容而非實際量測到的溫度。
在現實中,會顯示目前溫度或是其它量測到的數值。
Source Code
LCD.inc
EXAMPLE 16
RS232 序列通訊
這個例子說明微控制器 EUSART 模組的使用方法。與PC之間的連線使用的是 RS232 標準。這支程式的工作流程為:使用接在 port B 上的 LED 二極體來顯示每個經由序列通訊收到的位元組,而且之後自動將收到的字元組回傳到發射器上 (transmitter)。如果接收的時候發生錯誤,會打開 port A 上的 LED 二極體以表示之。實際測試設備運作最簡單的方法是使用 Windows 標準應用程式 Hyper Terminal。
Source Code
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。