学习PS2无线手柄解码通讯手册

news/2025/4/2 3:45:54/

学习 PS2 无线手柄的使用方法,将市场上 PS2 手柄通过解码应用到我们自己产品之中,比如控制智能车,机械臂等等任何涉及无线通信控制的一些diy场景。本次主要让大家了解 PS2 无线手柄的工作原理,以及掌握 PS2 无线手柄的使用并最终通过串口打印各按键的键值。

常见用途

diy 产品,舵机,寄存器 一些无线控制的设置和产品

手柄原理

ps2 由手柄与接收器两部分组成,手柄主要负责发送按键信息。都接通电源并打开手柄 开关时,手柄与接收器自动配对连接,在未配对成功的状态下,接收器绿灯闪烁,手柄上的 灯也会闪烁,配对成功后,接收器上绿灯常亮,手柄上灯也常亮,这时可以按“MODE” 键,选择手柄发送模式。
接收、、和主机(单片机)相连,实现主机与手柄之间的通讯。

实物参考如下图
在这里插入图片描述

接收器引脚输出,

123456789
DI/DATDO/CMDNCGNDVDDCS/SELCLKNCACK
数据命令空/不接通讯3.3V----时钟一般不接不接

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

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

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

3.NC:空端口。

4.GND:电源地。

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

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

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

8.NC:空端口。

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

在这里插入图片描述

	在时钟下降沿时,完成数据的发送与接收。

当主机想读手柄数据时,将会拉低 CS 线电平,并发出一个命令“0x01”; 手柄会回复 它的 ID“0x41=模拟绿灯,0x73=模拟红灯”;在手柄发送 ID 的同时,主机将传送 0x42,请求数据;随后手柄发送出 0x5A,告诉主机“数据来了”。idle:数据线空闲,改数据线无数据传送。
一共一个通讯周期有 9 个数据,这些数据是依次按为传送。
表 1:数据意义对照表!
在这里插入图片描述

当有按键按下,对应位为“0”,其他位为“1”,例如当键“SELECT”被按下时,Data[3]=11111110B,
红灯模式时:左右摇杆发送模拟值,0x00~0xFF(256) 之间的模拟量,且摇杆按下的键值值
L3、R3 有效;
绿灯模式时:左右摇杆模拟值为无效,推到极限时,对应发送 UP、RIGHT、DOWN、 LEFT、△、○、╳、□,按键 L3、R3 无效。

硬件连接部分

接收器与 stm32 连接方式
Dl—>PC13
DO—>PB14
CS—>OC15
CLK—>PB8

下面就是测试程序

完整程序详见工程文件。 这里主要介绍 ps2.c 文件中的函数。

void PS2_Init(void)
{//ÊäÈë  DI->PC13   		GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO,       ENABLE);GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_13;//PC13GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //ÏÂÀ­Ä£Ê½PWR_BackupAccessCmd(ENABLE);RCC_LSEConfig(RCC_LSE_OFF);BKP_TamperPinCmd(DISABLE);PWR_BackupAccessCmd(DISABLE);GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOC, &GPIO_InitStructure); 	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;//PB9GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); 	}

端口初始化

//向手柄发送命令

 void PS2_Cmd(u8 CMD)
{volatile u16 ref=0x01; for(ref=0x01;ref<0x0100;ref<<=1){if(ref&CMD){DO_H;	//输出一位控制位}else DO_L;CLK_H; //时钟拉高 delay_us(50);CLK_L; delay_us(50); CLK_H;}
}

//读取手柄数据

 void PS2_ReadData()
{volatile u8 byte=0; volatile u16 ref=0x01;//PS2 解码通讯CS_L;PS2_Cmd(Comd[0]); //开始命令PS2_Cmd(Comd[1]); //请求数据 for(byte=2;byte<9;byte++) //开始接受数据{for(ref=0x01;ref<0x100;ref<<=1){CLK_H;CLK_L; delay_us(50); CLK_H;if(DI)Data[byte] = ref|Data[byte];}delay_us(50);}CS_H;
}

上面两个函数分别为主机向手柄发送数据、手柄向主机发送数据。手柄向主机发送的数 据缓存在数组 Data[ ]中,数组中共有 9 个元素,每个元素的意义请见表 1。
//对读出来的 PS2 的数据进行处理 //按下为 0, 未按下为 1

u8 PS2_DataKey()
{u8 index; PS2_ClearData(); PS2_ReadData();Handkey=(Data[4]<<8)|Data[3]; //这是 16 个按键 按下为 0, 未按下为 1for(index=0;index<16;index++){if((Handkey&(1<<(MASK[index]-1)))==0) return index+1;}	return 0;	//没有任何按键按下
}	

8 位数 Data[3]与 Data[4],分别对应着 16 个按键的状态,按下为 0,未按下为 1。通过
对这两个数的处理,得到按键状态并返回键值。
编写主函数:
PS2 解码通讯

int main(void)
{u8 key;Stm32_Clock_Init(9); //系统时钟设置delay_init(72);	//延时初始化uart_init(72,9600);	//串口 1 初始化PS2_Init();	while(1)	{	key=PS2_DataKey();if(key!=0)	//有按键按下{	printf("	\r\n   %d  is  pressed  \r\n",key);}printf(" %5d %5d %5d %5d\r\n",PS2_AnologData(PSS_LX),PS2_AnologData(PSS_LY), PS2_AnologData(PSS_RX),PS2_AnologData(PSS_RY) );delay_ms(50);}
}

上面两个函数分别为主机向手柄发送数据、手柄向主机发送数据。手柄向主机发送的数 据缓存在数组 Data[ ]中,数组中共有 9 个元素,每个元素的意义请见表 1。

//对读出来的 PS2 的数据进行处理 //按下为 0, 未按下为 1

u8 PS2_DataKey()
{u8 index; PS2_ClearData(); PS2_ReadData();Handkey=(Data[4]<<8)|Data[3]; //这是 16 个按键 按下为 0, 未按下为 1 for(index=0;index<16;index++){if((Handkey&(1<<(MASK[index]-1)))==0)return index+1;}	return 0;	//没有任何按键按下
}	

当有按键按下时,输出按键值

4 下载与测试

编译程序并下载。按 ANALOG 可以改变模式,先选择红灯模式,遥控器上指示灯为红 色。串口输出的模拟值为 127 或 128,当晃动摇杆时,相应的模拟值就会改变,这时摇杆按 键可以按下,可以输出键值,见图 2。
在这里插入图片描述

在这里插入图片描述

图 2 按下“△”,输出对应的键值“13”。
在这里插入图片描述
图 3 按“ANALOG”,改为绿灯模式,手柄上指示灯变为“绿色”,串口输出的模拟值为

“255”,轻轻晃动摇杆,模拟值不变。

在这里插入图片描述

图 4 我们将右摇杆向上推到极限,这时串口输出“13 is pressed”,键值对应“△”,但模

拟的值不改变。
在这里插入图片描述

图 5 “红灯模式”和“绿灯模式”的主要区别就在与摇杆模拟值的输出。
在这里插入图片描述

测试案例2

ps2 函数

/*********************************************************
File:PS2驱动程序
Description: PS2驱动程序
**********************************************************/	 
u16 Handkey;
u8 Comd[2]={0x01,0x42};	//开始命令。请求数据
u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //数据存储数组
u16 MASK[]={PSB_SELECT,PSB_L3,PSB_R3 ,PSB_START,PSB_PAD_UP,PSB_PAD_RIGHT,PSB_PAD_DOWN,PSB_PAD_LEFT,PSB_L2,PSB_R2,PSB_L1,PSB_R1 ,PSB_GREEN,PSB_RED,PSB_BLUE,PSB_PINK};	//按键值与按键明void PS2_Init(void)
{//输入  DI->PB9   		GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable , ENABLE);GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_14;//PA14GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉模式//PWR_BackupAccessCmd(ENABLE);//RCC_LSEConfig(RCC_LSE_OFF);//BKP_TamperPinCmd(DISABLE);//PWR_BackupAccessCmd(DISABLE);GPIO_Init(GPIOA,&GPIO_InitStructure);//  DO->PA12    CS->PA8  CLK->PB9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); 	}//向手柄发送命令
void PS2_Cmd(u8 CMD)
{volatile u16 ref=0x01;Data[1] = 0;for(ref=0x01;ref<0x0100;ref<<=1){if(ref&CMD){DO_H;                   //输出以为控制位}else DO_L;CLK_H;                        //时钟拉高//delay_us(50);Delay_us(50);CLK_L;//delay_us(50);Delay_us(50);CLK_H;if(DI)Data[1] = ref|Data[1];}
}
//判断是否为红灯模式
//返回值;0,红灯模式
//		  其他,其他模式
u8 PS2_RedLight(void)
{CS_L;PS2_Cmd(Comd[0]);  //开始命令PS2_Cmd(Comd[1]);  //请求数据CS_H;if( Data[1] == 0X73)   return 0 ;else return 1;}
//读取手柄数据
void PS2_ReadData(void)
{volatile u8 byte=0;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){CLK_H;CLK_L;//delay_us(50);Delay_us(50);CLK_H;if(DI)Data[byte] = ref|Data[byte];}//delay_us(50);Delay_us(50);}CS_H;	
}//对读出来的PS2的数据进行处理      只处理了按键部分         默认数据是红灯模式  只有一个按键按下时
//按下为0, 未按下为1u8 PS2_DataKey()
{
//	static uint8 temp=1;u8 index;PS2_ClearData();PS2_ReadData();Handkey=(Data[4]<<8)|Data[3];     //这是16个按键  按下为0, 未按下为1for(index=0;index<16;index++){	    if((Handkey&(1<<(MASK[index]-1)))==0){//temp=(Handkey&(1<<(MASK[index]-1)));return index+1;}}return 0;          //没有任何按键按下
}//得到一个摇杆的模拟量	 范围0~255
u8 PS2_AnologData(u8 button)
{return Data[button];
}//清除数据缓冲区
void PS2_ClearData()
{u8 a;for(a=0;a<9;a++)Data[a]=0x00;
}
/**************************************************************************
函数功能:获取PS2无线手柄按键值,定时器每隔段时间进行读取PS2_DataKey()
入口参数:无
返回  值:无 
**************************************************************************/
void scan_ps2(void)
{if (flag_scan_ps2)   //定时时间到{flag_scan_ps2 = 0;key = PS2_DataKey();switch(key){case PSB_PAD_UP:CPWM[1]+=10;if(CPWM[1]>=2300)   CPWM[1]=2300;dj1+=10;if(dj1>=2200)dj1=2200;sprintf(buf,"#1P%dT1\r\n",dj1);UART_PutStr(USART3,buf);break; case PSB_PAD_DOWN:CPWM[1]-=10;if(CPWM[1]<=700)  CPWM[1]=700;dj1-=10;if(dj1<=700)dj1=700;sprintf(buf,"#1P%dT1\r\n",dj1);UART_PutStr(USART3,buf);break;case PSB_PAD_LEFT:CPWM[2]+=10;if(CPWM[2]>=2300) CPWM[2]=2300;dj2+=10;if(dj2>=2200)dj2=2200;sprintf(buf,"#2P%dT1\r\n",dj2);UART_PutStr(USART3,buf);break; case PSB_PAD_RIGHT:CPWM[2]-=10;if(CPWM[2]<=700) CPWM[2]=700;dj2-=10;if(dj2<=700)dj2=700;sprintf(buf,"#2P%dT1\r\n",dj2);UART_PutStr(USART3,buf);break;case PSB_TRIANGLE:CPWM[3]+=10;if(CPWM[3]>=2300) CPWM[3]=2300;dj3+=10;if(dj3>=2200)dj3=2200;sprintf(buf,"#3P%dT1\r\n",dj3);UART_PutStr(USART3,buf);break; case PSB_CROSS:CPWM[3]-=10;if(CPWM[3]<=700)  CPWM[3]=700;dj3-=10;if(dj3<=700)dj3=700;sprintf(buf,"#3P%dT1\r\n",dj3);UART_PutStr(USART3,buf);break;case PSB_PINK:CPWM[4]+=10;if(CPWM[4]>=2300)  CPWM[4]=2300;dj4+=10;if(dj4>=2200)dj4=2200;sprintf(buf,"#4P%dT1\r\n",dj4);UART_PutStr(USART3,buf);break; case PSB_CIRCLE:CPWM[4]-=10;if(CPWM[4]<=700) CPWM[4]=700;dj4-=10;if(dj4<=700)dj4=700;sprintf(buf,"#4P%dT1\r\n",dj4);UART_PutStr(USART3,buf);break;case PSB_L1:CPWM[5]+=10;if(CPWM[5]>=2300) CPWM[5]=2300;dj5+=10;if(dj5>=2200)dj5=2200;sprintf(buf,"#5P%dT1\r\n",dj5);UART_PutStr(USART3,buf);break; case PSB_L2:CPWM[5]-=10;if(CPWM[5]<=700)  CPWM[5]=700;dj5-=10;if(dj5<=700)dj5=700;sprintf(buf,"#5P%dT1\r\n",dj5);UART_PutStr(USART3,buf);break;case PSB_R1:CPWM[6]+=10;if(CPWM[6]>=2300) CPWM[6]=2300;dj6+=10;if(dj6>=2200)dj6=2200;sprintf(buf,"#6P%dT1\r\n",dj6);UART_PutStr(USART3,buf);break; case PSB_R2:CPWM[6]-=10;if(CPWM[6]<=700)  CPWM[6]=700;dj6-=10;if(dj6<=700)dj6=700;sprintf(buf,"#6P%dT1\r\n",dj6);UART_PutStr(USART3,buf);break;default:break;}}}

main 函数

uint16 CPWM[9]= {1500,1500,1500,1500,1500,1500,1500,1500,1500};
unsigned int dj1=1500;
unsigned int dj2=1500;
unsigned int dj3=1500;
unsigned int dj4=1500;
unsigned int dj5=1500;
unsigned int dj6=1600;char buf[30];
uint16 UartRec[9]; 			  //上位机字符串解析都放在这个数组里
unsigned char flag_uart1_rev=0;
unsigned char flag_uart2_rev=0;
char uart2_buf[255];
char uart1_buf[255];
unsigned char i=0;
u32 key, key_bak;
uint8 flag_vpwm=0;
unsigned char flag_scan_ps2;
void scan_ps2(void);
int main(void)
{   SysTick_Init();		//系统滴答定时器初始化 	Servor_GPIO_Config();		LED_Init();	      //LED 初始化函数Beep_Init();      //蜂鸣器初始化函数Beep_Test();      //蜂鸣器测试Led_Test(); Timer_Init();Timer_ON();PS2_Init();Uart_Init(1);	Uart_Init(3);	USART3_Config(115200);USART_Config(USART1,115200);while (1){	scan_ps2();}
} 

由于设备坏了,就不测试了,自己测试吧!!!1

快速学习Stm32舵机控制板控制多个舵机运动以及调速

文章来源:https://blog.csdn.net/qq_35653974/article/details/126177819
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/245977.html

相关文章

【Linux驱动开发】PS2游戏手柄驱动开发与输入子系统框架

文章目录 为什么需要输入子系统框架输入子系统框架驱动层事件处理层注册输入设备函数&#xff1a;注销输入设备函数&#xff1a;驱动实现——初始化(事件支持)驱动实现——报告事件驱动实现——报告结束关于事件报告的实现方法中断实现 常用于实体按键内核定时器实现&#xff0…

在linux下使用ps3手柄

PS&#xff1a;旧博文&#xff0c;发表于2017年4月17号&#xff0c;因为保留的是worldpress的xml。图片什么的就全没有了&#xff0c;另外我记得没错的话当初那个ps3手柄还是盗版的。正版的反而没问题。不过现在表示Xbox手柄真的好用。 在淘宝上购买了一个山寨的ps3手柄&#…

PS2手柄遥控Arduino小车

使用手柄遥控小车是经常要用到的&#xff0c;看到PS2手柄很6&#xff0c;就拿来尝试一下。 PS2手柄是索尼的PlayStation2游戏机的遥控手柄&#xff0c;因为这款手柄性价比较高&#xff0c;按键丰富&#xff0c;方便扩展到其它应用中&#xff0c;后来有人将其通讯协议破解&…

xbox手柄接收器驱动_Xbox精英手柄,对这款游戏手柄使用感受

创伟电子 --- 专业数码产品充电及数据传输解决方案商 在前段时间有一个打折卷要过期了&#xff0c;不知道买什么&#xff0c;后来充电姐看到了Xbox的精英手柄&#xff0c;于是就买了一个。首先得说明一下&#xff0c;其实我曾经也是一个非常沉迷于主机的这个游戏玩家&#xff0…

Micropython——使用PS2手柄实现远程遥控

文章目录 PS2遥控手柄遥控原理 接线代码PS2手柄库文件指令接收主程序函数解析主程序 按键对应数据图摇杆值对应的数组详解 PS2遥控手柄 此处使用的是较为低级便宜的PS2遥控手柄&#xff0c;不过可以实现绝大部分的控制基本功能&#xff0c;有想玩更丝滑的无级变速之类的小伙伴…

基于PS2手柄的Arduino遥控小车

前言 本文利用PS2手柄和Arduino开发板制作了一个简易的遥控小车&#xff0c;利用蓝牙进行通信&#xff0c;可以实现前后左右的移动。&#xff08;原理掌握之后可以自己拓展相关功能&#xff09; 一、零件 1.Arduino UNO开发板&#xff1a; ArduinoUNO是ArduinoUSB接口系列的…

ns手柄pc驱动_功能特点可以打满屏!北通宙斯白金版无线游戏手柄体验

不只是单纯的换壳&#xff0c;北通宙斯白金版游戏手柄针相对此前的纯黑版本&#xff0c;做了以下两点升级调整。首先是针对任天堂NS主机平台&#xff0c;已经可以做到直接通过蓝牙无线进行匹配连接&#xff0c;而不再需要底座插入接收器模块。其次&#xff0c;在2.4G的无线模式…

pc ps4手柄 驱动_《地平线:黎明时分》PC版性能表现分析

《地平线&#xff1a;黎明时分》是少有的移植到PC上的前PS4独占游戏&#xff0c;本作使用Decima引擎打造&#xff0c;利用了DX12 API&#xff0c;对AMD显卡功能有很好的的支持。外媒DSOGaming组织了一次性能测试&#xff0c;下面我们就来看看它在PC平台上的性能表现。 文章全文…