【STM32 嵌入式设计】PS2索尼游戏手柄解析和代码开发

基于PS2索尼游戏手柄开发

最近师妹拿了个PS2手柄给我

在这里插入图片描述
安排!!!!
代码下载链接
在这里插入图片描述
在32上面 成功用PS2 控制显示屏输出

PS2是一个很好学习通讯时序的的工具
这里写下他的实现代码和我的学习思路

123456789
DI/DATDO/CMDNCVDDGNDCS/SELCLKNCACK

我们不使用 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;
	}
}

  • 8
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-ATAO----

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值