PS2游戏手柄

文章目录

  • PS2游戏手柄
    • 1 PS2介绍
    • 2 PS2通讯协议介绍
    • 3 代码
      • h文件
      • c文件

1 PS2介绍


PS2手柄是日本SONY公司的PlayStation2 游戏机的遥控手柄。索尼的 PSX系列游戏主机在全球都很畅销。不知什么时候便有人打起 PS2手柄的主意,破解了通讯协议,使得手柄可以接在其他器件上遥控使用,比如遥控我们熟悉的机器人。突出的特点是这款手柄性价比极高,按键丰富,方便扩展到其它应用中。

2 PS2通讯协议介绍

PS2采用的是SPI通信协议,SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(DI、DO、CS、CLK),节约了芯片的管脚,同时为PCB的布局上节省空间。


PS2接收器上一共有九根引脚,按上图从左往右,依次为:

1.DI/DAT:信号流向,从手柄到主机,此信号是一个8bit 的串行数据,同步传送于时钟的下降沿。信号的读取在时钟由高到低的变化过程中完成。

2.DO/CMD:信号流向,从主机到手柄,此信号和 DI相对,信号是一个 8bit 的串行数据, 同步传送于时钟的下降沿。

3.NC:空端口。

4.GND:电源地。

5.VCC:接收器工作电源,电源范围 3~5V。

6.CS/SEL:用于提供手柄触发信号。在通讯期间,处于低电平。

7.CLK:时钟信号,由主机发出,用于保持数据同步。

8.NC:空端口。

9.ACK:从手柄到主机的应答信号。此信号在每个8bits数据发送的最后一个周期变低并且CS一直保持低电平,如果CS信号不变低,约60微秒PS主机会试另一个外设。在编程时未使用ACK端口。(可以忽略)

时钟频率 250KHz(4us),如果接收数据不稳定,可以适当的增加频率。 在通讯过中,
一串数据通讯完成后 CS 才会由低转高,不是 1 个字节通讯完成后就由低转高,在通讯期
间,一直处于低电平。

在时钟下降沿时,完成数据(lbit)的发送与接收,发送和接收是同时完成的。当单片
机想读手柄数据或向手柄发送命令时,将会拉低 CS 线电平,并发出一个命令“0x01”;手
柄会回复它的 ID “0x41=绿灯模式,0x73=红灯模式”;在手柄发送 ID 的同时,单片机将
传 送 0x42,请求数据;随后手柄发送出 0x5A,告诉单片机“数据来了”。
idle:数据线空闲,该数据线无数据传送。

一个通讯周期有 9 个字节(8 位),这些数据是依次按位传送。

注意的是:

  1. CS线在通讯期间拉低,通信过程中CS信号线在一串数据(9个字节,每个字节为8位)发送完毕后才会拉高,而不是每个字节发送完拉高。

  2. DO、DI在在CLK时钟的下降沿完成数据的发送和读取。
    下降沿:数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。

  3. CLK的每个周期为12us。若在某个时刻,CLK处于下降沿,若此时DO为高电平则取“1”,低电平则取“0”。连续读8次则得到一个字节byte的数据,连续读9个字节就能得到一次传输周期所需要的数据。DI也是一样的,发送和传输同时进行。

具体的通讯过程如下:

以STC15为例:

1、首先STC15拉低CS片选信号线,然后在每个CLK的下降沿读一个bit,每读八个bit(即一个byte)CLK拉高一小段时间,一共读九组bit。

2、第一个byte是STC15发给接收器命令“0X01” 。

3、PS2手柄会在第二个byte回复它的ID(0x41=绿灯模式,0x73=红灯模式),同时第二个byte时STC15发给PS2一个0x42请求数据。

红灯模式时:左右摇杆发送模拟值,0x00~0xFF 之间,且摇杆按下的 键值 L3 、 R3 有效;
绿灯模式时:左右摇杆模拟值为无效,推到极限时,对应发送 UP、RIGHT、DOWN、 LEFT、△、○、╳、□,按键 L3 、 R3 无效。

4、第三个byte PS2 会给主机发送 “0x5A” 告诉STC15数据来了。

5、从第四个byte开始全是接收器给主机发送数据,每个byte定义如上图,当有按键按下,对应位为“0 ”,例如当键“SELECT”被按下时, Data[3]=11111110。

对于整个通讯过程,你理解成下面的一段对话:


拉低CS,表示开始数据通信


byte 0 :
STC15(DO) : 0x01 ------------------------- [现在开始通信]
PS2手柄(DI) : 空 ---------------------------- [空]


byte 1 :
STC15(DO) : 0x42 -------------------------- [请求发送数据]
PS2手柄(DI) : 红灯0x73
            绿灯0X41---------------------[现在的ID]


byte 2:
STC15(DO) : 空 ------------------------------ [空]
PS2手柄(DI) : 0X5A ------------------------- [数据来了]


byte 3:
STC15(DO) : 0X00~0XFF ------------------ [右侧小震动电机是否开启]
PS2手柄(DI) : 00000000~11111111 ------- [SELECT、 L3 、 R3、 START 、 UP、 RIGHT、 DOWN、 LEFT 是否被按下,若被按下对应位为0]


byte 4:
STC15(DO) : 0X00~0XFF ------------------ [左侧大震动电机振动幅度]
PS2手柄(DI) : 00000000~11111111 ------- [L2 、 R2、L1 、R1、△、○、╳、□ 是否被按下,若被按下对应位为0]


byte 5:
STC15(DO) : 空 -------------------------------- [空]
PS2手柄(DI) : 0X00~0XFF ------------------ [左侧X轴摇杆模拟量]


byte 6:
STC15(DO) : 空 -------------------------------- [空]
PS2手柄(DI) : 0X00~0XFF ------------------ [左侧Y轴摇杆模拟量]


byte 7:
STC15(DO) : 空 -------------------------------- [空]
PS2手柄(DI) : 0X00~0XFF ------------------ [右侧X轴摇杆模拟量]


byte 8:
STC15(DO) : 空 -------------------------------- [空]
PS2手柄(DI) : 0X00~0XFF ------------------ [右侧Y轴摇杆模拟量]

注意:模拟量只对红灯模式下有效,绿灯模式下摇杆推至极限分别对应 UP、RIGHT、DOWN、 LEFT、△、○、╳、□ 。L3、R3只对红灯模式下有效,在绿灯模式下无效。

在手柄通信前还需要一系列的初始化(是否启动振动电机、是否进行锁存等),详情可以参考下面代码。当然,不进行初始化也是可以的,手柄会默认之前的配置。
——————————————————————————————

3 代码

主机收到的数据在out[]数组中,在其他文件用到PS2时,只要对out[]数组进行处理和分析即可。

h文件

#ifndef __PS2_H__
#define __PS2_H__
#include "STC15Fxxxx.h"#define Up_L 0xEF
#define Down_L 0xBF
#define Left_L 0x7F
#define Right_L 0xDF#define Up_R 0xEF
#define Down_R 0xBF
#define Left_R 0x7F
#define Right_R 0xDF#define L1 0xFB
#define L2 0xFE
#define L3 0xFD
#define R1 0xF7
#define R2 0xFD
#define R3 0xFB#define UP_L 0x7F
#define UP_L 0x7F
#define UP_L 0x7F
#define UP_L 0x7F
#define UP_L 0x7F
#define UP_L 0x7F
extern u8 out[9];  void PS2_Init(void);
void PS2_ShortPoll(void);
void psin(u8 command);//手柄发送子程序
u8 PS2_Cmd(u8 command);
void Read_PS2(void);
u8 PS2_RedLight(void);
void PS2_EnterConfing(void);
void PS2_TurnOnAnalogMode(void);
void PS2_VibrationMode(void);
void PS2_ExitConfing(void);
void PS2_ClearData();
void PS2_Vibration(u8 motor1,u8 motor2);
u8 PS2_AnologData(u8 button);
#endif

c文件

#include "PS2.h"
#include "delay.h"
/*****************************PS2遥控器说明
out[3]==0xEF//左4个按键中上
out[3]==0xBF//左4个按键中下
out[3]==0x7F//左4个按键中左
out[3]==0xDF//左4个按键中右out[4]==0xEF//右4个按键中上
out[4]==0xBF//右4个按键中下
out[4]==0x7F//右4个按键中左
out[4]==0xDF//右4个按键中右out[4]==0xFB//左1,2个按键中1
out[4]==0xFE//左1,2个按键中2
out[4]==0xF7//右1,2个按键中1
out[4]==0xFD//右1,2个按键中2当按下MODE键手柄MODE LED灯亮起时
out[7] 00——80——FF 左摇杆从左到右
out[8] 00——7F——FF 左摇杆从上到下
out[5] 00——80——FF 右摇杆从左到右
out[6] 00——7F——FF 右摇杆从上到下
当手柄MODE LED灯不亮时,手柄功能同左四右四按键
*******************************/
//******定义接口*********
sbit  DATA=P3^0;   //手柄接口
sbit  CMND=P3^1;
sbit  CS=P3^2;
sbit  CLK=P3^3;/********手柄定义变量*********/
u8 code Comd[9]={0x01,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
u8 out[9];  void PS2_Init(void)
{DATA=1;PS2_ShortPoll();PS2_ShortPoll();PS2_ShortPoll(); PS2_EnterConfing(); // 进入配置模式 PS2_TurnOnAnalogMode(); // “红绿灯”配置模式,并选择是否保存 PS2_VibrationMode(); // 开启震动模式 PS2_ExitConfing(); // 完成并保存配置
}
//手柄配置初始化:
void PS2_ShortPoll(void)
{CS=0; delay_us(16); PS2_Cmd(0x01); PS2_Cmd(0x42); PS2_Cmd(0X00); PS2_Cmd(0x00); PS2_Cmd(0x00); CS=1; delay_us(16);
}
void delay(u16 n)  //delay(x)=(2.5+x)us;
{u16 i;for(i=0;i<n;i++) _nop_();
//      _nop_();//每个_nop_();大概0.1微秒
}
void psin(u8 command)//手柄发送子程序
{u8 i;for(i=0;i<=7;i++)     //逐位接收     {if(command&0x01)  //此if下5行语句用时1usCMND=1;elseCMND=0;command=command>>1;_nop_();  _nop_();CLK=0;delay(10); CLK=1;delay(3);}CMND=1;
}u8 PS2_Cmd(u8 command)
{u8 i,j=1;u8 res=0; for(i=0;i<=7;i++)     //逐位接收     {if(command&0x01)CMND=1;elseCMND=0;command=command>>1;_nop_();   _nop_();CLK=0;delay(10);if(DATA) res=res+j;j=j<<1; CLK=1;delay(3);        }CMND=1;return res;
}void Read_PS2(void)//手柄读取程序
{u8 i;CS=0;for(i=0;i<9;i++)  //扫描按键{out[i]=PS2_Cmd(Comd[i]);    } CS=1;
} // 判断是否为红灯模式,0x41=模拟绿灯,0x73=模拟红灯
// 返回值;0,红灯模式
// 其他,其他模式
u8 PS2_RedLight(void)
{CS=0;PS2_Cmd(Comd[0]); // 开始命令PS2_Cmd(Comd[1]); // 请求数据CS=1;if( out[1]== 0x73) return 0;else return 1;
}
//进入配置
void PS2_EnterConfing(void)
{CS=0; delay_us(16); PS2_Cmd(0x01); PS2_Cmd(0x43); PS2_Cmd(0x00); PS2_Cmd(0x01); PS2_Cmd(0x00); PS2_Cmd(0x00);PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x00); CS=1; delay_us(16);
}
// 发送模式设置
void PS2_TurnOnAnalogMode(void)
{CS=0; PS2_Cmd(0x01); PS2_Cmd(0x44); PS2_Cmd(0x00); PS2_Cmd(0x01);//analog=0x01;digital=0x00 软件设置发送模式 PS2_Cmd(0xEE);//Ox03 锁存设置,即不可通过按键“MODE ”设置模式。        //0xEE 不锁存软件设置,可通过按键“MODE ”设置模式。 PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x00); CS=1; delay_us(16);
}// 振动设置
void PS2_VibrationMode(void)
{CS=0; delay_us(16); PS2_Cmd(0x01); PS2_Cmd(0x4D); PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x01); CS=1;delay_us(16);
}
// 完成并保存配置
void PS2_ExitConfing(void)
{CS=0;delay_us(16);PS2_Cmd(0x01);PS2_Cmd(0x43); PS2_Cmd(0x00);PS2_Cmd(0x00); PS2_Cmd(0x5A); PS2_Cmd(0x5A); PS2_Cmd(0x5A);PS2_Cmd(0x5A); PS2_Cmd(0x5A); CS=1; delay_us(16);
}
// 清除数据缓冲区
void PS2_ClearData()
{u8 a; for(a=0;a<9;a++) {out[a]=0x00;}
}
//手柄震动函数
//motor1:右侧小震动电机 0x00关,其他开
//motor2:左侧大震动电机 0x40~0xFF,电机开,值越大,震动越大
//只有在初始化函数 void PS2_Init(void)中,对震动电机进行了初始化
//(PS2_VibrationMode();//开启震动模式),这个函数命令才会被执行。
void PS2_Vibration(u8 motor1,u8 motor2)
{CS=0; delay_us(16); PS2_Cmd(0x01); // 开始命令PS2_Cmd(0x42);// 请求数据PS2_Cmd(0x00);PS2_Cmd(motor1);PS2_Cmd(motor2); PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x00); PS2_Cmd(0x00); CS=1; delay_us(16);
}
// 得到一个摇杆的模拟量 范围 0~256
u8 PS2_AnologData(u8 button)
{return out[button];
}

PS2游戏手柄——基于STC15W4K32S4相关推荐

  1. 基于STM32的PS2游戏手柄智能小车

    我们小时候在家经常使用PS2游戏手柄打游戏,后来学习了单片机也想做一个智能的遥控坦克.之前做的智能车基本上他的流畅度不好,所以感觉用起来不是很满意,于是在做毕业设计的时候就购买了这样一个坦克模型和PS ...

  2. 基于STC15W4K32S4单片机仿真《74HC595驱动数码管动态显示》

    1. 编 程 软 件:keil5版本. 2.仿 真 软 件:Proteus8.12 3.单片机选型:STC15W4K32S4 4.学 习 目 标:1.学习74HC595驱动数码管动态显示 5.程 序 ...

  3. 基于STC15W4K32S4单片机仿真《74HC595驱动数码管静态显示》

    1. 编 程 软 件:keil5版本. 2.仿 真 软 件:Proteus8.12 3.单片机选型:STC15W4K32S4 4.学 习 目 标:1.学习74HC595芯片工作方式           ...

  4. 基于STC15W4K32S4单片机仿真《点亮1个LED》

    1. 编 程 软 件:keil5版本. 2.仿 真 软 件:Proteus8.12.据说8.9版本也可以. 3.单片机选型:STC15W4K32S4 4.学 习 目 标:从点亮1个LED开始,先学习控 ...

  5. 基于STC15W4K32S4单片机仿真《外部中断INT0控制LED亮灭》

    1. 编 程 软 件:keil5版本. 2.仿 真 软 件:Proteus8.12 3.单片机选型:STC15W4K32S4 4.学 习 目 标:   1.学习外部中断INT0 5.程 序 文 件 : ...

  6. FPGA 解析PS2游戏手柄

    FPGA开发之解析SONY-PS2手柄SPI协议 在嵌入式开发过程中,常用到的通讯方式之一就是SPI协议,SPI(Serial Peripheral Interface–串行外设接口)总线系统是一种同 ...

  7. stc15w4k32s4芯片引脚图片_基于STC15W4K32S4芯片的智能晒衣装置

    基于 STC15W4K32S4 芯片的智能晒衣装置 阳思琦 [摘 要] 随着中国城市化进程的推进,楼房间距不断缩减,阳光愈显珍贵,寻 找晒衣物的空间成为令人头疼的问题:而在阴雨天气,人们常常由于难以跨 ...

  8. 【树莓派C语言开发】实验14:PS2游戏手柄模块(关联PCF8591)

    本次实验还是需要用到PCF8591模数转换器,莫非它要取代双色LED成为新的常驻嘉宾吗? 本次实验是摇杆实验.这个玩意可以用于操控机器人或者是树莓派的遥控小车.当然,生活中最常见的用途还是在游戏手柄上 ...

  9. 基于STC15W4K32S4单片机仿真《多个LED闪烁》

    1. 编 程 软 件:keil5版本. 2.仿 真 软 件:Proteus8.12.据说8.9版本也可以. 3.单片机选型:STC15W4K32S4 4.学 习 目 标:1.学习延时函数 2.学习子函 ...

  10. 综合设计——基于STC15W4K32S4(第三部分)

    "所有模式功能都有啦,快快把它们联系起来,好激动哦!!" 文章目录 3.7 模式选择 3.8 其余的一些小模式 3.8.1 短动画 3.8.2 显示正弦,余弦,中心水平垂直线.圆 ...

最新文章

  1. GridView 模版列编辑状态Dropdownlist 事件
  2. Lasso回归的坐标下降法推导
  3. 2057. [ZLXOI2015]殉国
  4. 2010年度报告:是谁在编写Linux内核?
  5. Spring的使用步骤
  6. python右对齐格式化输出_Python中格式化输出的两种方法介绍
  7. oracle取本月最后一天是星期几_oracle SQL语句取本周本月本年的数据
  8. MySQL中清空表和截断表的区别(新手入门.)
  9. 怒江java培训班_怒江万词霸屏是什么意思
  10. 做自媒体最重要的是坚持,但你坚持后还是一无所获
  11. Java---集合类框架图
  12. php7 libiconv,PHP7编译错误:php编译undefined reference to `libiconv 错误
  13. 软件设计师教程笔记整理
  14. office及各类软件的图标修复
  15. MMC-HVDC仿真模型,pscad柔性直流输电仿真mmc仿真模型,双端mmc模型,MMC为21电平NLM和均压控制
  16. 计算机打不开guest用户,win10系统打不开guest账户的解决方法
  17. 84消毒液和酒精混合的化学反应分析
  18. 以下11條小建議,幫助你們的異地戀一直保持活力
  19. 抽屉实现评论数据结构及评论内容显示示例
  20. 基于OpenSSL 1.1.1版实现的SM2签名与验签C程序

热门文章

  1. 关于通过请求获取的验证码不正确的解决的破解方法
  2. 如何将一个向量投影到一个平面上_线性代数总结 第三章 向量代数与几何计算(空间平面和直线)...
  3. 天翼云主机安装php环境,使用PHP空间和天翼云盘搭建私人云盘
  4. python全栈开发包括那些_简述Python全栈工程师要学会什么
  5. 全栈工程师将会缔造下一个高薪群体
  6. 百度网盘下载加速(pc端)
  7. 多视图几何三维重建实战系列之COLMAP
  8. 解决mac上复制粘贴失效问题
  9. Nacos 一致性协议:Distro协议
  10. windows 系统 system 进程占用80端口