2020年6月15日 星期一

OLED顯示器各式文、數字顯示實習

四.10-2  OLED顯示器各式文數字顯示實習

前一個由Adafruit原廠所提供的範例中可以說把OLED顯示器上可以使用的功能都演繹了一次但是一次把太多東西湊在一起對於初學者來說可能會沒辦法吸收因此筆者打算把那些動作與功能分解成不同的屬性後個別寫一個測試程式去示範一次。

不管是那一種顯示器最基本的至少都能顯示文數字而目前市面上能看得到的OLED顯示器其實都是繪圖型的顯示器只是透過程式的處理或是使用別人設計好的函式庫就可以很容易地在上面顯示各種形式的文數字在這個範例中將會示範一些比較常用的文字顯示功能而這些功能對一般的應來說應該很夠了

功能與動作說明

在本實習中我們會示範下列幾種跟文數字或ASCII字元碼有關的顯示範例讓大家參考

、基本英文字串顯示練習

、反白英文字串顯示練習

ASCII字元碼顯示練習

、數字顯示練習

5、不同數字基底顯示練習

、英文字串全螢幕捲動顯示練習

、英文字串部分捲動顯示練習


電路圖

圖425 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的供應電壓比較好


程式列表與說明

 

  1. #include <Wire.h>

  2. #include <Adafruit_GFX.h>

  3. #include <Adafruit_SSD1306.h>

  4.  

  5. #define SCREEN_WIDTH 128 // OLED display width, in pixels

  6. #define SCREEN_HEIGHT 64 // OLED display height, in pixels

  7.  

  8. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

  9.  

  10. void setup()   

  11. {                

  12.   Serial.begin(115200);

  13.   Serial.println();

  14.   // initialize with the I2C addr 0x3C

  15. if(display.begin(SSD1306_SWITCHCAPVCC, 0x3C))  // Address 0x3D for 128x64

  16.   Serial.println("SSD1036 OLED allocation Successed!");

  17. else  {

  18.     Serial.println("SSD1306 allocation failed");

  19.     for(;;);

  20.   }

  21.   delay(2000);

  22.  

  23.   // 清除顯示緩衝區:

  24.   display.clearDisplay();

  25.  

  26.   // 顯示正常顏色文字:

  27.   display.setTextSize(2);

  28.   display.setTextColor(WHITE);

  29.   display.setCursor(0,0);

  30.   display.println(" Program");

  31.   display.println("  Start!");

  32.   display.println("  Hello");

  33.   display.println("  world!");

  34.   display.display();

  35.   delay(2000);

  36.  

  37.   // 顯示反白顏色文字:

  38.   display.clearDisplay();

  39.   display.setTextColor(BLACK, WHITE); // 'inverted' text

  40.   display.setCursor(0,20);

  41.   display.println(" Hello ");

  42.   display.println(" world!");

  43.   display.display();

  44.   delay(2000);

  45.  

  46.   // 顯示 ASCII 碼字元:

  47.   display.clearDisplay();

  48.   display.cp437(true);

  49.   display.setTextColor(WHITE);

  50.   display.setCursor(0,0);

  51.   display.setTextSize(4);

  52.   for(int i=1;i<7;i++) 

  53.     display.write(i);

  54.   for(int i=11;i<15;i++) 

  55.     display.write(i);

  56.   display.display();

  57.   delay(4000);

  58.  

  59.   // 顯示數字:

  60.   display.clearDisplay();

  61.   display.setTextColor(WHITE);

  62.   display.setTextSize(2);

  63.   display.setCursor(0,28);

  64.   display.println(1234567890);

  65.   display.display();

  66.   delay(2000);

  67.  

  68.   // 顯示不同基底的數字:16,10及2進制

  69.   display.clearDisplay();

  70.   display.setCursor(0,0);

  71.   display.print("0x"); 

  72.   display.print(0xFF, HEX); 

  73.   display.print("(HEX) = ");

  74.   display.print(0xFF, DEC);

  75.   display.println("(DEC)"); 

  76.   display.print(" = ");

  77.   display.print(0xFF, BIN);

  78.   display.println("(BIN)"); 

  79.   display.display();

  80.   delay(2000);

  81.  

  82.   // 全螢幕捲動:

  83.   display.clearDisplay();

  84.   display.setCursor(0,0);

  85.   display.setTextSize(2);

  86.   display.println("Full");

  87.   display.println("screen");

  88.   display.println("scrolling");

  89.   display.println("test!");

  90.   display.display();

  91.   delay(1000);

  92.   display.startscrollright(0x00, 0x07);

  93.   delay(5000);

  94.   display.stopscroll();

  95.   delay(1000);

  96.   display.startscrollleft(0x00, 0x07);

  97.   delay(5000);

  98.   display.stopscroll();

  99.   delay(1000);    

  100.   display.startscrolldiagright(0x00, 0x07);

  101.   delay(5000);

  102.   display.startscrolldiagleft(0x00, 0x07);

  103.   delay(5000);

  104.   display.stopscroll();

  105.  

  106.   // 部分螢幕捲動之一:

  107.   display.clearDisplay();

  108.   display.setCursor(0,0);

  109.   display.setTextSize(1);

  110.   display.println("  Scroll");

  111.   display.println("some part");

  112.   display.println("of the screen.");

  113.   display.println("some part");

  114.   display.println("Line 5..");

  115.   display.println("line 6..");

  116.   display.println("line 7..");

  117.   display.println("line 8..");

  118.   display.println("line 9..");

  119.   display.display();

  120.   display.startscrollright(0x00, 0x00);

  121.   delay(6000);

  122.   display.stopscroll();

  123.   display.startscrollleft(0x01, 0x01);

  124.   delay(6000);

  125.   display.stopscroll();

  126.   display.startscrollright(0x02, 0x02);

  127.   delay(6000);

  128.   display.stopscroll();

  129.   

  130.   // 部分螢幕捲動之二:最後會一直循環動作

  131.   display.clearDisplay();

  132.   display.setCursor(0,0);

  133.   display.setTextSize(2);

  134.   display.println(" Scroll");

  135.   display.println("some part");

  136.   display.println("of the");

  137.   display.println(" screen.");

  138.   display.display();

  139.   while(1)  {

  140.     display.startscrollright(0x00, 0x00);

  141.     delay(5000);

  142.     display.stopscroll();

  143.     display.startscrollleft(0x01, 0x01);

  144.     delay(5000);

  145.     display.stopscroll();

  146.     display.startscrollright(0x02, 0x03);

  147.     delay(5000);

  148.     display.stopscroll();

  149.     display.startscrollleft(0x02, 0x03);

  150.     delay(5000);

  151.     display.stopscroll();

  152.     display.startscrollright(0x04, 0x05);

  153.     delay(5000);

  154.     display.stopscroll();

  155.     display.startscrollleft(0x04, 0x05);

  156.     delay(5000);

  157.     display.stopscroll();

  158.     display.startscrollright(0x06, 0x07);

  159.     delay(5000);

  160.     display.stopscroll();

  161.     display.startscrollleft(0x06, 0x07);

  162.     delay(5000);

  163.     display.stopscroll();

  164.   }

  165. }

  166.  

  167. 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)等這時就可以在後面接著進行第五個步驟了!這些特殊效果都會在這個範例中示範給各位看。

如果參考前面的圖420可知這種OLED顯示器和一般傳統的繪圖型LCD顯示器一樣都是以螢幕左上角作為原點即座標(0,0)且水平值是由左至右而垂直值則由上到下

接著我們就以條列式的方式加上圖片為大家說明前面六種文字顯示範例的程式內容。


、基本英文字串顯示

圖424-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())指令的原因你希望每個動作能看到多久就延遲多久。


、反白英文字串顯示


圖424-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!”的反白英文字串


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)」這個指令。


圖424-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);


、數字顯示練習


// 顯示數字:

  display.clearDisplay();

  display.setTextColor(WHITE);

  display.setTextSize(2);

  display.setCursor(0,28);

  display.println(1234567890);

  display.display();

  delay(2000);


要在OLED顯示器上顯示數字和顯示文字看起來沒多大的差別上面的程式是本範例用來顯示數字的部分可是請注意下面6種方法顯示數字的結果是不一樣的有些在編譯時就無法通過了大家覺得哪些是OK的呢


  1. display.println(1234567890); // 編譯通過,顯示正常

  2. display.println(“1234567890”); // 編譯通過,顯示正常

  3. display.println(0123456789); // 編譯不通過

  4. display.println(“0123456789”); // 編譯通過,顯示正常

  5. display.println(12345678901); // 編譯不通過

  6. 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進制)。

此外當數字有小數點時也可以加上第二個參數來指定小數點後面要顯示幾位數


圖424-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顯示器上顯示任何文、數字,可以歸納為四個步驟之後就可以加上一些特殊的顯示效果在本小節中將示範如何讓整個螢幕捲動


圖424-5 文字全螢幕捲動顯示畫面及程式碼


以上面的圖4、20-5】範例程式來說在顯示完左邊的三行文字之後共進行了四種的全螢幕捲動動作分別是

  1. 往右捲動(使用「display.startscrollright(0x00,0x07)」指令)

  2. 往左捲動(使用「display.startscrollleft(0x00,0x07)」指令)

  3. 往右上角捲動(使用「display.startscrolldiagright(0x00,0x07) 指令」)

  4. 往左上角捲動(使用「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”也就是只捲動第一行的意思


圖424-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.  }


#以下畫面為本範例實際執行的結果



沒有留言:

張貼留言