Play Station 2 Controller - Dual Shock 2 ======================================== 兼容 Sony PlayStation2 的遙控手柄。 通訊協定被破解後,被廣泛使用在其他需要遙控的應用上。 ## Overview 分為**手把**與**接收器**。 接收器連接主機(可抽換為其他控制器),接收來自手把的訊號,亦可發送命令至手把選擇模式。 - **手把 :** ![](https://i.imgur.com/BU7vjBJ.png =70%x) - **接收器 :** ![](https://i.imgur.com/xf2fhFW.png =80%x) ## Pin Figuration 通訊協定類似 SPI (Serial Peripheral Interface)。 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | |DI/DAT |DO/CMD |NC |GND |VDD |CS/SEL |CLK |NC |ACK | - **DI/DAT** : Master in Slave out. 單次送收 8bits,CS下降緣時發送。 - **DO/CMD** : Master out Slave in. 單次送收 8bits,CS下降緣時發送。 - **NC** : No Connection. ( 震動馬達電源 : 7.2~9V (?) ) - **GND** : 電源地。 - **VDD** : 工作電壓 3~5 V 。 - **CS/SEL** : Chip Select. 通訊期間維持**低電壓**。 - **CLK** : Clock Signal. 由主機發送。 - **NC** : No Connection. - **ACK** : Acknowledgement. 軟體設定不使用。 :::info (*註 : ACK在說明書解釋為 : 从手柄到主机的应答信号。此信号在每个 8bits 数据发送的最后一个周期变低并且 CS 一直保持低电平,如果 CS 信号不变低,约 60 微秒 PS 主机会试另一个外设。在编程时未使用 ACK 端口。) ::: ## 時序 CLK 頻率 **250kHz**,若通訊不穩,可適當增加頻率。 先送 LSB 。 ![](https://i.imgur.com/J3u6I05.png =80%x) - - - 一個通訊週期 **9 Bytes**,如下表依序傳送。 ![](https://i.imgur.com/ywOtNOb.png =80%x) - 主機讀取手把資料時: 1. 主機先拉低CS腳並發送"0x01"(CMD)。 2. 手把回復ID "0x41"(綠燈模式) 或 "0x73"(紅燈模式)。 3. 主機發送 "0x42" 請求數據。 4. 手把發送 "0x5A" 確認狀態。 5. 開始資料段如上圖。 當該按鍵被按下,其對應位置為 "0",其他為 "1" eg. LEFT 被按下時 Data[3] = 0b01111111. (*註 : idle 為閒置狀態) - 綠燈模式 : 左右搖桿回傳類比值(0x00 ~ 0xFF)。 L3、R3有效。 - 紅燈模式 : 左右搖桿推至極限時回傳 UP、RIGHT、DOWN、LEFT,△、〇、X、□。 L3、R3無效。 - 震動模式(未使用) : WW 位控制右邊震動馬達 : 0x00 為關,其他為開。 YY 位控制左邊震動馬達 : 0x40 ~ 0xFF 為開,數字越大震動越大。 ## 硬體使用說明 手把使用兩顆AAA電池,接收器使用主機電源(3~5V)。 接收器未配對時閃綠燈。 手把未配對時燈會不停地閃,未找到前手把的燈會不停地閃,超過一定時間會自動進入待機模式,等待 Start 鍵再次喚醒。 接收器接上電源,手把上開關撥至 ON 時會自動配對附近接收器,成功配對時,燈常亮。 成功配對後,Mode可切換綠燈模式或紅燈模式。 ## 程式範例 主機 : **ASA M128** * PA0 -> DAT (Input) * PA1 -> CMD (Output) * PA2 -> CS (Output) * PA3 -> CLK (Output) ### PS2.c ```c= /** * @file PS2.h * @author C.Y. YANG * @date 2020.1.31 * @brief PS2搖桿 * * NOTE: 硬體相依標頭檔內腳位定義與各檔案內工作頻率定義 */ /* * PA0 -> DAT (Input) * PA1 -> CMD (Output) * PA2 -> CS (Output) * PA3 -> CLK (Output) */ #include "PS2.h" #include <math.h> #include <stdio.h> #include <stdint.h> #include <avr/io.h> #include <util/delay.h> #define F_CPU 11059200UL uint8_t DO[9] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t DI[9] = {0}; void PORT_init (void) { DIR_SET(); CS_SET(); CLK_SET(); } // Full-Duplex uint8_t ByteCommunication (uint8_t cmd) { uint8_t res_buffer = 1; uint8_t res = 0; for (int i = 0; i < 8; i++) { if(cmd & 0x01) CMD_SET(); else CMD_CLR(); cmd >>= 1; _delay_us(100); CLK_CLR(); _delay_us(100); if(PINA & 0x01) res = res + res_buffer; res_buffer <<= 1; CLK_SET(); } return res; } uint8_t Read_PS2_Controller (void) { uint8_t controller_res = 0; CS_CLR(); for(int i = 0; i < 9; i++) DI[i] = ByteCommunication(DO[i]); CS_SET(); if(DI[1] == 0x41) //printf("Green Light Mode\n"); if(DI[3] == PSB_SELECT) printf("PSB_SELECT\n"); controller_res = 1; if(DI[3] == PSB_L3) printf("PSB_L3\n"); controller_res = 2; if(DI[3] == PSB_R3) printf("PSB_R3\n"); controller_res = 3; if(DI[3] == PSB_START) printf("PSB_START\n"); controller_res = 4; if(DI[3] == PSB_PAD_UP) printf("PSB_PAD_UP\n"); controller_res = 5; if(DI[3] == PSB_PAD_RIGHT) printf("PSB_PAD_RIGHT\n"); controller_res = 6; if(DI[3] == PSB_PAD_DOWN) printf("PSB_PAD_DOWN\n"); controller_res = 7; if(DI[3] == PSB_PAD_LEFT) printf("PSB_PAD_LEFT\n"); controller_res = 8; if(DI[4] == PSB_L2) printf("PSB_L2\n"); controller_res = 9; if(DI[4] == PSB_R2) printf("PSB_R2\n"); controller_res = 10; if(DI[4] == PSB_L1) printf("PSB_L1\n"); controller_res = 11; if(DI[4] == PSB_R1) printf("PSB_R1\n"); controller_res = 12; if(DI[4] == PSB_GREEN) printf("PSB_GREEN\n"); controller_res = 13; if(DI[4] == PSB_RED) printf("PSB_RED\n"); controller_res = 14; if(DI[4] == PSB_BLUE) printf("PSB_BLUE\n"); controller_res = 15; if(DI[4] == PSB_PINK) printf("PSB_PINK\n"); controller_res = 16; if(DI[1] == 0x73) { //printf("Red Light Mode\n"); if(DI[5] != 128) printf("PSS_RX : %d\n", DI[5]); if(DI[6] != 127) printf("PSS_RY : %d\n", DI[6]); if(DI[7] != 128) printf("PSS_LX : %d\n", DI[7]); if(DI[8] != 127) printf("PSS_LY : %d\n", DI[8]); } while ((DI[3] != 0xFF) || (DI[4] != 0xFF)) { CS_CLR(); for(int i = 0; i < 9; i++) DI[i] = ByteCommunication(DO[i]); CS_SET(); _delay_ms(1); } _delay_ms(10); return controller_res; } ``` ### PS2.h ```c= /** * @file PS2.h * @author C.Y. YANG * @date 2020.1.31 * @brief PS2搖桿 * * NOTE: 硬體相依標頭檔內腳位定義與各檔案內工作頻率定義 */ /* * PA0 -> DAT (Input) * PA1 -> CMD (Output) * PA2 -> CS (Output) * PA3 -> CLK (Output) */ #ifndef PS2_H_ #define PS2_H_ #include <math.h> #include <stdio.h> #include <stdint.h> #include <avr/io.h> #include <util/delay.h> #define F_CPU 11059200UL #define DIR_SET() (DDRA |= (1<<DDRA1) | \ (1<<DDRA2) | \ (1<<DDRA3)) #define CMD_SET() (PORTA |= (1<<PORTA1)) #define CMD_CLR() (PORTA &= ~(1<<PORTA1)) #define CS_SET() (PORTA |= (1<<PORTA2)) #define CS_CLR() (PORTA &= ~(1<<PORTA2)) #define CLK_SET() (PORTA |= (1<<PORTA3)) #define CLK_CLR() (PORTA &= ~(1<<PORTA3)) // PS2 Buttons #define PSB_SELECT 0b11111110 #define PSB_L3 0b11111101 #define PSB_R3 0b11111011 #define PSB_START 0b11110111 #define PSB_PAD_UP 0b11101111 #define PSB_PAD_RIGHT 0b11011111 #define PSB_PAD_DOWN 0b10111111 #define PSB_PAD_LEFT 0b01111111 #define PSB_L2 0b11111110 #define PSB_R2 0b11111101 #define PSB_L1 0b11111011 #define PSB_R1 0b11110111 #define PSB_GREEN 0b11101111 #define PSB_RED 0b11011111 #define PSB_BLUE 0b10111111 #define PSB_PINK 0b01111111 #define PSB_TRIANGLE 0b11101111 #define PSB_CIRCLE 0b11011111 #define PSB_CROSS 0b10111111 #define PSB_SQUARE 0b01111111 // PS2 Sticks // #define PSS_RX 5 // #define PSS_RY 6 // #define PSS_LX 7 // #define PSS_LY 8 void PORT_init (void); uint8_t ByteCommunication (uint8_t cmd); uint8_t Read_PS2_Controller (void); #endif /* PS2_H_ */ ``` ### main.c ```c= /* * PS2_contoller.c * * Created: 2020/1/31 * Author : C.Y. YANG * NOTE: */ /* * Play Station 2 Controller * * PA0 -> DAT (Input) * PA1 -> CMD (Output) * PA2 -> CS (Output) * PA3 -> CLK (Output) * */ #define F_CPU 11059200UL #include "c4mlib.h" #include "PS2.h" int main (void) { C4M_DEVICE_set(); uint8_t ck = 0; printf("----- Start -----\n"); PORT_init(); while (1) { ck = Read_PS2_Controller(); //printf("ck = %d\n", ck); _delay_ms(100); } return 0; } ``` ![](https://i.imgur.com/fLJFmSl.png =80%x) ![](https://i.imgur.com/rfb419D.png =80%x) ### Reference 參考智宇机器人PS2 无线遥控手柄说明书 **版权声明** 本手册版权归智宇机器人(以下简称“ZYRobot”)所有,对该手册保留一切权力, 非经 ZYRobot 授权同意(书面形式),任何单位及个人不得擅自摘录本手册部分及全部内容用于商业用途,违者将追究其法律责任。可以在网上传播,以方便更多人,但必须保证手册的完整性。 https://shopee.tw/product/139069730/2342925575 https://store.curiousinventor.com/guides/PS2 https://robosavvy.com/forum/viewtopic.php?t=2561 https://www.rhydolabz.com/wiki/?p=12663 https://zh.wikipedia.org/wiki/DualShock ###### tags: `MODULE` `ROBOT`