2021-02-22

Arduino 經 ST7789 控制 TFT IPS LCD 熒幕

之前使用的 單色LCD 或 多段顯示器 實際只是 1位元的方式顯示,即是使用開或關的方式顯示
雖然曾經使用電子紙總有 3種顏色 ,但在下使用的電子紙並非使用調色方法設定顏色
因此使用 TFT LCD 測試效果

TFT IPS LCD 熒幕

見下文
LCD熒幕的正面

見下文
模組底部會寫著型號,例如在下使用
  • 尺寸為 1.54寸
  • 像素為 240x240
  • 顏色顯示為 RGB

見下文
模組頂部寫著引腳的功能
(最左為第1引腳)

見下文
LCD熒幕的背面

見下文
寫著驅動晶片名稱(ST7789)及介面類型(SPI)

見下文
背面還有一些焊接用的焊墊,可以用作焊接 SD卡模組

TFT IPS LCD 操作訊號

CD RW Data DEC HEX 說明
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 0 0 1 1 0x01 軟件重設
執行重設後,需要等待最少5毫秒才完成
0 0 0 0 0 1 0 0 0 # 16 0x10 設定休眠模式
  • 0x00 = 關閉
  • 0x01 = 開啟
0 0 0 0 1 0 0 0 0 # 32 0x20 設定負片模式
  • 0x00 = 關閉
  • 0x01 = 開啟
0 0 0 0 1 0 0 1 1 0 38 0x26 設定Gamma修正
(1位元組設定值)
1 0 # # # # # # # # # #
0 0 0 0 1 0 1 0 0 # 40 0x28 設定熒幕狀態
  • 0x00 = 關閉
  • 0x01 = 開啟
0 0 0 0 1 0 1 0 1 0 43 0x2A 設定寫入欄的範圍
第1位元組 第15位元 至 第8位元 欄開始位置
第2位元組 第7位元 至 第0位元 欄開始位置
第3位元組 第15位元 至 第8位元 欄結束位置
第4位元組 第7位元 至 第0位元 欄結束位置
(4位元組設定值)
1 0 S S S S S S S S # #
1 0 S S S S S S S S # #
1 0 E E E E E E E E # #
1 0 E E E E E E E E # #
0 0 0 0 1 0 1 0 1 1 42 0x2B 設定寫入列的範圍
第1位元組 第15位元 至 第8位元 列開始位置
第2位元組 第7位元 至 第0位元 列開始位置
第3位元組 第15位元 至 第8位元 列結束位置
第4位元組 第7位元 至 第0位元 列結束位置
(4位元組設定值)
1 0 S S S S S S S S # #
1 0 S S S S S S S S # #
1 0 E E E E E E E E # #
1 0 E E E E E E E E # #
0 0 0 0 1 0 1 1 0 0 44 0x2C 寫入內容
位元組數量與 寫入欄及列的範圍相應
如果設定寫入:
欄的範圍為 0 至 9 ,列的範圍為 0 至 4
便需要寫入 10欄 x 5列 共 50個位元組資料
(多位元組設定值)
1 0 # # # # # # # # # #
0 0 0 0 1 1 0 0 1 1 51 0x33 設定捲動範圍
第1位元組 第15位元 至 第8位元 固定的頂部高度
第2位元組 第7位元 至 第0位元 固定的頂部高度
第2位元組 第15位元 至 第8位元 捲動的內容高度
第2位元組 第7位元 至 第0位元 捲動的內容高度
第3位元組 第15位元 至 第8位元 固定的底部高度
第3位元組 第7位元 至 第0位元 固定的底部高度
T + S + B = 320 ,否則無效,會重設
(6位元組設定值)
1 0 T T T T T T T T # #
1 0 T T T T T T T T # #
1 0 S S S S S S S S # #
1 0 S S S S S S S S # #
1 0 B B B B B B B B # #
1 0 B B B B B B B B # #
0 0 0 0 1 1 0 1 0 # 52 0x34 設定畫面撕裂模式
  • 0x00 = 關閉
  • 0x01 = 開啟
0 0 0 0 1 1 0 1 1 1 55 0x37 設定捲動列數
第1位元組 為 第15位元 至 第8位元
第2位元組 為 第7位元 至 第0位元
(2位元組設定值)
1 0 # # # # # # # # # #
1 0 # # # # # # # # # #
0 0 0 0 1 1 1 0 0 # 56 0x38 設定閒置模式
  • 0x00 = 關閉
  • 0x01 = 開啟
0 0 0 0 1 1 1 0 1 0 58 0x3A 設定記憶體用量及顏色深度
R 為使用記憶體用量
  • 0x03 - 12位元記憶體
  • 0x05 - 16位元記憶體
  • 0x06 - 18位元記憶體
  • 0x07 - 24位元記憶體
B 為使用顏色深度
  • 0x03 - 12位元顏色深度
  • 0x05 - 16位元顏色深度
  • 0x06 - 18位元顏色深度
  • 0x07 - 24位元顏色深度
(1位元組設定值)
1 0 0 R R R 0 B B B # #
0 0 0 1 0 1 0 0 0 1 81 0x51 設定光度數值
(1位元組設定值)
1 0 B B B B B B B B # #

同樣還有很多功能及設定,但這些功能便可以在熒幕上繪製出圖案,其他功能可以在資料表上找出及嘗試

ST7789 引腳功能

編號 名稱 方向 功能
1 GND LCD 熒幕 接地
(TFT IPS LCD 引腳)
2 VCC LCD 熒幕 電源,接受 5V
(TFT IPS LCD 引腳)
3 SCL 輸入 序列時脈,資料由高至低取值
每8位元資料為1週期
4 SDA 輸入 序列資料
5 RES 輸入 低電壓時,重置所有控制模式的設定值
若果不需要重置,保持高電壓
(TFT IPS LCD 引腳)
6 DC 輸入 低電壓時,為控制模式
高電壓時,為資料模式
(TFT IPS LCD 引腳)
7 CS 輸入 低電壓時,為啟動狀態
高電壓時,為閒置狀態
8 BLK LED 背光 電源,接受 5V
(TFT IPS LCD 引腳)

ST7789 是使用 最高有效位 (Most Significant Bit (MSB)) 次序發送訊號
引腳初始化開始1位元組 控制 週期n位元組 資料 週期 (選用)結束
延遲1微秒
RES
CS
DC
SCL
SDA高/低高/低

訊號波紋時序圖
RES CS DC SCL SDA

線路接駁

見下文
如果使用 Arduino SPI引腳SPI.h 函式庫,可以使用這種接駁方式
Arduino引腳 ST7789引腳
GND GND
5V VCC
13 (SCK) SCL
11 (MOSI) SDA
9 RES
8 DC
10 (SS) CS
5V BLK

見下文
見下文
今次在下接駁線路,使用預設的 SPI.h 的 API

見下文
接駁 電源 及 LCD背光 , TFT IPS LCD 仍未顯示畫面
要讓熒幕顯示畫面,最少需要
  • 取消休眠狀態
  • 顯示畫面
  • 設定顏色

見下文
起動 TFT IPS LCD 後,會顯示雜訊是正常情況

填色

要在 TFT IPS LCD 上填色,要先設定欄列開始及結束位置,再將顏色寫入到 Display Data RAM (DDRAM)
例如在下的 TFT IPS CDL 欄列各有240像素,即是如果要填滿整個畫面,欄列的開始及結束位置分別是 0 及 239

見下文
在下使用紅綠藍三原色測試顏色

捲動效果

雖然在下使用的 TFT IPS LCD 實際上能夠支援 240x320 ,但顯示範圍只有 240x240
能夠透過捲動功能,顯示 240 之後的內容

見下文
列數範圍 預設顯示範圍 可讀寫範圍
0 ~ 239
240 ~ 319
320 ~ 511
捲動的內容為循環顯示
捲動值為 320, 832, 1344 (512倍數 - 192) 等數值都是相同
捲動值為 511, 1023, 1535 (512倍數 - 1) 等數值後,都會出現撕裂畫面,這個範圍是無法讀寫

捲動範圍

見下文
例如只使用預設顯示範圍,設定 T = 0, S = 240, B = 80 ,捲動範圍為 0 ~ 239

見下文
設定 T = 60, S = 120, B = 140 ,便可以模擬標頭、註腳、內容的捲動效果
但當改變捲動範圍後,要製作循環效果,捲動的數值範圍都要改變
以 T = 60, S = 120, B = 140 為例,捲動的數值範圍 是 60 ~ 179 ,而不是 0 至 119
以方程式表示可以寫成
L = 0; // 
L = L % S + T;
便可以修正捲動數值

顏色深度

數碼顏色深度,在現今的電子工具上,通常都使用 24位元 ,即是以 紅、綠、藍 三原色為基礎,各使用 8位元,甚至更高顏色深度
雖然 ST7789 可以設定使用 24位元顏色深度 ,實際上記憶體未能支援
ST7789 還有 12位元 、 16位元 、 18位元 顏色深度,預設是使用 16位元顏色深度

12位元顏色深度
12位元顏色深度 共有4096種顏色,能夠讓紅、綠、藍平均分配,各使用4位元

D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

分配顏色時,以 3位元組 控制 2組顏色
第1位元組 的 第7位元 至 第4位元 控制 第1組紅色
第1位元組 的 第3位元 至 第0位元 控制 第1組綠色
第2位元組 的 第7位元 至 第4位元 控制 第1組藍色
第2位元組 的 第3位元 至 第0位元 控制 第2組紅色
第3位元組 的 第7位元 至 第4位元 控制 第2組綠色
第3位元組 的 第3位元 至 第0位元 控制 第2組藍色

第1位元組 第2位元組 第3位元組
D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0
第1組紅色 第1組綠色 第1組藍色 第2組紅色 第2組綠色 第2組藍色

16位元顏色深度
16位元顏色深度 共有65536種顏色,不能夠讓紅、綠、藍平均分配,紅藍各使用5位元,綠色使用6位元

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

分配顏色時
第1位元組 的 第7位元 至 第3位元 控制 紅色
第1位元組 的 第2位元 至 第0位元 及 第2位元組 的 第7位元 至 第5位元 控制 綠色
第2位元組 的 第4位元 至 第0位元 控制 藍色

第1位元組 第2位元組
D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0
紅色 綠色 藍色

18位元顏色深度
18位元顏色深度 共有262144種顏色,能夠讓紅、綠、藍平均分配,各使用6位元

D17 D16 D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

分配顏色時
第1位元組 的 第7位元 至 第2位元 控制 紅色
第2位元組 的 第7位元 至 第2位元 控制 綠色
第3位元組 的 第7位元 至 第2位元 控制 藍色

第1位元組 第2位元組 第3位元組
D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0
紅色 綠色 藍色

在資料轉換上,在下認為使用 16位元 或 18位元 操作上會比較簡單
16位元 使用 2位元組 , 18位元 使用 3位元組
而 12位元,其一:顏色深度不足,可調整的顏色數量不多;
其二:寫入 DDRAM 的程序繁複,還要區分 第1組顏色 及 第2組顏色,非常麻煩,因此不建議使用 12位元

顏色深度換算
由於在一般電腦語言通常都是使用 24位元顏色深度,要使用其他位元顏色深度便需要轉換
Arduino 提供 map 可以簡單將目標數值的範圍轉換到成另一個範圍
byte y = map(x, 0x00, 0xFF, 0x00, 0x0F);
便可以簡單地將 8位元顏色深度 轉換成 4位元顏色深度
然後再要將常用的 24位元RGB 轉換,例如 16位元
unsigned long convert24to18(unsigned long rgb) {
    unsigned long r = (rgb >> 16) & 0xFF;
    unsigned long g = (rgb >> 8) & 0xFF;
    unsigned long b = rgb & 0xFF;
    r = map(r, 0x00, 0xFF, 0x00, 0x3F) << 16;
    g = map(g, 0x00, 0xFF, 0x00, 0x3F) << 8;
    b = map(b, 0x00, 0xFF, 0x00, 0x3F);
    return r | g | b;
}

unsigned long convert24to16(unsigned long rgb) {
    unsigned long r = (rgb >> 16) & 0xFF;
    unsigned long g = (rgb >> 8) & 0xFF;
    unsigned long b = rgb & 0xFF;
    r = map(r, 0x00, 0xFF, 0x00, 0x1F) << 11;
    g = map(g, 0x00, 0xFF, 0x00, 0x3F) << 5;
    b = map(b, 0x00, 0xFF, 0x00, 0x1F);
    return r | g | b;
}

unsigned long convert24to12(unsigned long rgb, bool isFirst) {
    unsigned long r = (rgb >> 16) & 0xFF;
    unsigned long g = (rgb >> 8) & 0xFF;
    unsigned long b = rgb & 0xFF;
    if (isFirst) {
        r = map(r, 0x00, 0xFF, 0x00, 0x0F) << 12;
        g = map(g, 0x00, 0xFF, 0x00, 0x0F) << 8;
        b = map(b, 0x00, 0xFF, 0x00, 0x0F) << 4;
    } else {
        r = map(r, 0x00, 0xFF, 0x00, 0x0F) << 8;
        g = map(g, 0x00, 0xFF, 0x00, 0x0F) << 4;
        b = map(b, 0x00, 0xFF, 0x00, 0x0F);
    }
    return r | g | b;
}
便可以將 24位元顏色深度 轉換為 不同顏色深度,例如:12位元、16位元、18位元

見下文
使用 PM5566 熒幕測試

見下文
顯示 Linux Tux 亦沒有問題

總結

TFT的操作訊號,有點像編寫程度時,使用 API 時執行一些 功能(Function) 或 方法(Method) ,例如:
軟件重設 0x01(); 及 捲動列數 0x37(#, #); 非常相似

過往使用 1位元組 便可以控制 8個像素 的單色 LCD 熒幕非常不同
最初閱讀總 300多頁的資料表,完全不懂操作方法,只能使用 Arduino 的函式庫
使用函式庫當然能夠運作正常,但卻不太明白運作原理
最後只能慢慢將函式庫的原始碼逆向工程,才理解運作原理

另外在下發現使用預設的 SPI.h 函式庫執行速度比自行編寫的 SPI 更快
原因是 ATmega328P 為指定引腳供硬件級別的 SPI協定,不需要在軟件上透過編寫程式來模擬
而且原生的 硬件電子訊號 比 軟件模擬電子訊號 快得多 但原生SPI 必須使用預設引腳,軟件模擬SPI 則可以 改用其他引腳

在下亦將自行編寫的函式庫上載,閣下有興趣可以
https://bitbucket.org/hkgoldenmra/tft240240rgbst7789module

https://create.arduino.cc/editor/hkgoldenmra/ecee6e5f-cd14-42ec-ad29-69e62cac805d/preview
或在 Terminal 輸入
git clone "https://bitbucket.org/hkgoldenmra/tft240240rgbst7789module.git" --depth=1
下載測試

參考資料

沒有留言 :

張貼留言