基于PS2索尼游戏手柄开发
最近师妹拿了个PS2手柄给我
安排!!!!
代码下载链接
在32上面 成功用PS2 控制显示屏输出
PS2是一个很好学习通讯时序的的工具
这里写下他的实现代码和我的学习思路
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|
DI/DAT | DO/CMD | NC | VDD | GND | CS/SEL | CLK | NC | ACK |
我们不使用 NC NC ACK 这三个端口
NC:空端口;
ACK:从手柄到主机的应答信号。
此信号在每个 8bits 数据发送的最后一个周期变低
并且 CS 一直保持低电平,如
果CS 信号不变低,
约 60 微秒 PS 主机会试另一个外设。
在编程时未使用 ACK 端口。
我们接线如下
DI PB12 浮空输入 输入 数据
DO PB13 推挽输出 输出 数据
CS PB14 推挽输出 输出 数据
CLK PB15 推挽输出 输出 数据
首先是PS2 初始化 我们将IO配置成对应的模式
void PS2_Init(void) //PB12 ÊäÈëÏÂÀ ÆäËûµÄÍÆÍìÊä³ö
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_12;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
然后 我们宏定义了一堆指令 为了简化我们后续的代码
这里用到了正点原子给的 sys.h 的32库
`#define DI PBin(12) //PB12 (DI 数据输入位)输入
#define DO_H PBout(13)=1 //令PB13 (DO 命令位/数据输出位) 输出高电平
#define DO_L PBout(13)=0 //令PB13 (DO 命令位/数据输出位)输出低电平
#define CS_H PBout(14)=1 //令PB14 (CS 使能位)输出拉高
#define CS_L PBout(14)=0 //令PB14 (CS 使能位)输出拉低
#define CLK_H PBout(15)=1 //令PB15 (CLK)输出拉高
#define CLK_L PBout(15)=0 //令PB15 (CLK)输出拉低
//These are our button constants
#define PSB_SELECT 1
#define PSB_L3 2
#define PSB_R3 3
#define PSB_START 4
#define PSB_PAD_UP 5
#define PSB_PAD_RIGHT 6
#define PSB_PAD_DOWN 7
#define PSB_PAD_LEFT 8
#define PSB_L2 9
#define PSB_R2 10
#define PSB_L1 11
#define PSB_R1 12
#define PSB_GREEN 13
#define PSB_RED 14
#define PSB_BLUE 15
#define PSB_PINK 16
#define PSB_TRIANGLE 13
#define PSB_CIRCLE 14
#define PSB_CROSS 15
#define PSB_SQUARE 26
//#define WHAMMY_BAR 8
//These are stick values
#define PSS_RX 5 右遥控数据
#define PSS_RY 6
#define PSS_LX 7
#define PSS_LY 8
`
再开始命令之前
我们定义一个数组存放我们要发的数据
u16 Handkey;
u8 Comd[2]={
0x01,0x42};
u8 Data[9]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
然后我们根据时序图设置我们要发送的代码
DI 是发送 DO 是接收 可以看出
DI DO 一次发送过程有8位 数据
发送的过程中 CS一直处于 低电平
而CLK是每发送一个数据 则发送出一个脉冲 (低电平——高电平)
所以我们根据时序规律写出代码
Tips:volatile是易变型变量,是防止编译器优化代码时假设这个变量的值,保证每次小心地重新读取值。
void PS2_Cmd(u8 CMD)
{
volatile u16 ref=0x01;// 定义一个
Data[1] = 0; //令数据位1 为0
for(ref=0x01;ref<0x0100;ref<<=1) //发送一个8位的数据
{
if(ref&CMD) //输入你要发送的数据 如果不是发送为0x00 则拉高电平 准备发送状态
DO_H;
else DO_L; //否则拉低电平
CLK_H; //每发送一个指令 给CLK一个>50us电平脉冲
delay_us(50);
CLK_L;
delay_us(50);
CLK_H;
if(DI) //如果输入得到了高电平
Data[1] = ref|Data[1]; //与上一段的数据进行赋值处理
}
}
发送数据之后我们紧接着来读取PS2的数据表
这个数据意义顺序对照表的含义是 数据 一共 有 (9位)
首先 我们明白 英文含义 idle 空置 data 数据位置
DO 代表输出 输出指向手柄 (遥控)输出 DI 代表 向手柄接收器输入
我们先从 DO来看
第一位: 向遥控输出 0x01
第二位: 向遥控输出 0x42
第三位: 向遥控输出 0x00 既 NULL 、空 、 闲置
第四位: 向遥控输出 左电机参数
第五位: 向遥控输出 右电机参数
第六位: 向遥控输出 0x00 既 NULL 、空 、 闲置
第七位: 向遥控输出 0x00 既 NULL 、空 、 闲置
第八位: 向遥控输出 0x00 既 NULL 、空 、 闲置
第九位: 向遥控输出 0x00 既 NULL 、空 、 闲置
然后再从 DI来看
第一位: 接收器得到 0x00 既 NULL 、空 、 闲置
第二位: 接收器得到 接收器得到遥控器上面的ID
第三位: 接收器得到 0X5A ...(未知这个数据有什么用)
第四位: 接收器得到 SELECT L3 R3 START UP RIGHT DOWN LEFT 这8个按键的数值 (正好8个 对应8个数据位置)
第五位: 接收器得到 L3 R2 L1 R1 三角形 圆形 X 正方形 这8个按键的数值 (正好8个 对应8个数据位置)
第六位: 接收器得到 右遥杆 X值的坐标
可以看到 0X00 是指左边 0XFF 指指向右边 从左到右边 分了 0 ~255 个数值
第七位: 接收器得到 右遥杆 Y值的坐标
可以看到 0X00 是指上边 0XFF 指指向下边 从上到下边 分了 0 ~255 个数值
第八位: 接收器得到 左遥杆 X值的坐标
可以看到 0X00 是指左边 0XFF 指指向右边 从左到右边 分了 0 ~255 个数值
第九位: 接收器得到 左 遥杆 Y值的坐标
可以看到 0X00 是指上边 0XFF 指指向下边 从左到右边 分了 0 ~255 个数值
void PS2_ReadData(void)
{
volatile u8 byte=0;
volatile u16 ref=0x01;
CS_L; //拉低电平表示准备要来数据了
PS2_Cmd(Comd[0]); //0X01
PS2_Cmd(Comd[1]); //0X02
for(byte=2;byte<9;byte++) //发送6次 从2开始是因为指引数组
{
for(ref=0x01;ref<0x100;ref<<=1) //循环8次 不断左移 1 位
{
CLK_H; //脉冲
CLK_L;
delay_us(50);
CLK_H;
if(DI) //收到DI的数据
Data[byte] = ref|Data[byte]; //把脉冲数据保存下来
}
delay_us(50);
}
CS_H;
}
判断PS2是否为红灯状态 (红灯状态不工作)
u8 PS2_RedLight(void)
{
CS_L; //低电平 告诉
PS2_Cmd(Comd[0]); //发送第一个通信码
PS2_Cmd(Comd[1]); //发送第二个通信码
CS_H;
if( Data[1] == 0X73) return 0 ; //0X41 表示绿灯 0X73表示红灯
else return 1
}
读取手柄数据
void PS2_ReadData(void)
{
volatile u8 byte=0; // volatile防止比特位数据被修改
volatile u16 ref=0x01; //
CS_L;
PS2_Cmd(Comd[0]); //开始命令
PS2_Cmd(Comd[1]); //请求数据
for(byte=2;byte<9;byte++) //开始接受数据
{
for(ref=0x01;ref<0x100;ref<<=1) //循环8次 每一次 脉冲一个字节
{
CLK_H;
DELAY_TIME;
CLK_L;
DELAY_TIME;
CLK_H;
if(DI)
Data[byte] = ref|Data[byte];//存储数据
}
delay_us(16); //接收数据处理时间
}
CS_H; //高电平
}
得到摇杆值的模拟量 范围 0 ~ 255
u8 PS2_AnologData(u8 button)
{
return Data[button];
}
清空数据缓冲区
void PS2_ClearData() //将数组的数据全部清理为0
{
u8 a;
for(a=0;a<9;a++)
Data[a]=0x00;
}
手柄振动函数
Motor1 右侧小 振动电机 0X00 关 其他开
Motor2 左侧大 振动电机 0x40 ~ 0XFF 电机开 值越大 振动越大
void PS2_Vibration(u8 motor1, u8 motor2)
{
CS_L;
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_H;
delay_us(16);
}
简短刷新连接函数(?)
//short poll
void PS2_ShortPoll(void)
{
CS_L;
delay_us(16);
PS2_Cmd(0x01);
PS2_Cmd(0x42);
PS2_Cmd(0X00);
PS2_Cmd(0x00);
PS2_Cmd(0x00);
CS_H;
delay_us(16);
}
进入配置模式
void PS2_EnterConfing(void)
{
CS_L;
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_H;
delay_us(16);
}
发送模式设置
void PS2_TurnOnAnalogMode(void)
{
CS_L;
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_H;
delay_us(16);
}
振动模式设置
void PS2_VibrationMode(void)
{
CS_L;
delay_us(16);
PS2_Cmd(0x01);
PS2_Cmd(0x4D);
PS2_Cmd(0X00);
PS2_Cmd(0x00);
PS2_Cmd(0X01);
CS_H;
delay_us(16);
}
完成并保存配置
void PS2_ExitConfing(void)
{
CS_L;
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_H;
delay_us(16);
}
手柄配置 初始化
//ÊÖ±úÅäÖóõʼ»¯
void PS2_SetInit(void)
{
PS2_ShortPoll();
PS2_ShortPoll();
PS2_ShortPoll();
PS2_EnterConfing(); //进入配置模式
PS2_TurnOnAnalogMode(); //红绿灯配置模式 能否初始化
PS2_VibrationMode(); //开启震动模式
PS2_ExitConfing(); //完成并且保存配置
分割线
下面是 我的主函数
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "pstwo.h"
u8 x=0;
int main(void)
{
u8 angle=0;
u8 key=0;
u8 lcd_id[12];
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
LED_Init();
LCD_Init();
LCD_Clear(BLACK);
PS2_Init();
sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);
LED1=1;
LED0=1;
PS2_SetInit();
while(1)
{
sprintf((char*)lcd_id,"KEY :%04X",key);
LCD_ShowString(30,40,210,24,24,"SHOW TIME !! ");
x = key%10;
switch(x)
{
case 0:
LCD_Clear(WHITE);
delay_ms(10);
;break;
case 1:
LCD_Clear(BLACK);
delay_ms(10);
break;
case 2:
LCD_Clear(BLUE);
delay_ms(10);
break;
case 3:
LCD_Clear(RED);
delay_ms(10);
break;
case 4:
LCD_Clear(MAGENTA);
delay_ms(10);
break;
case 5:
LCD_Clear(GREEN);
delay_ms(10);
break;
case 6:
LCD_Clear(CYAN);
delay_ms(10);
break;
case 8:
LCD_Clear(YELLOW);
delay_ms(10);
break;
case 9:
LCD_Clear(BRRED);
delay_ms(10);
break;
case 10:
LCD_Clear(GRAY);
delay_ms(10);
break;
case 11:
LCD_Clear(LGRAY);
delay_ms(10);
break;
}
key=PS2_DataKey();
sprintf((char*)lcd_id,"PS2::%04X",key);
POINT_COLOR=RED;
LCD_ShowString(30,110,200,16,16,lcd_id);
PS2_ReadData();
angle = (PS2_AnologData(PSS_RX)/5)*3+15;
delay_ms(1);
if(key!=0)
LED1=!LED1;
}
}