AVR單片機教程——數碼管

先解答以前一個思考題:若是不把引腳配置爲輸出而寫高電平,鏈接LED會怎樣?git

實驗結果是,LED會亮,但相比於輸出高電平的狀況,亮度很低。這是爲何呢?數組

經過上一篇教程咱們知道,引腳輸入輸出模式是由寄存器DDRx中DDxn位控制的,能夠推斷出 pin_mode 函數會改變一個引腳對應的DDxn值,輸入爲0,輸出爲1,而其復位後的值爲0,即輸入,所以若是不把引腳配置爲輸出,它的模式就是輸入。相似地 pin_write 函數會改變PORTxn,其值爲函數的第二個參數。函數

因此不配置輸出而寫高電平的結果就是,這一引腳的DDxn爲0,PORTxn爲1,是帶上拉電阻的輸入模式。上拉電阻至關於VCC接電阻後再接在引腳上,外部電路是引腳接一個電阻再接一個LED到地,整體能夠等效爲LED被一個電阻限流後接在VCC和地之間,所以LED會亮。工具

這個電阻的阻值是上拉電阻和原本的限流電阻的阻值之和,上拉電阻是比較大的(根據datasheet P432 Figure30-164能夠估算出上拉電阻約40kΩ),相比於限流電阻就是外電阻的輸出高電平的狀況,LED上的電流小不少,所以亮度也相應低了。ui

這是一個模擬電路的問題,只用數字電路的分析方法是解決不了的。這個簡答的問題也反映了單片機相關知識的綜合性。spa

 

今天來說數碼管,就是開發板左邊那兩個日字。設計

早期數碼管也成爲輝光燈,依靠氣體放電發光,如今通常指7段數碼管(由於日字有7段),其本質是許多LED。咱們的開發板上是2位數碼管,也有1位、3位、4位的;除了7段數碼管還有米字數碼管,不過比較少見。注意7段數碼管其實有8段,每一個數字右下角有一個小數點。還有一種4位時鐘數碼管還有中間兩點,但有兩個小數點不能點亮。code

數碼管的8段從上面一段開始順時針依次爲A、B、C、D、E、F,中間爲G,小數點稱爲DP。blog

各類LED數碼管的原理都相同,內部電路鏈接略有不一樣。1位數碼管有10個引腳,而內部有8個LED,是怎樣鏈接的呢?在數字輸出那一篇中提到過共陽和共陰,數碼管也是這樣的:8個LED的正極(或負極)一塊兒鏈接到兩個引腳上,負極(或正極)分別鏈接到一個引腳,稱爲共陽(或共陰)數碼管。開發板上的2位數碼管中,每一位都是共陰鏈接的。教程

8個正極與單片機之間的鏈接有些複雜,如今沒法講解,不過能夠理解爲8個由單片機控制的、相互獨立的輸出引腳,每一個分別串聯了電阻後鏈接數碼管的正極。左右兩位的負極鏈接到數碼管右側的排針,使用時要把它們鏈接到單片機引腳上。

若是要讓數碼管的特定幾段亮,應該如何配置9個引腳的電平呢?答案是要亮的段的正極爲高電平,其餘爲低電平,負極也是低電平。另外,若是負極是高電平,則LED正負極之間電壓爲0或小於0,是不會亮的,這一事實在驅動多位數碼管時很重要。注意到負極低電平亮而高電平不亮,與此前使用過的LED不一樣,寫程序的時候不要想固然。

根據手冊,咱們能夠用 segment_dec 等函數設置數碼管顯示的數據,用 segment_display 來讓數碼管顯示。把顯示數據分紅設置數據與控制顯示,分別由兩個函數來完成,往高深講是將數據與視圖分離,而更貼近本篇教程主題的緣由是,咱們後面將會看到,多位數碼管沒法僅靠一個函數顯示多位數字(至少在順序控制流下)。

做爲數碼管的第一個程序,咱們先點亮右邊的數碼管,讓它循環顯示0~9。左右兩位的負極分別鏈接到單片機4和5號引腳上。

 1 #include <ee1/segment.h>
 2 #include <ee1/delay.h>
 3 
 4 int main()
 5 {
 6     segment_init(PIN_4, PIN_5);
 7     while (1)
 8         for (uint8_t i = 0; i != 10; ++i)
 9         {
10             segment_dec(i);
11             segment_display(SEGMENT_DIGIT_R);
12             delay(500);
13         }
14 }

根據上一篇教程的內容,你應該不難推斷出 segment_display 對數碼管負極作的工做吧。它把要顯示的一位的負極所鏈接的引腳的電平拉低,另外一位拉高,這是比較顯然的。segment_display 對數碼管正極作的工做是根據數據配置數碼管8個正極的電平,這裏的數據是由 segment_dec 設置的。數據中每個bit和數碼管中一段的亮暗相對應,最低位對應A段而最高位對應DP段(這只是我設計開發板時的鏈接方式)。那麼,顯示數字1須要點亮B和C段,其顯示數據就是0b00000110,即數字1的段碼。segment_dec 等函數根據輸入參數,給一個大小爲2的數組寫入了兩個值,分別對應兩位數碼管要顯示的數據。

這裏插一句,0b前綴表示二進制數,這是GNU擴展,不屬於C標準,C++也從C++14纔開始支持這種表示法。按理來講應該儘量少用編譯器擴展,但這個特性實在是太好用了,因此就拿來用了。

庫中已經聲明瞭一些段碼。segment_digit 爲0~九、A~F的段碼,segment_dot 表示小數點,它們的定義是:

 1 const uint8_t segment_digit[] =
 2 {
 3     0b00111111, // 0
 4     0b00000110, // 1
 5     0b01011011, // 2
 6     0b01001111, // 3
 7     0b01100110, // 4
 8     0b01101101, // 5
 9     0b01111101, // 6
10     0b00000111, // 7
11     0b01111111, // 8
12     0b01101111, // 9
13     0b01110111, // A
14     0b01111100, // B
15     0b00111001, // C
16     0b01011110, // D
17     0b01111001, // E
18     0b01110001  // F
19 };
20 const uint8_t segment_dot = 0b10000000;

這些段碼是根據字符形狀手動輸入的。你能夠用相同的方法創造更多字符,也能夠在網上找一些工具來完成。

若是(我是說若是)共陰數碼管的A~G、DP段分別鏈接單片機PA0~PA7,負極接低電平,DDRA全爲1,那麼用一個段碼給PORTA賦值,數碼管上就能顯示對應的數字或字母,或其餘圖案。這裏留個思考題,對於共陽數碼管,應該在什麼地方做些改動?

以上就是驅動一位數碼管的方法。

 

如今咱們來看如何驅動多位數碼管。開發板上是2位數碼管,實際上多位數碼管的驅動方法都是同樣的,在此以共陰2位數碼管爲例。

前面提到共陰多位數碼管的每一位都是共陰鏈接的,那麼不一樣位之間是如何鏈接的呢?有一些2位數碼管的兩位之間沒有任何聯繫,整個器件至少有18個引腳,這個數字已經有些大了。而有一種電路鏈接能夠作到在1位數碼管的基礎上,每增長一位,只需多一個引腳,見下圖:

增長的這個引腳就是新的一位的負極,而其每一段的正極與第一位是共用的。對於不一樣位上的同一段,不難發現它們是共陽鏈接的。位是共陰的,段是共陽的,處於習慣上的考慮,這樣的數碼管稱爲共陰數碼管。

然而。這樣節省引腳的接法是有代價的,就是你在一個時刻只能獨立控制8個LED,即一位的8段(根據對稱性,你也能夠控制2位的同一段,但這樣更受限制)。你沒法作到的是點亮第1位的A段和第2位的B段,同時其餘段都不亮。爲何呢?點亮第1位的A段,須要使共陽A段爲高電平而共陰第1位爲低電平,同理B段爲高電平,第2位爲低電平,結果就是第1位的B段和第2爲的A段也被同時點亮了。在這樣的方案下,兩位數碼管只能顯示同一個數字。

咱們須要一些別的辦法。根據前一部分的內容,分別顯示兩位中的任意一位都是能夠作到的。若是用戶想看到兩個數字,程序能夠在第1秒顯示左邊的數字,第2秒顯示右邊的數字,用戶至少是能夠理解的,儘管這種方法至關糟糕。爲何是1秒而不是10秒呢?1秒的狀況下用戶平均等待0.5秒能夠看到兩個數字的信息,10秒的話就是5秒等待,用戶不肯意。

但是如今用戶連0.5秒都不肯意等了,怎麼辦?必定要把等待時間壓縮到0嗎?用戶是人,人眼的刷新率不過高,只要每秒有24幅圖像,它(實際上是大腦)就認爲這些圖像是連續的。因此只要這一等待時間比人眼獲取圖像的間隔要小,用戶就不會再有怨言了。

如今你來作用戶。你能看到兩個數字,兩個數字呈現的時間間隔是一段很短的時間,短到你分辨不出它到底有沒有一段間隔。這意味着什麼?兩個數字同時顯示了出來。在此背後,數碼管的控制器每隔一小段時間切換一個數字來顯示,這種驅動方式成爲動態掃描

咱們來寫個程序驗證一下這種方案。在前一個程序的基礎上,咱們讓左邊一位顯示0。

 1 #include <ee1/segment.h>
 2 #include <ee1/delay.h>
 3 
 4 int main()
 5 {
 6     segment_init(PIN_4, PIN_5);
 7     while (1)
 8     for (uint8_t i = 0; i != 10; ++i)
 9     {
10         segment_dec(i);
11         for (uint8_t j = 0; j != 250; ++j)
12         {
13             segment_display(SEGMENT_DIGIT_L);
14             delay(1);
15             segment_display(SEGMENT_DIGIT_R);
16             delay(1);
17         }
18     }
19 }

在這個程序中,數碼管刷新間隔設置爲2毫秒(1毫秒*2位)。實際上這一間隔在10毫秒如下便可,人眼感覺不到差異。在這個程序中你甚至能夠不寫延時(相應地循環次數要增長不少倍),刷新間隔就很小,但其餘應用中單片機除了驅動數碼管還有別的工做,若是這些工做的運行時間是不固定的,就不能用它們替換掉延時,由於會致使數碼管亮度不均衡。應該加入一個比運行時間長得多的延時來保證間隔大體相同,然而這一方案在其餘工做運行時間達到10毫秒這一量級時就不能使用了。

在使用動態掃描來控制數碼管時,你會發現一種從史無前例的麻煩,尤爲是當與按鍵等輸入相結合時,如做業第2題。之前使用的LED等設備,只需調用一次函數,其狀態就能一直保持,直到再調用相應函數;而動態掃描是動態的,即你須要不斷去控制它,這就是麻煩的根源所在。單片機固然有別的方案來解決這些問題,咱們之後再講。

 

今天的做業:

1. 保持1毫秒的延時,將顯示切換的時間從0.5秒改成1秒(這有什麼難的,不是嗎?);

2. 實現如下功能:按下一個按鍵時,數碼管顯示的十六進制數字加1;保持按下1000毫秒後,每200毫秒數字加1;再加入另外一個按鍵,但它的效果是數字減1。

相關文章
相關標籤/搜索