2025-10-07

使用觸控熒幕改裝成 USB HID

在下曾經製作一些模擬鍵盤、滑鼠、遊戲控制器等 USB HID
但通常功能越多便需要連接越多按鈕,或需要在編寫一鍵多功能的程式設計
所以在下打算參考現代的觸控操作產品,製作一個類似 Stream Deck 的輔助工具

電阻式觸控熒幕

現今常見的觸控熒幕主要是: 電阻式(Resistive)觸控熒幕 及 電容式(Capacitive)觸空熒幕
兩者的運作原理在下不在這裡詳說明,在下只測試觸控熒幕作為 USB HID 裝置的可行性

外觀

型號為 MSP2807 的 2.8寸 TFT 電阻式觸控熒幕模組 的正面
其解像度為 240x320

MSP2807 的背面

引腳
編號 引腳 方向 功能
1 VCC 電源,接受 3.3V 至 5V
2 GND 接地
3 CS 輸入 LCD 的 CS引腳,低電壓時選取
4 RESET 輸入 LCD 的 RESET引腳,從高電壓到低電壓並保持10微秒後回到高電壓,執行重設
5 D/C 輸入 LCD 的 D/C引腳,高電壓時寫入資料,低高電時寫入指令
6 SDI(MOSI) 輸入 LCD 的 SDI(MOSI)引腳,寫入到暫存器的資料
7 SCK 輸入 LCD 的 SCK引腳,時脈從高到低時將資料寫入及讀取
8 LED 輸入 LCD 的 LED引腳,高電壓時高著背光燈
9 SDO(MISO) 輸出 LCD 的 SDO(MISO)引腳,從暫存器讀寫的資料
10 T_CLK 輸入 Touch 的 CLK引腳,時脈從高到低時將資料寫入及讀取
11 T_CS 輸入 Touch 的 CS引腳,低電壓時選取
12 T_DIN 輸入 Touch 的 DIN引腳,寫入到暫存器的資料
13 T_OUT 輸出 Touch 的 OUT引腳,從暫存器讀寫的資料
14 T_IRQ 輸出 Touch 的 IRQ引腳,中斷訊號並讓裝置資料
15 SD_SS 輸入 SD 的 SS引腳,低電壓時選取
16 SD_MOSI 輸入 SD 的 MOSI引腳,寫入到暫存器的資料
17 SD_MISO 輸出 SD 的 MISO引腳,從暫存器讀寫的資料
18 SD_SCK 輸入 SD 的 SCK引腳,時脈從高到低時將資料寫入及讀取
控制晶片

HR2046 是一種與 XPT2046 相同的觸控操作晶片

引腳名稱以 T_ 為前綴都是 與 觸控(Touch) 相關的引腳

SD卡控制

觸控熒幕還提供 SD卡模組 ,可以將容量大的圖片資料保存到 SD卡

引腳名稱以 SD_ 為前綴都是 與 SD控制 相關的引腳

注意:

由於 觸控熒幕模組 沒有提供 電阻 或 3.3V電壓穩定器 到 SD卡模組
另外 熒幕控制晶片 及 觸控晶片 同樣需要使用 3.3V
如果 微控制器 的 GPIO 電壓只能輸出 5V
可能會損壞 SD卡 、 熒幕控制晶片 及 觸控晶片

因此需要使用 電位器(Potentiometer) 、 電壓分配定則(Voltage Divider Rule) 、
電位移轉器(Logic Level Shifter) 讓電壓設定為 3.0V 至 3.3V
因此測試時,建議使用 Raspberry Pi Pico 、
ESP8266 或 ESP32 這些 GPIO 為 3.3V 輸出的 微控制器

控制熒幕

線路原型

Raspberry Pi Pico MSP2807
5V VCC, LED
GND GND
3V3 RESET
GP22 D/C
GP21 CS
GP19 SDI(MOSI)
GP18 SCK
GP16 SDO(MISO)

實際線路

Sketch > Include Library > Manage Libraries... 搜尋 ili9341 並下載 Adafruit ILI9341

第一次下載 Adafruit ILI9341 會建議下載相依函式庫,包括:

  • Adafruit GFX Library
  • Adafruit BusIO
  • Adafruit STMPE610
  • Adafruit TouchScreen
  • Adafruit TSC2007
  • Adafruit SH110X

Install all 安裝所有相依函式庫

File > Examples > Adafruit ILI9341 > graphicstest

將 範例 的 TFT_DCTFT_CS 的引腳分別修改成 L_DCL_SS 對應的 微控制器的引腳

測試影像

觸控回應

線路原型

Raspberry Pi Pico MSP2807
5V VCC
GND GND
GP20 T_CS
GP19 T_DIN
GP18 T_CLK
GP16 T_OUT

實際線路

再到 Manage Library 搜尋 xpt2046
(在下使用由 Paul Stoffregen 提供的 XPT2046 Touchscreen)

到 File > Examples > XPT2046_Touchscreen > TouchTest

將 範例 的 CS_PIN 引腳分別修改成 T_SS 對應的 微控制器的引腳
(暫時不使用 TIRQ_PIN 設定)

測試觸控
觸控操作除了能夠偵測相對的 X軸Y軸 ,還能夠偵測 Z軸(按壓)

線路原型

Raspberry Pi Pico MSP2807
5V VCC
GND GND
GP20 T_CS
GP19 T_DIN
GP18 T_CLK
GP17 T_IRQ
GP16 T_OUT

實際線路

T_IRQ 監察觸控熒幕的狀態,當 觸控熒幕 被按壓 , T_IRQ 會接地
因此可以使用 中斷(Interrupt) 讓 微控制器 監察 T_IRQ 的狀態來執行當 觸控熒幕 被按壓 或 未被按壓 的操作

線路原型

Raspberry Pi Pico MSP2807
5V VCC, LED
GND GND
3V3 RESET
GP22 D/C
GP21 CS
GP20 T_CS
GP19 SDI(MOSI), T_DIN
GP18 SCK, T_SCK
GP17 T_IRQ
GP16 SDO(MISO), T_OUT

因為 ILI9341 及 XPT2046 都是使用 SPI ,所以能共用相同的 SCK, MOSI, MISO 線路
不過 SS引腳 則需要分開連接,讓 主機 能獨立控制 ILI9341 及 XPT2046
因此 初始化 2塊晶片 前,需要先將 L_SS 及 T_SS 設定為 高電壓 ,確保 2塊晶片 都未被選取

實際線路

最終測試效果

電容式觸控熒幕

外觀

在下還測試另一款電容式的觸控熒幕,解像度同樣是 240x320,型號為 MSP2834

型號為 MSP2834 的 3.2寸 TFT 電容式觸控熒幕模組 的正面

MSP2834 的背面

引腳
編號 引腳 方向 功能
1 VCC 電源,接受 3.3V 至 5V
2 GND 接地
3 LCD_CS 輸入 LCD 的 CS引腳,低電壓時選取
4 LCD_RST 輸入 LCD 的 RST引腳,從高電壓到低電壓並保持10微秒後回到高電壓,執行重設
5 LCD_RS 輸入 LCD 的 RS引腳,高電壓時寫入資料,低高電時寫入指令
6 MOSI 輸入 LCD 及 SD 的 MOSI引腳,寫入到暫存器的資料
7 SCK 輸入 LCD 及 SD 的 SCK引腳,時脈從高到低時將資料寫入及讀取
8 LED 輸入 LCD 的 LED引腳,高電壓時高著背光燈
9 MISO 輸出 LCD 及 SD 的 MISO引腳,從暫存器讀寫的資料
10 CTP_SCL 輸入 Touch 的 SCL引腳,時脈從高到低時將資料寫入及讀取
11 CTP_RST 輸入 Touch 的 RST引腳,從高電壓到低電壓並保持10微秒後回到高電壓,執行重設
12 CTP_SDA 輸入 Touch 的 SDA引腳,寫入到暫存器的資料
13 CTP_INT 輸出 Touch 的 INT引腳,中斷訊號並讓裝置資料
14 SD_CS 輸入 SD 的 CS引腳,低電壓時選取

MSP2834 的引腳分佈
需要留意的是 觸控的協定不是 SPI 而是 I2C

MSP2834 還提供 FCP-14 連接器,可以讓 MSP2834 使用 FCP-14 柔性線路 連接到其他裝置

控制晶片

LVC245A晶片,是 74245系列 之一
接收SPI訊號 來 控制熒幕

MSP2834 的 觸控晶片 使用 柔性電路板 連接到 MSP2834 的印刷電路板

柔性電路板 焊接著 FT6336U晶片,常用於 電容式觸控熒幕,使用 I2C 協定

SD卡控制

MSP2834 同樣提供 SD卡槽 讓 MSP2834 能讀取 SD卡 資料,滅省由微控制器儲存圖像資料的空間
但 MSP2834 使用 Micro SD卡 不是 標準SD卡

觸控回應

由於能夠使用同樣的函式庫控制熒幕,因此在下省卻熒幕的測試,直接測試觸控

線路原型

Raspberry Pi Pico MSP2834
5V VCC, LED
GND GND
3V3 LCD_RST
GP26 CTP_INT
GP22 LCD_RS
GP21 LCD_CS
GP20 CTP_RST
GP19 MOSI
GP18 SCK
GP17 CTP_SCL
GP16 CTP_SDA

由於寫入影像到熒幕的訊號只需要使用 SPI 的 LCD_CS引腳 、 SCK引腳 、 MOSI引腳 ,不需要讀取資料,因此可以省卻連接 MISO引腳
不過如果需要讀取 Micro SD卡 的資料,便需要連接 MOSI引腳 及 SD_CS引腳

實際線路

最終測試效果

補充資料

SD 卡

如果閣下打算將比較細緻圖片在熒幕上顯示,將 圖片 保存到 SD卡
並使用 微控制器 讀取 SD卡,則可以節省 微控制器 的儲存空間
不過由於 SD卡模組 的操作方法同樣能夠使用 SPI 來讀寫資料,因此在下不在此測試

熒幕與觸控

Adafruit Ili9341 及 XPT2046 Touchscreen 同樣提供 setRotation 的功能來調整熒幕的方向
令使用者不需要計算圖案的在旋轉後的內容位置
但兩者的 setRotation 的旋轉方向有些分別

Pins Screen 0,0 240,320 4096,4096

ili9341.setRotation(0) 及 xpt2046.setRotation(2) 的情況

Pins Screen 0,0 320,240 4096,4096

ili9341.setRotation(1) 及 xpt2046.setRotation(3) 的情況

Pins Screen 0,0 240,320 4096,4096

ili9341.setRotation(2) 及 xpt2046.setRotation(0) 的情況

Pins Screen 0,0 320,240 4096,4096

ili9341.setRotation(3) 及 xpt2046.setRotation(1) 的情況

由於 MSP2807 的觸控熒幕 由於使用 電阻式觸控(Resistive Touch) 技術,因此只能回應 1個觸控點
如果超過 1個觸控點 在熒幕上按壓,偵測到的 X軸 、 Y軸 、 Z軸 的資料會錯誤

在下製作一個簡單的資料表來比較 3軸 的數值

最小值 最大值
X 111 3910
Y 231 3872
Z 400 2550

ILI9341 及 XPT2046 的旋轉設定偏差 2

雖然資料指 XPT2046 擁有 12位元 的深度,不過在下實際測試時,3軸 都無法達到 4095
當中 Z軸 表示 按壓力度,在下測試時使用觸控筆,大致上沒有用力按壓,只是讓地心吸力自然按壓
由於 按壓力度越大, Z軸的數值便越大,但在下避免損受模組,因此不敢太用力按壓
而在下亦測試 平均按壓力度 大約是 1154 (1024個樣本) ,因此如果想避免觸控操作太敏感,可以參考平均值

另外 FT6336U 的函式庫則沒有提供旋轉設定,因此必須自行計算觸控範圍

Pins Screen 0,0 240,320

對應方向與 ili9341.setRotation(0) 相同
如果將熒幕上的圖案旋轉成與 ili9341.setRotation(0) 相同的方向,便可以省卻自行計算觸控制位置

除了無法旋轉, FT6336U 亦沒有壓力感應
而在 FT6336U 的函式庫運用上,不是傳回位元資料,而是已經將資料轉換成對應位置的資料
這種設計,好處是使用者不需要自行轉換資料便能夠直接使用
但壞處是當觸控熒幕解像度超過 240x320 便無法傳回正確的位置資料

另外 FT6336U 的電容式觸控制熒幕能夠偵測同時最多2個觸控位置

由於測試的程式碼都是能夠直接使用範本修改,便可以使用
不過要設定觸控範圍是比較麻煩的編程內容,因為需要規劃不同觸控範圍的操作

Column 1
(80 px)
Column 2
(80 px)
Column 3
(80 px)
Row 1
(80 px)
Row 1
Column 1
Row 1
Column 2
Row 1
Column 3
Row 2
(80 px)
Row 2
Column 1
Row 2
Column 2
Row 2
Column 3
Row 3
(80 px)
Row 3
Column 1
Row 3
Column 2
Row 3
Column 3
Row 4
(80 px)
Row 4
Column 1
Row 4
Column 2
Row 4
Column 3
if (0 <= x && x < 80 && 0 <= y && y < 80) {
	// row 1 column 1 action
} else if (80 <= x && x < 160 && 0 <= y && y < 80) {
	// row 1 column 2 action
} else if (160 <= x && x < 240 && 0 <= y && y < 80) {
	// row 1 column 3 action
} else if (0 <= x && x < 80 && 80 <= y && y < 160) {
	// row 2 column 1 action
} else if (80 <= x && x < 160 && 80 <= y && y < 160) {
	// row 2 column 2 action
} else if (160 <= x && x < 240 && 80 <= y && y < 160) {
	// row 2 column 3 action
} else if (0 <= x && x < 80 && 160 <= y && y < 240) {
	// row 3 column 1 action
} else if (80 <= x && x < 160 && 160 <= y && y < 240) {
	// row 3 column 2 action
} else if (160 <= x && x < 240 && 160 <= y && y < 240) {
	// row 3 column 3 action
} else if (0 <= x && x < 80 && 240 <= y && y < 320) {
	// row 4 column 1 action
} else if (80 <= x && x < 160 && 240 <= y && y < 320) {
	// row 4 column 2 action
} else if (160 <= x && x < 240 && 240 <= y && y < 320) {
	// row 4 column 3 action
}

例如要製作上述觸控範圍,假設 闊240像素 及 高320像素
每個觸控範圍都是 闊80像素 及 高80像素,分割成 3列 4欄 ,便需要自訂觸控範圍以觸發觸控操作
程式碼中的 x 及 y 分別是 觸控 的 x軸 及 y軸 位置

I2C 地址
#include <Wire.h>
void setup() {
	pinMode(13, OUTPUT);
	Serial.begin(115200);
	Wire.begin();
	digitalWrite(13, HIGH);
	delay(1000);
	for (byte i = 0; i < 128; i++) {
		Wire.beginTransmission(i);
		byte code = Wire.endTransmission();
		if (code == 0) {
			Serial.print("0x");
			if (i < 16) {
				Serial.print("0");
			}
			Serial.print(i, HEX);
			Serial.println();
		}
	}
	digitalWrite(13, LOW);
}
void loop() {
	delay(1000);
}

由於一些使用 I2C 協定的裝置未必有提供 I2C 地址
因此在下製作了一個能偵測已連接的 I2C 裝置的地址的 Sketch

在下使用另一款 I2C 熒幕測試,地址分別為 0x3C 及 0x3D

微控制器會將偵測到的 I2C 地址 顯示在序列輸出
輸出結果與裝置設定的 I2C 地址 相同

測試 MSP2834 便能夠獲取 I2C 地址

總結

在下曾經閱讀過一篇文章,提到 SpaceX 的控制裝置設計採用了觸控熒幕,取代了大量的按鈕、旋鈕及類比操作
這種設計不僅可以降低開發成本,還能加快開發進度和縮短檢查時間
不過,對於涉及安全的操作,仍然使用傳統的控制方式

在這次測試中,在下確實減少了大量的佈線問題
例如如果在下需要製作一個具備熒幕顯示結果並且有 12個功能 的裝置
僅僅是 12個按鈕 就需要 12個GPIO,即使使用矩陣方式佈線,最少仍需 7個GPIO
如果需要增加按鈕,則需額外增加 GPIO 的使用量

然而,觸控熒幕使用序列訊號協定,只需要 4個GPIO(SPI) 甚至 2個GPIO(I2C) 就能完成操作
此外,觸控範圍和數量可以根據需求在更新韌體時進行調整,無需因為增加功能而重新添置按鈕或佈線
即使需要接上 RST引腳 及 INT引腳,最多也只需 6個GPIO

如果觸控熒幕損壞,更換整個觸控模組將會更迅速方便
雖然按鈕的價格相對較低,但在 SpaceX 執行太空任務時發生事故的情況下
能夠即時更換整個觸控模組反而會更快、更安全

但在下仍然覺得按鈕、旋鈕及類比的操作方式有存在的必要
例子某些電子遊戲、手術儀器等需要非常準確的操作要求,觸控操作反而會非常不方便
另外還有如果沒有聲音輔助,盲人是無法使用觸控熒幕操作

參考資料

沒有留言 :

張貼留言