Play Station 2 Controller - Dual Shock 2
========================================
兼容 Sony PlayStation2 的遙控手柄。
通訊協定被破解後,被廣泛使用在其他需要遙控的應用上。
## Overview
分為**手把**與**接收器**。
接收器連接主機(可抽換為其他控制器),接收來自手把的訊號,亦可發送命令至手把選擇模式。
- **手把 :**

- **接收器 :**

## 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 。

- - -
一個通訊週期 **9 Bytes**,如下表依序傳送。

- 主機讀取手把資料時:
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;
}
```


### 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`