PS2游戏机手柄资料

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

最近在开发一款PS2游戏机产品,学到了很多东西,也遇到很多问题,在这里把自己认为重要的东西记下来,作为以后的备忘,如果你在这篇文章中学到哪怕一点点东西,我都将会感到很荣幸。

关于PS2游戏机更详细的文章,应该算是Adam Chapweske著Roy Show译的“PS2技术参考”,其实开发一款PS2产品,兼容性是很重要的,在我们开发的这款PS产品中就出现过这样的问题,我们测试了几十台PC都是没问题的,可是偏偏就在一台HP的PC上不能识别,找了很久才发现问题所在。

设备到主机的通信,当设备要发送数据给主机时,只要检测到总线有空,就可以发了,这里简单给出波形图,更详细的请参考“PS2技术参考“。

(原文件名:d to H.JPG)
下面是主机到设备通信的简单波形图:
使用示波器抓下来的主机跟设备通信的实际波形:
(原文件名:PS2KB_WF01.JPG)
主机到设备的通信,当主机要发送数据给设备时,大部分PC都会先拉低时钟线至少100us 来抑制通信,然后才开始产生数据发送请求状态,但是也有部分PC不会拉低时钟来抑制通信,而是直接产生数据发送请求状态,所以在检测主机有没有数据发送的时候直接检测有没有数据发送请求是比较好的,一般情况下,当PC要发送数据给设备时,会重试三次,每次10ms超时,如果在重试三次设备都没有响应,那就比较危险了,如果是要发送关健的数据,可能会直接导致设备不能识别的情况
对于PS2键盘,必须要回复的命令如下:
1,主机命令设备复位(0xff),设备必须应答0xfa后回复0xaa;
2,主机请求获得设备ID命令(0xf2),设备必须应答0xfa后回复0xab,0x83;
3,主机ECHO命令(0xee),设备直接回复0xee;
除以上命令之外的其它命令,设备只要回复0xfa即可,当然要视设备的功能作相应的动作。

对于PS2鼠标,要做的事情比较多,而且比键盘更容易不被PC识别,一个简单的PS2鼠标,必须支持STREAM模式及WRAP模式,STREAM为鼠标的主要功能,但是有些PC刚开机的时候会命令鼠标进入WRAP模式,否则就不识别此设备,还有一点比较重要的就是不要在PC发送使能鼠标(命令0xf4)之前发送移动及按键数据,而且一旦收到禁止数据命令(0xf5)后立即停止发送移动及按键数据,直到主机再次发送使能命令之后。

否则会出现在有些PC 中虽然鼠标能被识别,而且在Windows也能移动,但是快速移动几秒钟之后鼠标就死了,使用示波器量数据线,发现数据还在传输,实际上在这种情况下,主机并没有拉低时钟来禁止鼠标,而是认为这个设备是一个疯狗,不会理会它发送的任何数据。

除了要支持以上二种模式之外,必须回复的命令如下:
1.主机命令设备复位命令(0xff),设备必须应答0xfa后回复0xaa,0x00
2.主机请求获得设备ID命令(0xf2),设备必须应答0xfa后回复ID:0x03(适用于标准的3D鼠标);
3.主机请求状态命令(0xe8),设备回复:0xfa,0x00,0x02,0x64;
PS鼠标使用如下的坐标系,这跟USB鼠标使用的坐标系是不同的:
(原文件名:坐标.JPG)
不管是键盘还是鼠标,在整个Windows启动的过程中会被检测二次,第一次是BIOS检测,第二次是Windows检测,如果第一次未通过,则第二次不会再检测,而且二次检测都必须通过再能使用,有趣的是有些PC在BIOS检测之后,Windows启动之前再插入键盘,还是可以使用的,但是Windows启动之后就会拉低时钟来禁止设备,
附件是实际的波形及部份代码
PS2实际波形ourdev_546437.rar(文件大小:65K) (原文件名:PS2 Waveform.rar)
PS2键盘鼠标例程ourdev_546438.rar(文件大小:4K) (原文件名:PS2KM_CODE.rar)
PS.C
1.#include <STDIO.H>
2.#include <REG922.H>
3.#include <INTRINS.H>
4.#define uchar unsigned char
5.#define uint unsigned int
6.
7.uchar code dout[5]={0x01,0x42,0x00,0x00,0x00};
8.uchar data din[5]={0x00,0x00,0x00,0x00,0x00};
9.uint code hos_tabup[16]={0xE31B,0xF800,0xF50D,0xFF07,0xDE26,0x0000,0x0000,0x
DF27, //主手柄释放键表
10. 0x877F,0xB941,0xBA42,0x8870,0xDC24,0xA159,0xD820,0xA058};
11.uint code hos_tabdn[16]={0x639B,0x7880,0x758D,0x7F87,0x5EA6,0x0000,0x0000,0x
5FA7, //主手柄按键表
12. 0x07FF,0x39C1,0x3AC2,0x08F0,0x5CA4,0x21D9,0x58A0,0x20D8};
13./*uint code hos_tabup[16]={0x0000,0x8870,0x877F,0x0000,0xDE26,0x0000,0x0000,0x
DF27, //主手柄释放键表
14. 0xF50D,0xFF07,0xE31B,0xF800,0xDC24,0xA159,0xD820,0xA058};
15.uint code hos_tabdn[16]={0x0000,0x08F0,0x07FF,0x0000,0x5EA6,0x0000,0x0000,0x5
FA7, //主手柄按键表
16. 0x758D,0x7F87,0x639B,0x7880,0x5CA4,0x21D9,0x58A0,0x20D8};
*/
17.uint code gue_tabup[16]={ 0x9961,0x9A62,0x9B63,0x8B73,0x0000,0x0000,0x0000,0x
0000, //副手柄释放键表
18. 0xB149,0xA35B,0xFC04,0xF901,0x0000,0x0000,0x0000,0x0000};
19.uint code gue_tabdn[16]={ 0x19E1,0x1AE2,0x1BE3,0x0BF3,0x0000,0x0000,0x0000,0
x0000, //副手柄按键表
20. 0x31C9,0x23DB,0x7C84,0x7981,0x0000,0x0000,0x0000,0x0000};
21.//unsigned long endcode=0x0005AA8A; //结束码的红外发
射码,
22.uint KEY_hos[2]={0xFFFF,0xFFFF}; //主游戏手柄扫描
值,
23.uint KEY_gue[2]={0xFFFF,0xFFFF}; //副游戏手柄扫描
值,
24.uint ir_key; //标准红外码缓冲,
25.uint chan_key; //手柄按键有变化的位,
26.uint stat_key; //当前手柄的状态值,
27.
28.unsigned long ref;
29.unsigned long idata irbuf[32]; //红外输出缓冲区,
30.unsigned long *p_t; //指向红外缓冲区的两个指针,
31.unsigned long *p_b;
32.bit bdata Buf_emp=1; //红外输出缓冲区空标
志,
33.bit bdata Bit_F=0; //红外输出的移位标志,
34.bit bdata Buf_ful=0; //红外输出缓冲区满标志
35.bit bdata Saf_F=0; //安全间隔标志位
36.bit bdata Host_F=0;
37.
38.sbit ACK1 =P1^2;
39.sbit ACK2 =P1^3;
40.sbit DI1 =P0^3;
41.sbit DI2 =P1^6;
42.sbit DO1 =P0^0;
43.sbit DO2 =P0^4;
44.sbit CLK1 =P0^2;
45.sbit CLK2 =P0^7;
46.sbit SEN1 =P0^1;
47.sbit SEN2 =P1^7;
48.sbit PWM =P0^5;
49.sbit IR_OUT=P0^6;
50.
51.
52.void delay(uchar n);
53.void Isp_Init(); //初始化串口,
54.//void keysignout(void) ;
55.void Getkey_hos(void);
56.void Getkey_gue(void);
57.void Trans(void); //手柄键码转化为红外输出码,
58.
59.main()
60.{
61.
62. P0M1=0XDF; //外部引脚I/O配置,
63. P0M2=0XF7;
64. P1M1=0XFF;
65. P1M2=0X83;
66. DIVM=0; //时钟分频设置,
67.// AUXR1=AUXR1|0x80;
68. TH0=238;
69. TL0=212;
70. TMOD=0X01; //记数器0设置,
71. PWM=0;
72. IR_OUT=1;
73. TCON=0x14; //设置下降沿中断,
74. IEN0=0x06; //开外部中断一 ,定时器零,
75. EA=1;
76. Isp_Init(); //初始化串口,
77.
78. p_t=&irbuf[0];
79. p_b=&irbuf[0];
80.
81.
82.
83. while(1)
84. {
85. Getkey_hos();
86. chan_key=KEY_hos[0]^KEY_hos[1];
87. if(chan_key)
88. {
89. Host_F=1;
90. stat_key=KEY_hos[0];
92. KEY_hos[1]=KEY_hos[0];
93. }
94. Getkey_gue();
95. chan_key=KEY_gue[0]^KEY_gue[1];
96. if(chan_key)
97. {
98. Host_F=0;
99. stat_key=KEY_gue[0];
100. Trans();
101. KEY_gue[1]=KEY_gue[0];
102. }
103. }
104.
105.}
106.
107.void irout() interrupt 2 using 2 //使用外部中断1中断服务程序 ,
108.{
109. EA=0;
110. IR_OUT=PWM;
111. PWM=!PWM;
112. EA=1;
113.}
114.
115.void delay( uchar n) //n*12us延时程序;
116.{
117. uchar i, j;
118. for(i=0;i<N;I++) for(j="0;j<10;j++)" _nop_(); } void Getkey_hos() get gamekdy uchar p,i,ref; SEN2="0;" delay(4); for(p="0;p<5;p++)" { for(ref="0x01;ref">0x00;ref<<=1) 119. {
120. CLK2=0;
121. if(ref&dout[p]) //输出一位控制数据?是否满足4us
122. DO2=1;
123. else
124. DO2=0;
125. for(i=0;i<3;i++) //大概2us的延迟,
126. _nop_();
127. CLK2=1;
128. if(DI2)
129. din[p]=ref|din[p]; //输入一位控制数据?是否满足4us
130. for(i=0;i<2;i++) //大概2us的延迟,
131. _nop_();
132. }
133. DO2=1;
135. {
136.
137.// delay(1);
138.// do
139.// {
140.// while(ACK2) //检测ACK信号是否响应。

有响应则向下执行。

141.// {
142. _nop_();
143.// ACK=1;
144.// timeout=10000;
145.// break;
146.// }
147. delay(2);
148.// }
149.// while(timeout--);
150. }
151.// else
152.// {}
153.
154. }
155. SEN2=1;
156.// delay(2);
157.// keysignout(); // 键码值通过串口输出。

158. KEY_hos[0]=din[3];
159. KEY_hos[0]<<=8;
160. KEY_hos[0]+=din[4];
161. for(i=0;i<5;i++)
162. din[i]=0x00;
163.// for(i=0;i<255;i++)
164.// delay(255);
165.
166.}
167.
168.void Getkey_gue() //get gamekdy
169.{
170. uchar p,i,ref;
171.
172. SEN1=0;
173. delay(4);
174. for(p=0;p<5;p++)
175. {
176. for(ref=0x01;ref>0x00;ref<<=1)
177. {
179. if(ref&dout[p]) //输出一位控制数据?是否满足4us
180. DO1=1;
181. else
182. DO1=0;
183. for(i=0;i<3;i++) //大概2us的延迟,
184. _nop_();
185. CLK1=1;
186. if(DI1)
187. din[p]=ref|din[p]; //输入一位控制数据?是否满足4us
188. for(i=0;i<2;i++) //大概2us的延迟,
189. _nop_();
190. }
191. DO1=1;
192. if(p<4)
193. {
194.
195.// delay(1);
196.// do
197.// {
198.// while(ACK2) //检测ACK信号是否响应。

有响应则向下执行。

199.// {
200. _nop_();
201.// ACK=1;
202.// timeout=10000;
203.// break;
204.// }
205. delay(2);
206.// }
207.// while(timeout--);
208. }
209.// else
210.// {}
211.
212. }
213. SEN1=1;
214.// delay(2);
215.// keysignout(); // 键码值通过串口输出。

216. KEY_gue[0]=(din[3]|0x0F);
217. KEY_gue[0]<<=8;
218. KEY_gue[0]+=(din[4]|0x0F);
219. for(i=0;i<5;i++)
220. din[i]=0x00;
221.// for(i=0;i<255;i++)
222.// delay(255);
223.
224.}
225.void Isp_Init() //初始化串口,
226.{
227. SSTA T=0xFA;
228. BRGCON=0x00;
229. AUXR1=0x40;
230. SCON=0x50;
231. BRGR1=0x90;
232. BRGR0=0x00;
233. BRGCON=0x03;
234.}
235.
236.void t0() interrupt 1 using 2 //使用定时器0中断服务程序 , 237.{
238./* if(Buf_emp==0)
239. TxD=0;
240. else
241. TxD=1; */
242. if(Saf_F) //安全间隔时间定时,
243. {
244. Saf_F=0;
245. IR_OUT=1;
246. TH0=39;
247. TL0=200;
248. TR0=1;
249. }
250. else
251. {
252. if(Buf_emp==0)
253. {
254. TH0=255; //定时45us
255. TL0=6;
256. if(Bit_F==0)
257. {
258. Bit_F=1;
259. ref=0x00000001;
260. }
261. if(*p_b&ref) //输出一位红外数据,
262. IR_OUT=0;
263. else
264. IR_OUT=1;
265. TR0=1;
266. ref<<=1;
267. if(ref>0x00080000) //判断一个红外码字是否发完,
268. {
269. Bit_F=0;
270. Buf_ful=0;
271. if(p_b==&irbuf[31])
272. p_b=&irbuf[0];
273. else
274. p_b++;
275. if(p_b==p_t)
276. Buf_emp=1;
277. Saf_F=1; //置保护时间标志,
278.
279. }
280. }
281. else
282. {
283. TH0=255; //这个时间能否再减小?
284. TL0=6;
285. TR0=1;
286. IR_OUT=!PWM;
287. }
288. }
289.}
290.
291. void Trans() //手柄键码值转换为红外输出码值,292.{
293. uchar i;
294. uint ref; //一次扫描手柄后出现变化的键,295. // if(KEY[0]==KEY[1])
296. // return;
297. // else
298. if(Buf_ful==0)
299. {
300.
301. for(i=0,ref=0x8000;i<16;i++,ref>>=1)
302. {
303. if(chan_key&ref)
304. {
305. if(stat_key&ref) //为真检测到释放键
306. {
307. if(Host_F) //释放键为主手柄的释放键
308. ir_key=hos_tabup[i];
309. else //释放键为副手柄的释放键
310. ir_key=gue_tabup[i]; //释放键进入红外寄存器
311. *p_t=ir_key&0x00FF;
312. *p_t<<=12;
313. *p_t+=(((ir_key&0xFF00)>>6)|0x0802);
314.
315./* if(p_t==&irbuf[31])
316. p_t=&irbuf[0];
317. else
318. p_t++;
319. Buf_emp=0; //红外输出缓冲区有数据,
320. if(p_t==p_b)
321. {
322. Buf_ful=1; //红外输出缓冲区满,
323. while(1) //测试缓冲区是否会满,
324. {
325. DO1=1;
326. delay(5);
327. DO1=0;
328. delay(5);
329. }
330. return;
331. }
332.
333. *p_t=0x0005AA8A; //结束键进缓冲区, */
334. }
335. else //检测到按下键,
336. {
337. if(Host_F) //按下键为主手柄的按下键,
338. ir_key=hos_tabdn[i];
339. else //按下键为副手柄的按下键,
340. ir_key=gue_tabdn[i];
341. *p_t=ir_key&0x00FF;
342. *p_t<<=12;
343. *p_t+=(((ir_key&0xFF00)>>6)|0x0802); //标准红外码转换为添加过引导码的发送红外码,
344. }
345. if(p_t==&irbuf[31])
346. p_t=&irbuf[0];
347. else
348. p_t++;
349. Buf_emp=0; //红外输出缓冲区有数据,
350. if(p_t==p_b)
351. {
352. Buf_ful=1; //红外输出缓冲区满,
353.// while(1) //测试缓冲区是否会满,
354.// {
355. TxD=0;
356.// }
357. return;
358. }
359.
360. }
361.
362. }
363. }
364.
365.}
/***********************************
说明:
以下程序是用spi去读取当某按键按下后,ps2手柄发出的信号(data线)comd线是程序(即主机)向手柄发出的控制信号
clk是硬件运行时钟(频率有具体要求)
att是控制线(有使能的意思)
这里没有用到ack,不需要
另外是vcc(幅值有要求),和gnd,电源提供。

程序要做的就是模拟时序,发出控制信号,读取信号。

就这么简单。

***************************************/
//====================================
int HandKey = 0x00;
//Not Used Now
//int HandRunFlag = 0;
//int HandKey2 = 0x00;
//int HandKey3 = 0x00;
int Comd[9] = {0x01,0x42,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
int Data[9] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void InitPS2(void) //手柄PS2通信初始化
{
EALLOW;
GpioMuxRegs.GPAMUX.bit.TDIRA_GPIOA11 = 0; //GPIO A11 :DA TE signal GpioMuxRegs.GPAMUX.bit.TCLKINA_GPIOA12 = 0; //GPIO A12 :COMD signal
GpioMuxRegs.GPEMUX.bit.XINT2_ADCSOC_GPIOE1 = 0; //GPIO E1 :CLK signal
GpioMuxRegs.GPEMUX.bit.XNMI_XINT13_GPIOE2 = 0; //GPIO E2 :A TT signal
GpioMuxRegs.GPADIR.bit.GPIOA11 = 0; //IN mode DA TA
GpioMuxRegs.GPADIR.bit.GPIOA12 = 1; //OUT mode COMD
GpioMuxRegs.GPEDIR.bit.GPIOE1 = 1; //OUT mode CLK
GpioMuxRegs.GPEDIR.bit.GPIOE2 = 1; //OUT mode A TT
EDIS;
GpioDataRegs.GPADA T.bit.GPIOA12 = 1; //初始状态均为高电平
GpioDataRegs.GPEDA T.bit.GPIOE1 = 1;
GpioDataRegs.GPEDA T.bit.GPIOE2 = 1;
}
void ReadHand(void) //读手柄按键
{
volatile int byte = 0;
volatile int pointer = 0;
volatile int ref = 0;
GpioDataRegs.GPEDA T.bit.GPIOE2 = 0; //A TT拉低
Delay(5);
for(byte=0; byte<9; byte++)
{
for(ref=0x01; ref<0x0100; ref<<=1)
{
GpioDataRegs.GPEDA T.bit.GPIOE1 = 0;//时钟拉低
if(ref & Comd[byte])
{
GpioDataRegs.GPADA T.bit.GPIOA12 = 1; //输出一位控制位}
else
{
GpioDataRegs.GPADA T.bit.GPIOA12 = 0;
}
Delay(15);
GpioDataRegs.GPEDA T.bit.GPIOE1 = 1; //时钟拉高
Delay(15);
if(GpioDataRegs.GPADA T.bit.GPIOA11)
{
Data[byte]=ref | Data[byte];
}
}
Delay(50);
}
GpioDataRegs.GPEDA T.bit.GPIOE2 = 1; //拉高A TT
HandKey = Data[3];
HandKey = HandKey <<8;
HandKey = HandKey + Data[4];
// HandKey2=Data[5];
// HandKey2=HandKey2<<8;
// HandKey2=HandKey2+Data[6];
// HandKey3=Data[7];
// HandKey3=HandKey3<<8;
// HandKey3=HandKey3+Data[8];
for(pointer=0;pointer<9;pointer++)
{
Data[pointer]=0x00;
}
if(HandKey==0xffff)
{
HandKeyFlag=0;
}
else
{
HandKeyFlag=1;
}
}
索尼PS手柄原理分析与制作目录
∙Playstation 手柄针脚输出
∙PS手柄信号
∙PS手柄数据
∙用74XX逻辑电路仿真PS手柄
∙用微处理器仿真PS手柄
PS手柄针脚输出
面对插头
-------------------------------
PIN 1->| o o o | o o o | o o o |
\_____________________________/
针脚# 作用
1.DA TA
MAND
3.N/C (9 V olts unused)
4.GND
5.VCC
6.A TT
7.CLOCK
8.N/C
9.ACK
DA TA
信号流向从手柄到主机。

此信号是一个8 bit的串行数据,同步传送于时钟下降沿(输入输出信号在时钟信号由高到低时变化,所有信号的读取在时钟前沿到电平变化之前完成。

) COMMAND
信号流向从主机到手柄。

此信号和DA TA相对,同样是一个8 bit的串行数据,同步传送于时钟下降沿。

VCC
电源电压从5V到3V原装的索尼手柄都可以工作。

主机主板上装有表面安装的750mA保险丝,用于防止外设过载(750mA是包括左右手柄和记忆卡)。

A TT
A TT 用于提供手柄触发信号。

信号在通信期间处于低电平。

又有人将此针脚叫做Select, DTR 和Command。

CLOCK
信号流向从主机到手柄。

用于保持数据同步。

ACK
从手柄到主机的应答信号。

此信号在每个8 bits数据发送之后的最后一个时钟周期变低,并且A TT 一直保低电平。

如果ACK 信号不变低约60微秒PS主机会试另一个外设。

相关文档
最新文档