在下不打算購買轉接器,想自行改裝手掣,因此便開始學習了解電子訊號原理
資料搜集後,可以透迥 Arduino 獲得 PlayStation One 手掣 的訊號後,再傳送到電腦
在下購買一塊 Arduino UNO Rev 3 開始嘗試及學習了解電子工具
(一塊 Arduino UNO Rev 3 及其他配件大約 $300 左右,但一個 PlayStation 手掣 轉 USB Type-A 轉換器只需要 $30 ……)
甚麼是 Arduino 在下不在此詳細說明,可以到 Auduino官方網頁 或 Arduino @ Wiki 了解
在下的 PlayStation 手掣 ,屬於 PlayStation One 的 DualShock 手掣 版本
引腳編排
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|
(在下現在才發現在下的 PlayStation One 手掣沒有 第八引腳)
引腳功用
號碼 | 顏色 | 輸入輸出 | 功能 | 使用 |
---|---|---|---|---|
1 | 啡 | 輸出 | Data | 必須 |
2 | 橙 | 輸入 | Command | 必須 |
3 | 灰 | 7.0V ~ 9.0V 震動器 (震動手掣才有) | 選用 | |
4 | 黑 | 接地線 | 必須 | |
5 | 紅 | 3.3V 至 5.0V 電源線 | 必須 | |
6 | 黃 | 輸入 | Attention | 必須 |
7 | 藍 | 輸入 | Clock | 必須 |
8 | 白 | 保留使用 | 選用 | |
9 | 綠 | 輸出 | Acknowledge | 選用 |
- 電源線 (Power) - 接受 3.3V 至 5V 電源
- 接地線 (Ground) - 電源回路
- 資料 (Data) - 由手掣向目標發出的回應訊號,以 位元 為 單位,以 位元組 為 週期
- 指令 (Command) - 由目標向手掣發出的指令訊號,以 位元 為 單位,以 位元組 為 週期
- 關注 (Attention) - 由目標向手掣發出的控制訊號,控制 指令訊號 及 回應訊號 週期範圍
- 定時 (Clock) - 由目標向手掣發出的控制訊號,控制 發出指令訊號 及 接收回應訊號 的狀態
引腳訊號
引腳 | 初始化 | 指令訊號 及 回應訊號 週期 | ||||
---|---|---|---|---|---|---|
開始 | 位元組訊號 週期 | 完成 | 終止 | |||
指令訊號 位元 | 回應訊號 位元 | |||||
att | 高訊號 | 低訊號 | 高訊號 | |||
clk | 高訊號 | 低訊號 | 高訊號 | |||
cmd | 低訊號 | 高或低訊號 | ||||
dat | 高訊號 | 高或低訊號 | ||||
Time | 延遲16微秒 | 延遲2微秒 | 延遲2微秒 | 延遲16微秒 | 延遲16微秒 |
在連接到主機前 PlayStation 手掣 會先發出 初始化 (Initialize) 的訊號,然後便會不斷執行 指令訊號及回應訊號 (Command and Response)
執行 指令與回應 前都會先發出 開始 (Start) 訊號,然後 延遲16微秒,在完成所有 位元組訊號 後,會發出 終止 (Stop) 訊號,再 延遲16微秒
位元組訊號 中,先 發出 指令訊號位元 (Command bit) ,延遲2微秒,然後會接收到 回應訊號位元 (Response bit) ,再延遲2微秒
由於 位元組 = 8個位元 ,因此一個完整的指令需要發出 8次指令訊號位元 並接收 8次回應訊號位元
將 8次回應訊號位元 整合成 位元組,才完成整個 位元組訊號
(1微秒 = 一百萬分之1秒)
指令及回應
編號 | 指令值 | 指令 | 回應值 | 回應 |
---|---|---|---|---|
1 | 0x01 | 握手 (Handshake) | 0xFF | 連接 (Connect) |
2 | 0x?? | 操作 (Operation) | 0x?? | 手掣ID (Gamepad ID) |
3 | 0x00 | 閒置 (Idle) | 0x5A | 準備 (Ready) |
- 握手 - 0x01
- 操作 - 0x??
- 閒置 - 0x00
- 已連接 - 0xFF
- 手掣ID - 0x??
- 標準手掣ID - 0x41
- 類比手掣ID - 0x73
- 已準備 - 0x5A
操作類型
指令值 | 操作 |
---|---|
0x40 | 壓力感應按鈕 (Pressure Buttons) |
0x42 | 資料 (Data) |
0x43 | 設定 (Config) |
0x44 | 類比 (Analog) |
0x4D | 震動 (Vibration) |
0x4F | 壓力感應器 (Pressure Sensor) |
- 壓力感應按鈕 - 設定需要啟動壓力感應的按鈕
- 資料 - 回應手掣按鈕及轉軸訊號
- 設定 - 控制手掣啟用或停止類比訊號、震動效果、壓力感應時,需要先啟動設定
- 類比 - 設定啟用或停止類比訊號
- 震動 - 設定啟用或停止震動效果
- 壓力感應 - 設定啟用或停止壓力感應器
操作回應
手掣設定
編號 | 指令 | 回應 |
---|---|---|
1 | 握手 | 連接 |
2 | 設定 | 手掣ID |
3 | 閒置 | 準備 |
4 | 0x?? | 0xFF |
5 | 0x00 | 0xFF |
6 | 0x00 | 0xFF |
7 | 0x00 | 0xFF |
8 | 0x00 | 0xFF |
9 | 0x00 | 0xFF |
10 | 0x00 | 0xFF |
- 0x00 - 關閉
- 0x01 - 開啟
類比設定
編號 | 指令 | 回應 |
---|---|---|
1 | 握手 | 連接 |
2 | 類比 | 手掣ID |
3 | 閒置 | 準備 |
4 | 0x?? | 0xFF |
5 | 0x?? | 0xFF |
6 | 0x00 | 0xFF |
7 | 0x00 | 0xFF |
8 | 0x00 | 0xFF |
9 | 0x00 | 0xFF |
10 | 0x00 | 0xFF |
- 0x00 - 關閉
- 0x01 - 開啟
- 0x00 - 不鎖定
- 0x03 - 鎖定
震動設定
編號 | 指令 | 回應 |
---|---|---|
1 | 握手 | 連接 |
2 | 震動 | 手掣ID |
3 | 閒置 | 準備 |
4 | 0x?? | 0xFF |
5 | 0x?? | 0xFF |
6 | 0xFF | 0xFF |
7 | 0xFF | 0xFF |
8 | 0xFF | 0xFF |
9 | 0xFF | 0xFF |
10 | 0xFF | 0xFF |
- 0xFF - 關閉
- 0x00 - 開啟
- 0xFF - 關閉
- 0x01 - 開啟
壓力感應按鈕設定
編號 | 指令 | 回應 |
---|---|---|
1 | 握手 | 連接 |
2 | 壓力感應按鈕 | 手掣ID |
3 | 閒置 | 準備 |
4 | 0x?? | 0xFF |
5 | 0x?? | 0xFF |
6 | 0x00 | 0xFF |
7 | 0x00 | 0xFF |
8 | 0x00 | 0xFF |
9 | 0x00 | 0xFF |
10 | 0x00 | 0xFF |
- 0x00 - 右按鈕
- 0x01 - 左按鈕
- 0x02 - 上按鈕
- 0x03 - 下按鈕
- 0x04 - 三角按鈕
- 0x05 - 圓圈按鈕
- 0x06 - 交叉按鈕
- 0x07 - 正方按鈕
- 0x08 - L1按鈕
- 0x09 - R1按鈕
- 0x0A - L2按鈕
- 0x0B - R2按鈕
- 0x00 - 關閉
- 0x02 - 開啟
壓力感應設定
編號 | 指令 | 回應 |
---|---|---|
1 | 握手 | 連接 |
2 | 壓力感應器 | 手掣ID |
3 | 閒置 | 準備 |
4 | 0xFF | 0xFF |
5 | 0xFF | 0xFF |
6 | 0x?? | 0xFF |
7 | 0x00 | 0xFF |
8 | 0x00 | 0xFF |
9 | 0x00 | 0xFF |
10 | 0x00 | 0xFF |
- 0x00 - 關閉
- 0x03 - 開啟
手掣資料
編號 | 指令 | 回應 | |||||||
---|---|---|---|---|---|---|---|---|---|
第0位元 | 第1位元 | 第2位元 | 第3位元 | 第4位元 | 第5位元 | 第6位元 | 第7位元 | ||
1 | 握手 | 連接 | |||||||
2 | 資料 | 手掣ID | |||||||
3 | 閒置 | 準備 | |||||||
4 | 閒置 | Select | L3 | R3 | Start | 上 | 右 | 下 | 左 |
5 | 閒置 | L2 | R2 | L1 | R1 | 三角 | 圓圈 | 交叉 | 正方 |
6 | 閒置 | 右類比X轉軸數值,0x00 為最左 0xFF 為最右 | |||||||
7 | 閒置 | 右類比Y轉軸數值,0x00 為最上 0xFF 為最下 | |||||||
8 | 閒置 | 左類比X轉軸數值,0x00 為最左 0xFF 為最右 | |||||||
9 | 閒置 | 左類比Y轉軸數值,0x00 為最上 0xFF 為最下 | |||||||
10 | 閒置 | 右按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
11 | 閒置 | 左按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
12 | 閒置 | 上按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
13 | 閒置 | 下按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
14 | 閒置 | 三角按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
15 | 閒置 | 圓圈按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
16 | 閒置 | 交叉按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
17 | 閒置 | 正方按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
18 | 閒置 | L1按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
19 | 閒置 | R1按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
20 | 閒置 | L2按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 | |||||||
21 | 閒置 | R2按鈕壓力數值,0x00 為沒有按下 0xFF 為完全按下 |
標準手掣資料
以 原版 PlayStation 手掣 為例,完成 發出及接收3次位元組訊號 後,第4次及第5次位元組訊號 就是手掣按鈕的回應訊號位元組訊號 是 十進制 0 至 255 (十六進制 0x00 至 0xFF) 之間的數值,以 二進制顯示 就是 00000000 至 11111111
每個二進制位值便是相對特定按鈕有否按下的狀態,沒有按下為 1 ,按下為 0
以 第4次位元組訊號 為例
- Select 為 第0位元
- L3 為 第1位元
- R3 為 第2位元
- Start 為 第3位元
- 上 為 第4位元
- 右 為 第5位元
- 下 為 第6位元
- 左 為 第7位元
如果同時按下 Start 及 Select ,回應的位元組訊號便會是 11110110 (二進制) 或 246 (十進制) 或 0xF6 (十六進制)
展示效果:
11110110 |||||||| |||||||>- Select (按下) ||||||>-- L3 (沒有按下) |||||>--- R3 (沒有按下) ||||>---- Start (按下) |||>----- 上 (沒有按下) ||>------ 右 (沒有按下) |>------- 下 (沒有按下) >-------- 左 (沒有按下)第5次位元組訊號 都是以相同方法偵測按鈕有否按下的狀態
類比手掣資料
後期推出的 PlayStation 手掣 包含 L3 、 R3 、 左右類比操作,增加至 第6次至第9次位元組訊號
- 第6次位元組訊號 指 右類比 的 X軸數值資料,越偏向左數值越接近 0 ,越偏向右數值越接近 255
- 第7次位元組訊號 指 右類比 的 Y軸數值資料,越偏向上數值越接近 0 ,越偏向下數值越接近 255
- 第8次位元組訊號 指 左類比 的 X軸數值資料,越偏向左數值越接近 0 ,越偏向右數值越接近 255
- 第9次位元組訊號 指 左類比 的 Y軸數值資料,越偏向上數值越接近 0 ,越偏向下數值越接近 255
如果沒有推動右類比,讓右類比置中,數值大約在 127 、 128 左右 (器材老化可能會有偏差,在下使用的手掣偏差值最大達到 25)
將右類比推向 最左會回應 0 ,最右會回應 255
第7次至第9次位元組訊號 都是以相同方法偵測其他轉軸數值
如果 以標準手掣測試類比轉軸數值,必定傳回 255
壓力感應手掣資料
再後期推出 PlayStation2 DualShock2 手掣還具備壓力感應按鈕,能感應按下的力度
第10次至第21次位元組訊 都是壓力感應的數值,沒有壓力感應的手掣,都會回應 255
啟動 類比操作 才能啟動 壓力感應器
- 第10次位元組訊號 指 右按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第11次位元組訊號 指 左按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第12次位元組訊號 指 上按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第13次位元組訊號 指 下按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第14次位元組訊號 指 三角按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第15次位元組訊號 指 圓圈按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第16次位元組訊號 指 交叉按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第17次位元組訊號 指 正方按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第18次位元組訊號 指 L1按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第19次位元組訊號 指 R1按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第20次位元組訊號 指 L2按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
- 第21次位元組訊號 指 R2按鈕 的 壓力數值資料,沒有按下為 0 ,完全按下為 255
硬件連接
使用 跳線 (Jump Wire) 將 PlayStation 手掣引腳 接上
除了 Arduino 數碼引腳 (Digital Pin) 0, 1 外,數碼引腳 2 至 13 及 類比引腳 (Analog Pin) A0 至 A5 都能夠使用
PlayStation 手掣引腳 | Arduino引腳 |
---|---|
1 | 2 |
2 | 3 |
4 | GND |
5 | 3.3V |
6 | 4 |
7 | 5 |
(選用的引腳可以不連接)
硬件設置後,開始偏寫軟件編寫程式碼
Arduino IDE
使用由 Arduino 官方網站提供的 Arduino IDE 編程
可以到 https://www.arduino.cc/en/Main/Software 下載
在正式編寫程式碼前先設置正在使用的 Arduino型號 及 Arduino連接埠
到 Tools > Board 選擇 Arduino型號
到 Tools > Port 選擇 Arduino連接埠
使用 Linux 的話,需要將使用者增加至 dialout 的群組中,讓使用者能存取 序列 (Serial) 操作
到 使用者及群組 按 管理群組
選擇 dialout 群組後,按 屬性 (Properties)
群組成員 增加使用者到 dialout
* 修改系統資料需要使用 root 或 super user 帳戶 權限
使用指令亦可,在 Terminal 輸入
sudo usermod -a -G "dialout" "${USER}"
程式碼
先在全域區域宣告需要使用哪些引腳,方便追蹤程式碼及修改
const byte DAT_PIN = 2; const byte CMD_PIN = 3; const byte ATT_PIN = 4; const byte CLK_PIN = 5;
在 void setup() { } 中設定
讓 Arduino 通過 序列埠 (Serial Port) 以 115200 Baud 傳送資料
(可以選擇其他常用 Baud ,例如 9600, 19200, 38400 亦可)
根據 PlayStation 手掣引腳 的輸入輸出模式,設定 Arduino引腳 的輸入輸出模式
及 初始化 引腳訊號
Serial.begin(115200); pinMode(DAT_PIN, INPUT); pinMode(CMD_PIN, OUTPUT); pinMode(ATT_PIN, OUTPUT); pinMode(CLK_PIN, OUTPUT); digitalWrite(DAT_PIN, HIGH); digitalWrite(CMD_PIN, LOW); digitalWrite(ATT_PIN, HIGH); digitalWrite(CLK_PIN, HIGH);
根據位元組訊號編寫發出指令的功能方便重覆調用
const byte BIT_DELAY = 2; const byte BYTE_DELAY = BIT_DELAY * 8; byte sendCommand(byte command) { byte response = 0; for (byte i = 0; i < 8; i++) { const byte SHIFTED = 1 << i; digitalWrite(CLK_PIN, LOW); if ((command & SHIFTED) > 0) { digitalWrite(CMD_PIN, HIGH); } else { digitalWrite(CMD_PIN, LOW); } delayMicroseconds(BIT_DELAY); digitalWrite(CLK_PIN, HIGH); if (digitalRead(DAT_PIN) > LOW) { response |= SHIFTED; } delayMicroseconds(BIT_DELAY); } delayMicroseconds(BYTE_DELAY); return response; }
在 void loop() { } 編寫程式讓 Arduino 不斷執行操作
digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x42); sendCommand(0x00); const byte BUTTON_SET_1 = sendCommand(0x00); const byte BUTTON_SET_2 = sendCommand(0x00); const byte RIGHT_X = sendCommand(0x00); const byte RIGHT_Y = sendCommand(0x00); const byte LEFT_X = sendCommand(0x00); const byte LEFT_Y = sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY); char message[68] = ""; sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x01) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x02) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x04) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x08) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x10) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x20) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x40) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_1 & 0x80) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x01) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x02) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x04) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x08) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x10) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x20) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x40) == 0); sprintf(message, "%s%d, ", message, (BUTTON_SET_2 & 0x80) == 0); sprintf(message, "%s%03d, ", message, LEFT_X); sprintf(message, "%s%03d, ", message, LEFT_Y); sprintf(message, "%s%03d, ", message, RIGHT_X); sprintf(message, "%s%03d, ", message, RIGHT_Y); Serial.println(message); delay(16);
上載程式碼到 Arduino
到 Sketch ,按 Upload ,將程式碼上載到 Arduino
顯示 avrdude done. Thank you. 表示已經上載到 Arduino
到 Tools ,按 Monitor 監察序列
選擇與 Serial.begin 相同的 Buad 數值後,便可以正常顯示序列的內容
影片展示按下手掣按鈕及類比時,通常 Arduino 監察序列的功能讀取資料
// 開啟設定 digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x43); sendCommand(0x00); sendCommand(0x01); // 開啟設定指令 sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY); // 開啟類比 digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x44); sendCommand(0x00); sendCommand(0x01); // 類比狀態, 0x00 為 關閉, 0x01 為開啟 sendCommand(0x03); // 狀態鎖定, 0x00 為 不鎖定, 0x03 為鎖定 sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY); // 關閉設定 digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x43); sendCommand(0x00); sendCommand(0x00); // 關閉設定指令 sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY);
控制類比開關及鎖定
影片展示發出不同類比指令,啟動但不鎖定類比、閉關但不鎖定類比、開啟並鎖定類比 (在下沒有在影片展示閉關並鎖定類比)
// 加入需要壓力感應的按鈕 for (byte i = 0; i < 12; i++) { digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x40); sendCommand(0x00); sendCommand(i); // 啟動需要壓力感應的按鈕 sendCommand(0x02); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY); } // 啟動壓力感應器 digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x4F); sendCommand(0x00); sendCommand(0xFF); sendCommand(0xFF); sendCommand(0x03); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); sendCommand(0x00); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY);
啟動壓力感應器的設定
影片展示12個具備壓力感應的按鈕按下時的數值資料
在下為了測試震動器,購買了 9V電池接駁器 及 9V電池
由於希望 不損壞接駁器 ,因此將 保護外彀卸下,露出金屬零件
使用鱷魚夾將接駁器的正負極接上
通常紅色線路為正極,黑色線路為負極
鱷魚夾的顏色只是方便展示正負極 ,若閣下沒有對色的鱷魚夾並沒有影響
若沒有鱷魚夾,使用萬字夾亦可,但要注意附近不要有易燃物或液體
連接鱷魚夾及跳線
注意:正極使用插頭插座跳線,負極使用雙插頭跳線
正極跳線連接到手掣第3引腳
負極跳線連接到 Arduino 另一個地接線引腳
// 啟動震動器 digitalWrite(ATT_PIN, LOW); delayMicroseconds(BYTE_DELAY); sendCommand(0x01); sendCommand(0x4D); sendCommand(0x00); // 小震動器設定,0xFF 為關閉, 0x00 為啟動 sendCommand(0x01); // 大震動器設定,0xFF 為關閉, 0x01 為啟動 sendCommand(0xFF); sendCommand(0xFF); sendCommand(0xFF); sendCommand(0xFF); sendCommand(0xFF); sendCommand(0xFF); digitalWrite(ATT_PIN, HIGH); delayMicroseconds(BYTE_DELAY); /* 其他程式碼 */ sendCommand(0x01); sendCommand(0x42); sendCommand(0x00); sendCommand(0xFF); // 啟用小震動器 sendCommand(0x40); // 以 0x40 啟用大震動器 /* 其他程式碼 */
完成程式碼並上載到 Arduino 前,記得還要接上 9V電池
啟動震動器設定
影片展示小震動器及大震動器的驅動情況
小震動器 只能控制開啟或關閉 的設定
大震動器 能夠控制震動程度,範圍由 0x40 至 0xFF ,小於 0x40 則不會震動
注意:震動器放在硬質平台上啟用會非常噪吵,建議用布之類減震物品墊底以減低噪音
額外資料
使用 9V電池 能夠驅動 震動器後,在下嘗試使用 Arduino 提供的 5V 電源
5V 比 9V 低,測試失敗只是無法驅動震動器,並不是損壞震動器或 Arduino
因此直接將 手掣第3引腳 接駁到 Arduino 5V引腳
結果震動器能正常啟用,即是無需額外電源,只使用 Arduino 的電源已經足夠啟用手掣的震動器
在下將這個專案上載到 https://bitbucket.org/hkgoldenmra/playstationgamepad
及 https://create.arduino.cc/editor/hkgoldenmra/f3402f94-7eaf-443a-916c-19708f795aba/preview
有興趣可以透過 git
git clone "https://bitbucket.org/hkgoldenmra/playstationgamepad.git" --depth=1下載、使用甚至改良
感想
在下最初都只是借解析 PlayStation 手掣為由,購買一塊 Arduino 嘗試接觸及學習電子知識
便開始尋找 Arduino 的資料,觀看在 Youtube 由 Arduino 創作人之一 Massimo Banzi 的 Arduino 入門教學
本來只係學習接駁電路,按 按鈕 後令 LED燈 發光,已經覺得很有趣,然後便學習 Arduino 的 AVR-C/C/C++ 的程式語言
之後便開始接駁電路到手掣,發覺完全不知如何整理,不懂看電路圖,不懂別人的 Arduino 函式庫,不懂手掣運作原理
抄別人的函式庫,跟著說明接駁引腳,但不知道有甚麼錯誤,網上尋找到的範例都是使用這個函式庫,但在下卻不能運作
再找到另一個函式庫,終於能夠運作,便慢慢由零開始理解函式庫內容
而註解資料太少,程式碼都是看不懂,最後還是從原始的電子訊號及其他手掣逆向工程文件開始理解
但文件內容仍然有很多不明確,例如網上有人指出使用 6V便可以驅動震動器,但又有人指出使用 9V會損壞震動器
最後在下還是使用鱷魚夾,直接將 9V電池 及 震動器 接駁並測試,幸好沒有損壞,最後在下發現使用 Arduino 的 5V 也可以驅動
現今有太多不明確不肯定的資訊,但可能全是錯誤,如果放棄思考,便會被誤導
需要由自己親身查證,不斷嘗試,才能找到答案
參考資料
- https://playground.arduino.cc/Main/PSXLibrary (在下的函式庫由此函式庫改良而來)
- https://github.com/madsci1016/Arduino-PS2X/tree/master/PS2X_lib (網上最多人建議使用的函式庫,有興趣可以嘗試)
- https://gamesx.com/controldata/psxcont/psxcont.htm
- http://kaele.com/~kashima/games/ps_jpn.txt (需要將文字編碼改為 Shift_JIS)
- http://store.curiousinventor.com/guides/PS2
- http://www.wegmuller.org/arduino/Arduino-Playstation_gameport.html
- https://gist.github.com/scanlime/5042071
- https://github.com/AZO234/Arduino_PSXPad
沒有留言 :
張貼留言