四.10-2 OLED顯示器各式文、數字顯示實習
在前一個由Adafruit原廠所提供的範例中,可以說把OLED顯示器上可以使用的功能都演繹了一次,但是一次把太多東西湊在一起,對於初學者來說可能會沒辦法吸收,因此筆者打算把那些動作與功能分解成不同的屬性後,個別寫一個測試程式去示範一次。
不管是那一種顯示器,最基本的至少都能顯示文、數字,而目前市面上能看得到的OLED顯示器,其實都是繪圖型的顯示器,只是透過程式的處理,或是使用別人設計好的函式庫,就可以很容易地在上面顯示各種形式的文數字。在這個範例中,將會示範一些比較常用的文字顯示功能,而這些功能對一般的應來說應該很夠了。
◎功能與動作說明:
在本實習中,我們會示範下列幾種跟文、數字或ASCII字元碼有關的顯示範例讓大家參考:
1、基本英文字串顯示練習
2、反白英文字串顯示練習
3、ASCII字元碼顯示練習
4、數字顯示練習
5、不同數字基底顯示練習
6、英文字串全螢幕捲動顯示練習
7、英文字串部分捲動顯示練習
◎電路圖:
圖4、25 NodeMCU使用I2C介面與OLED顯示器麵包板接線及電路圖
本範例和前一個使用的電路有些不同,主要是其中單晶片的部分改成了NodeMCU這塊模組板,不過兩者都是使用ESP8266系列中的ESP-12E這個型號的郵票板,對我們的範例來說,兩種電路其實沒什麼差異,都可以使用,只是想讓大家有個不同的選擇,所以如果沿用上一個範例的電路也是OK的。在NodeMCU中,I2C這個匯流排介面的預設的接腳及電源為:
SCL 🡪 D1(GPIO5), SDA 🡪 D2(GPIO4), VCC 🡪 3.3V
雖然說一般這種0.96''使用SSD1306的OLED顯示器模組板,大多內建有5V🡪3.3V的穩壓器,可是還是建議使用3.3V的供應電壓比較好。
◎ 程式列表與說明:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup()
{
Serial.begin(115200);
Serial.println();
// initialize with the I2C addr 0x3C
if(display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) // Address 0x3D for 128x64
Serial.println("SSD1036 OLED allocation Successed!");
else {
Serial.println("SSD1306 allocation failed");
for(;;);
}
delay(2000);
// 清除顯示緩衝區:
display.clearDisplay();
// 顯示正常顏色文字:
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(" Program");
display.println(" Start!");
display.println(" Hello");
display.println(" world!");
display.display();
delay(2000);
// 顯示反白顏色文字:
display.clearDisplay();
display.setTextColor(BLACK, WHITE); // 'inverted' text
display.setCursor(0,20);
display.println(" Hello ");
display.println(" world!");
display.display();
delay(2000);
// 顯示 ASCII 碼字元:
display.clearDisplay();
display.cp437(true);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.setTextSize(4);
for(int i=1;i<7;i++)
display.write(i);
for(int i=11;i<15;i++)
display.write(i);
display.display();
delay(4000);
// 顯示數字:
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0,28);
display.println(1234567890);
display.display();
delay(2000);
// 顯示不同基底的數字:16,10及2進制
display.clearDisplay();
display.setCursor(0,0);
display.print("0x");
display.print(0xFF, HEX);
display.print("(HEX) = ");
display.print(0xFF, DEC);
display.println("(DEC)");
display.print(" = ");
display.print(0xFF, BIN);
display.println("(BIN)");
display.display();
delay(2000);
// 全螢幕捲動:
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2);
display.println("Full");
display.println("screen");
display.println("scrolling");
display.println("test!");
display.display();
delay(1000);
display.startscrollright(0x00, 0x07);
delay(5000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x07);
delay(5000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(5000);
display.startscrolldiagleft(0x00, 0x07);
delay(5000);
display.stopscroll();
// 部分螢幕捲動之一:
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println(" Scroll");
display.println("some part");
display.println("of the screen.");
display.println("some part");
display.println("Line 5..");
display.println("line 6..");
display.println("line 7..");
display.println("line 8..");
display.println("line 9..");
display.display();
display.startscrollright(0x00, 0x00);
delay(6000);
display.stopscroll();
display.startscrollleft(0x01, 0x01);
delay(6000);
display.stopscroll();
display.startscrollright(0x02, 0x02);
delay(6000);
display.stopscroll();
// 部分螢幕捲動之二:最後會一直循環動作
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2);
display.println(" Scroll");
display.println("some part");
display.println("of the");
display.println(" screen.");
display.display();
while(1) {
display.startscrollright(0x00, 0x00);
delay(5000);
display.stopscroll();
display.startscrollleft(0x01, 0x01);
delay(5000);
display.stopscroll();
display.startscrollright(0x02, 0x03);
delay(5000);
display.stopscroll();
display.startscrollleft(0x02, 0x03);
delay(5000);
display.stopscroll();
display.startscrollright(0x04, 0x05);
delay(5000);
display.stopscroll();
display.startscrollleft(0x04, 0x05);
delay(5000);
display.stopscroll();
display.startscrollright(0x06, 0x07);
delay(5000);
display.stopscroll();
display.startscrollleft(0x06, 0x07);
delay(5000);
display.stopscroll();
}
}
void loop() {}
程式名稱:ESP_OLED1_Text.ino
程式開始的1~24行內容和功能和前一個範例的1~50行是一樣的,只是少了一些和Adafruit功有關的變數而已,至此會初始化這個SSD1304 OLED顯示器,並清除其內部的顯示緩衝記憶體(GDDRAM);由於我們這個範例只是單純示範一些文數字顯示功能的用法,所以就比照前一個Adafruit的範例,把所有的動作都在初始化「setup()」部分就把它演繹完。
前面我們已經說過,要在SSD1306 OLED顯示器上顯示任何文、數字或圖片及動畫時,可歸納為三個步驟:
1、先使用“.clearDisplay”這個方法,在背景將顯示記憶體(GDDRAM)內容清除2、將要顯示的結束處理好之後,載入顯示記憶體(GDDRAM)內
3、使用“.Display”這個方法將處理好的內容從顯示記憶體(GDDRAM)上傳到OLED顯示器面板上,也就是真正的顯示出來
而這三個步驟可由下面這幾行程式的結構來說明,中間”…….”部份就是我們想要執行的動作或是功能,也就是程式設計的地方。
// Clear the buffer
display.clearDisplay();
……………………………………..
………………………………………….
display.display();
delay(2000); // Pause for 2 seconds
中間”…….”部份就是要在SSD1306 OLED顯示器上顯示任何文、數字的程式部分,也可以歸納為四個步驟:
1、設定文字的大小(使用方法”.setTextSize()”)
2、設定文字的顏色(或反白,使用方法”.setTextColor()”)
3、設定文字顯示的位置(以像素為單位,使用方法”.setCursor()”)
4、開始列印或顯示文字字串(使用方法”.write”、”.print”或”.println”)
除此之外,有時候使用者會想在這些已經顯示的文數字上加上一些特殊的效果,例如螢幕捲動(Scroll screen)等,這時就可以在後面接著進行第五個步驟了!這些特殊效果都會在這個範例中示範給各位看。
如果參考前面的【圖4、20】可知,這種OLED顯示器和一般傳統的繪圖型LCD顯示器一樣,都是以螢幕左上角作為原點,即座標(0,0),且水平值是由左至右,而垂直值則由上到下。
接著我們就以條列式的方式加上圖片,為大家說明前面六種文字顯示範例的程式內容。
1、基本英文字串顯示
圖4、24-1 基本文字顯示畫面及程式碼
如【圖4、20-1】左邊螢幕所示內容,便是由右邊的程式碼所完成的,看起來是不是完全符合上面所列舉的四個基本步驟﹖在前面的【圖4、20】中我們已經說明過,整個SSD1306內部1K bytes的GDDRAM結構被分割成8個頁面page(編號為page0~page7),且每個page是由1個byte(8 bits)所構成,也就是說在實際的OLED螢幕上1個page的高度為8個pixels;而當字元尺寸(TextSize)大小為1時,整個螢幕可以顯示8行文字,且每一行文字的數目為20個。
接著就讓我們來看這個範例程式,在顯示正常顏色文字部分的內容為何﹖下面就是這部分的程式碼,在此我們把字型碼的大小設定為2倍,這麼一來OLED顯示器的螢幕可顯示的文字數量就變成上下為4行,而左右水平每行剩10個字了!
// 顯示正常顏色文字:
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(" Program");
display.println(" Start!");
display.println(" Hello");
display.println(" world!");
display.display();
delay(2000);
在上面的程式中,由於一行只能顯示10個英文字,所以把"Program Start! Hello world!"這組字串拆成了四行來顯示;為了能和其他顯示動作區別,所以每一個動作在啟動顯示功能之後,要記得有足夠的時間顯示,這也是為何每個「display.display」指令之後要配合一個延遲((delay())指令的原因,你希望每個動作能看到多久就延遲多久。
2、反白英文字串顯示
圖4、24-2 反白文字顯示畫面及程式碼
如【圖4、20-2】所示,當我們要顯示反白的文字時,只要把設定文字顏色的指令改成下面的格式就可以了
display.setTextColor(BLACK, WHITE); // 'inverted' text
下面幾行程式便是本範例的設計內容(36~43行)
// 顯示反白顏色文字:
display.clearDisplay();
display.setTextColor(BLACK, WHITE); // 'inverted' text
display.setCursor(0,20);
display.println(" Hello ");
display.println(" world!");
display.display();
delay(2000);
這時在螢幕(0,20)開始的位置上,會看到兩行內容分為” Hello “及” world!”的反白英文字串。
3、ASCII字元碼顯示
ASCII是由一個位元組(byte)所構成的字元碼,不過一般來說標準且常用的ASCII碼主要集中在前128(0x00~0x7f)個,而且不是每一個字碼都能在螢幕上顯示出來,因為有許多都是做為控制用的字元,例如換行”\n(0ah)”、回頭”\r(0dh)”等控制字碼。不過因為ASCII碼中空白沒用到的字碼很多,所以就有一些大公司會自性建構屬於他們自己的擴充型ASCII碼,例如日系的LCD控制晶片會在後面的128個字碼中加上日文的五十音,或是早期個人電腦(PC)的原創公司-IBM等都有一套他自己的字碼。下面【圖4、20-3】範例程式在左邊螢幕所示的心形圖案,就是IBM公司所建構之「Code Page 437」字型碼中的第3號字碼,而這些特殊的字形碼如果要在OLED顯示器上顯示出來,就必須使用「display.write(3)」這個指令。
圖4、24-3 ASCII字元碼顯示畫面及程式碼
在我們的範例程式中,挑了「Code Page 437」字型碼前面幾個比較有趣的圖形字碼做為示範,其內容除了心形之外,還有撲克牌的四個花色及音符等,在後面的展示影片中可以看得到、當然最好是讀者自己動手測試!
// 顯示 ASCII 碼字元:
display.clearDisplay();
display.cp437(true);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.setTextSize(4);
for(int i=1;i<7;i++)
display.write(i);
for(int i=11;i<15;i++)
display.write(i);
display.display();
delay(4000);
上面的幾行程式是本範例中和顯示特殊ASCII碼有關的部分,基本上和前面顯示標準的文字部分類似,只是把列印指令由:
display.print() 🡪 display.weite()
此外最重要的就是宣告要啟動IBM公司的「Code Page 437」字型碼,這樣才能順利把圖/字形顯示出來!
display.cp437(true);
4、數字顯示練習
// 顯示數字:
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0,28);
display.println(1234567890);
display.display();
delay(2000);
要在OLED顯示器上顯示數字和顯示文字看起來沒多大的差別,上面的程式是本範例用來顯示數字的部分,可是請注意下面6種方法顯示數字的結果是不一樣的,有些在編譯時就無法通過了,大家覺得哪些是OK的呢﹖
display.println(1234567890); // 編譯通過,顯示正常
display.println(“1234567890”); // 編譯通過,顯示正常
display.println(0123456789); // 編譯不通過
display.println(“0123456789”); // 編譯通過,顯示正常
display.println(12345678901); // 編譯不通過
display.println(“12345678901”); // 編譯通過,顯示正常
上述6種方式中,奇數部分括弧中的參數都是純數字,而偶數部分則是字串,由結果可知,如果顯示的參數是字串就不會有問題;由於在此使用了2號的字型大小,所以一行只能顯示10個數字,第6種方式因為括弧中共有11個數字,所以最後一個字(1)會跑到下一行的第一個位置;至於第5種方式,則是因為系統在編譯時會自動檢查每一行數字的長度,所以如果超過10位數就無法通過!至於第3種不通過的原因,就請讀者自行實作測試看看,錯誤訊息說明的原因為何﹖
5、不同數字基底顯示
前面已經說明過,如果要顯示純數字很容易遇到一些不容易看出來的問題,而之所以要使用純數字的方式,原因就是因為它可以很方便的幫我們以不同的數字基底形式去顯示;下面的【圖4、20-4】範例程式便是以16進制及10進制的方式顯示0xFF這個數值,不管是「display.print()」或是「display.println()」這兩種指令,如果括弧中的參數是純數字,那麼我們可加上第二個用以指定數字基底的參數,幫我們自動轉換顯示的格式,目前可用的數值基底有BIN (2進制)、OCT (8進制)、DEC (10進制)及HEX (16進制)。
此外當數字有小數點時,也可以加上第二個參數來指定小數點後面要顯示幾位數。
圖4、24-4 數字與基底顯示畫面及程式碼
// 顯示不同基底的數字:16,10及2進制
display.clearDisplay();
display.setCursor(0,0);
display.print("0x");
display.print(0xFF, HEX);
display.print("(HEX) = ");
display.print(0xFF, DEC);
display.println("(DEC)");
display.print(" = ");
display.print(0xFF, BIN);
display.println("(BIN)");
display.display();
delay(2000);
上述程式碼是本範例程式中示範顯示不同數值基底的部分,在此我們將”0xFF”這個16進制數字再轉成10進制及2進制,最後在OLED顯示器的螢幕上會看到下列的結果:
0xFF(HEX) = 255((DEC) = 11111111(BIN)
不過因為字型大小設定為2,所以並不會顯示在同一行上。
6、英文字串全螢幕捲動顯示
在本節程式說明的一開始就說過,如果要在SSD1306 OLED顯示器上顯示任何文、數字,可以歸納為四個步驟,之後就可以加上一些特殊的顯示效果,在本小節中將示範如何讓整個螢幕捲動。
圖4、24-5 文字全螢幕捲動顯示畫面及程式碼
以上面的【圖4、20-5】範例程式來說,在顯示完左邊的三行文字之後,共進行了四種的全螢幕捲動動作,分別是:
往右捲動(使用「display.startscrollright(0x00,0x07)」指令)
往左捲動(使用「display.startscrollleft(0x00,0x07)」指令)
往右上角捲動(使用「display.startscrolldiagright(0x00,0x07) 指令」)
往左上角捲動(使用「display.startscrolldiagleft(0x00,0x07)」指令)
如果要停止捲動的動作,只要呼叫下列指令即可:
display.stopscroll();
其實上述四個捲動指令是捲動動動作的通用指令,也可以用在部分螢幕的捲動,後面的範例會再對此作詳細的說明。由指令的方法名稱應該就可以看出它在做什麼動作,例如往右捲動的方法為「.startscrollright()」,所以能捲動整個螢幕,主要是靠後面括弧中的兩個參數,它們分別代表起點頁(start page)和終點頁(end page),而所謂的頁(page),就是前面提到SSD1306控制晶片內部GDDRAM結構中的8個頁面,這8個頁面(page)編號由page0~page7,由於上述四個捲動指令後面括弧的部分都是”(0x00,0x07)”,因此就代表選取了整個螢幕!
接著讓我們看看範例程式中關於全螢幕捲動的部分(82~104行),程式碼如下面所示,其實和上面的【圖4、20-5】範例程式相差無幾,主要是把字型大小放大兩倍,並加上第四行的”test!”文字而已;如此一來整個螢幕都有文字在上面,捲動的效果會比較清楚,除此之外每個動作執行的時間也加長了一些,這樣比較好觀察結果。
// 全螢幕捲動:
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2);
display.println("Full");
display.println("screen");
display.println("scrolling");
display.println("test!");
display.display();
delay(1000);
display.startscrollright(0x00, 0x07);
delay(5000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x07);
delay(5000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(5000);
display.startscrolldiagleft(0x00, 0x07);
delay(5000);
display.stopscroll();
7、英文字串部分捲動顯示
前面說過這些捲動指令是通用指令,也可以用來捲動部分的螢幕,下方的【圖4、20-6】範例程式便是把捲動的範圍侷限在第一頁(也可以說是第一行),而且是往右捲動,這是因為下面這行指令中兩個參數都是”0x00”,也就是只捲動第一行的意思。
圖4、24-6 文字部分捲動顯示畫面及程式碼
在這單元的範例程式中,共設計了兩個部分螢幕捲動的範例,好讓讀者可比較其中的差異,首先是106~128行的【部分螢幕捲動之一】,在下方程式中我們先設定字型大小為1,也就是最小的一種,在這種模式下,整個螢幕可以顯示8行的文字;可是在5~13行的程式碼卻列印了9行的文字,也就是說第9行的”line 9..”這一段文字將不會出現在螢幕上,做了也是白搭,在此只是讓讀者了解螢幕真正的大小而已!
在所有的測試文字都顯示完之後,剩下的程式其實只對1、2、3執行左右的捲動動作而已,由於內容很簡單,所以在此就不多做說明,請自行觀看後面影片中實際執行的效果。
1. // 部分螢幕捲動之一:
2. display.clearDisplay();
3. display.setCursor(0,0);
4. display.setTextSize(1);
5. display.println(" Scroll");
6. display.println("some part");
7. display.println("of the screen.");
8. display.println("some part");
9. display.println("Line 5..");
10. display.println("line 6..");
11. display.println("line 7..");
12. display.println("line 8..");
13. display.println("line 9..");
14. display.display();
15. display.startscrollright(0x00, 0x00);
16. delay(6000);
17. display.stopscroll();
18. display.startscrollleft(0x01, 0x01);
19. delay(6000);
20. display.stopscroll();
21. display.startscrollright(0x02, 0x02);
22. delay(6000);
23. display.stopscroll();
至於130~164行的【部分螢幕捲動之二】,則是把字型放大兩倍後,同樣再次顯示前面的四行文字內容(如下列程式所示的2~9行),而剩下的部分則是由一個無窮的while(1) {….}所構成,這部分的程會依序把這四行文字做左右的捲動,不過要注意第一行的文字是由下列兩行指令所控制:
11. display.startscrollright(0x00, 0x00);
14. display.startscrollleft(0x01, 0x01);
也就是說” Scroll”這行文字會被切割成兩半,這是因為在前面已經說過,SSD1306控制晶片內部GDDRAM結構會把整個OLED螢幕分割成8個頁面,這8個頁面(page)編號由page0~page7,當我們把文字大小設成2倍時,每一行的文字其實是佔了兩個頁的高度,所以當我們只捲動一個頁面時(也就是起點頁和終點頁這兩個參數值相同)就等於只移動了一半的文字畫面而已,這也就是畫面上的文字看起來被切成兩半的原因!
剩下部分程式的捲動指令不管方向為何,都是一次選取兩個頁面,所以看起來就是很正常的捲動一整行。程式至此,會一直重複執行範例程式中140~163行的動作,也就是無窮的while(1) {….}迴圈不再離開。
1. // 部分螢幕捲動之二:最後會一直循環動作
2. display.clearDisplay();
3. display.setCursor(0,0);
4. display.setTextSize(2);
5. display.println(" Scroll");
6. display.println("some part");
7. display.println("of the");
8. display.println(" screen.");
9. display.display();
10. while(1) {
11. display.startscrollright(0x00, 0x00);
12. delay(5000);
13. display.stopscroll();
14. display.startscrollleft(0x01, 0x01);
15. delay(5000);
16. display.stopscroll();
17. display.startscrollright(0x02, 0x03);
18. delay(5000);
19. display.stopscroll();
20. display.startscrollleft(0x02, 0x03);
21. delay(5000);
22. display.stopscroll();
23. display.startscrollright(0x04, 0x05);
24. delay(5000);
25. display.stopscroll();
26. display.startscrollleft(0x04, 0x05);
27. delay(5000);
28. display.stopscroll();
29. display.startscrollright(0x06, 0x07);
30. delay(5000);
31. display.stopscroll();
32. display.startscrollleft(0x06, 0x07);
33. delay(5000);
34. display.stopscroll();
35. }
沒有留言:
張貼留言