PS2手柄介绍
PS2手柄作为一种经典且常见的控制器,具有良好的手感和丰富的按键,适合用来控制STM32进行机器人、遥控车等项目。
手柄使用 SPI 串行通信,在本项目中使用软件模拟SPI,实现单片机与数字 摇杆手柄之间的通讯
PS2手柄连接STM32
1、硬件准备
PS2手柄、stm32f103c8t6最小系统板,oled使用江科大的例程
2、引脚说明与接线
我的接收器有六个引脚,从左往右依次为DI,DO,GND,VCC,CS,CLK;
DI/DAT : 信号流向 , 从手柄到主机 , 此信号是一个 8bit 的串行数据 , 同步传送于时钟 的下降沿。信号的读取在时钟由高到低的变化过程中完成。DO/CMD : 信号流向 , 从主机到手柄 , 此信号和 DI 相对 , 信号是一个 8bit 的串行数据 ,同步传送于时钟的下降沿。GND : 电源地 ;VDD : 接收器工作电源 , 电源范围 3~5V ;CS/SEL : 用于提供手柄触发信号。在通讯期间, 处于低电平 ;CLK : 时钟信号 , 由主机发出, 用于保持数据同步;ACK : 从手柄到主机的应答信号。此信号在每个 8bits 数据发送的最后一个周期变低并且 CS 一直保持低电平 , 如果 CS 信号不变低 , 约 60 微秒 PS 主机会试另一个外设。在编程 时未使用 ACK 端口。
通讯时序
SPI通信时序,我们使用软件模拟SPI,在CLK上升沿进行数据交换,DO发送数据,DI接收数据
接线:CS----->PA4 CLK-------->PA5 DI------->PA6 DO------->PA7
3、PS2手柄通信原理

这是ps2手柄通信的一帧数据,在这一帧数据中包含九个字节,在DO发送的一帧数据中,
当主机想读手柄数据时
,
将会拉低
CS
线电平
,
并发出一个命令
“
0x01
”;
手柄会回复
它的
ID
“
0x41=
模拟绿灯
,
0x73=
模拟红灯
”;
在手柄发送
ID
的同时
,
主机将传送
0x42
,
请求数据
;
随后手柄发送出
0x5A
,
告诉主机
“
数据来了
”
。
idle
: 数据线空闲,
该数据线无数据传送。
在DI接收的一帧数据中,包含手柄数据的是后六个字节,我们建立一个长度为9的Data[9]来接收DI数据,则Data[3]~Data[8]是我们需要解析的手柄数据,
当有按键按下
,
对应位为
“
0
”,
其他位为
“
1
”,
例如当键
“
SELECT
”
被按下时,
Data[3]=11111110
4、程序设计
#include "PS_2.h"
#include "stm32f10x.h" // Device header
/*********************************************************
**********************************************************/
unsigned int Handkey; //定义按键
uint8_t ps2_mode;
uint8_t Comd[9]={0x01,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //发送命令DO的数组
uint8_t Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //接收数据DI的数组
uint8_t key_search[12] = {0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00};
unsigned int MASK[16][2]={
{PSB_SELECT,0},
{PSB_L3,0},
{PSB_R3,0},
{PSB_START,0},
{PSB_PAD_UP,0},
{PSB_PAD_RIGHT,0},
{PSB_PAD_DOWN,0},
{PSB_PAD_LEFT,0},
{PSB_L2,0},
{PSB_R2,0},
{PSB_L1,0},
{PSB_R1 ,0},
{PSB_GREEN,0},
{PSB_RED,0},
{PSB_BLUE,0},
{PSB_PINK,0}
}; //定义按键键码
void PS2_Init(void)
{
PS2_GPIO_Init();//手柄GPIO初始化,SPI引脚GPIO初始化
PS2_SCK(1);
PS2_CS(1);
PS2_ShortPoll();
PS2_EnterConfing(); //进入手柄配置模式
PS2_TurnOnAnalogMode(); //红灯或绿灯配置模式,并选择是否保存
//PS2_VibrationMode(); //手柄震动马达设置
PS2_ExitConfing(); //保存配置并退出
ps2_mode_get(); //获取手柄模式
}
//发送命令
unsigned char PS2_Cmd(unsigned char ByteSend)
{
unsigned char i,j=1;
unsigned char ByteReceive=0x00;
for (i = 0; i < 8; i++)
{
PS2_DO(ByteSend & (0x01 << i));//将数据一位一位的发送,不理解的可以举个例子算一下
PS2_SCK(0);
PS2_Delay_US(100);
if(PS2_DI())
{
ByteReceive=ByteReceive+j;
}
j=j<<1;
PS2_SCK(1);
PS2_Delay_US(1);
}
return ByteReceive;
}
//读取手柄数据,核心函数
void PS2_ReadData(void)
{
unsigned char byte=0;
PS2_CS(0);
PS2_Delay_US(10);
for(byte = 0; byte < 9; byte++)
{
Data[byte] = PS2_Cmd(Comd[byte]);
}
PS2_CS(1);
PS2_Delay_US(10);
}
unsigned char ps2_mode_get(void)
{
if( Data[1] == 0X73)
{
ps2_mode = PSB_REDLIGHT_MODE;
}
else if ( Data[1] == 0X41)
{
ps2_mode = PSB_GREENLIGHT_MODE;
}
else
{
ps2_mode = PSB_LOSE;
}
return ps2_mode;
}
unsigned char ps2_key_serch()
{
unsigned char index;
unsigned char key_num = 0;
//PS2_ClearData();
PS2_ReadData();
Handkey=(Data[4]<<8)|Data[3];
for(index=4;index<16;index++)
{
if((Handkey&(1<<(MASK[index][0]-1)))==0)
{
MASK[index][1] = 1;
key_num++;
}
else
{
MASK[index][1] = 0;
}
}
return key_num;
}
unsigned char ps2_get_key_state(unsigned char key_id)
{
if (key_id < PSB_SELECT)
return 0;
else
return MASK[key_id - 1][1];
}
unsigned char ps2_get_anolog_data(unsigned char button)
{
return Data[button];
}
int rocker_value(unsigned char button)
{
unsigned char get_value;
int value;
get_value=ps2_get_anolog_data(button);
if(button==5)
{
value=128-get_value;
}
if(button==6)
{
value=127-get_value;
}
if(button==7)
{
value=128-get_value;
}
if(button==8)
{
value=127-get_value;
}
return value;
}
//Çå³ýÊý¾Ý»º³åÇø
void PS2_ClearData()
{
unsigned char a;
for(a=0;a<9;a++)
Data[a]=0x00;
}
void PS2_Vibration(unsigned char motor1, unsigned char motor2)
{
PS2_CS(0);
PS2_Delay_US(100);
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);
PS2_CS(1);
PS2_Delay_US(100);
}
//short poll
void PS2_ShortPoll(void)
{
PS2_CS(0);
PS2_Delay_US(100);
PS2_Cmd(0x01);
PS2_Cmd(0x42);
PS2_Cmd(0X00);
PS2_Cmd(0x00);
PS2_Cmd(0x00);
PS2_CS(1);
PS2_Delay_US(100);
}
//½øÈëÅäÖÃ
void PS2_EnterConfing(void)
{
PS2_CS(0);
PS2_Delay_US(100);
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);
PS2_CS(1);
PS2_Delay_US(100);
}
void PS2_TurnOnAnalogMode(void)
{
PS2_CS(0);
PS2_Delay_US(100);
PS2_Cmd(0x01);
PS2_Cmd(0x44);
PS2_Cmd(0X00);
PS2_Cmd(0x01);
PS2_Cmd(0xEE);
PS2_Cmd(0X00);
PS2_Cmd(0X00);
PS2_Cmd(0X00);
PS2_Cmd(0X00);
PS2_CS(1);
PS2_Delay_US(100);
}
//Õñ¶¯ÉèÖÃ
void PS2_VibrationMode(void)
{
PS2_CS(0);
PS2_Delay_US(100);
PS2_Cmd(0x01);
PS2_Cmd(0x4D);
PS2_Cmd(0X00);
PS2_Cmd(0x00);
PS2_Cmd(0X01);
PS2_CS(1);
PS2_Delay_US(100);
}
void PS2_ExitConfing(void)
{
PS2_CS(0);
PS2_Delay_US(100);
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);
PS2_CS(1);
PS2_Delay_US(100);
}
void PS2_GPIO_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void PS2_CS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}
void PS2_SCK(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}
void PS2_DO(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}
uint8_t PS2_DI()
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}
void PS2_Delay_US(uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
PS_2.c
#ifndef __PS_2_H__
#define __PS_2_H__
#include "stdint.h"
#define ANALOG_CENTER 0x80
#define PSB_LOSE 0
#define PSB_REDLIGHT_MODE 1
#define PSB_GREENLIGHT_MODE 2
#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 16
//#define WHAMMY_BAR 8
//These are stick values
#define PSS_RX 5
#define PSS_RY 6
#define PSS_LX 7
#define PSS_LY 8
#define PSS_LX_MID 0X80
#define PSS_LY_MID 0X7F
#define PSS_RX_MID 0X7F
#define PSS_RY_MID 0X80
extern unsigned char Data[9];
extern unsigned int MASK[16][2];
extern unsigned int Handkey;
/**/ void PS2_Init(void);
/**/ unsigned char ps2_mode_get(void);
/**/ void PS2_ReadData(void);
/**/ unsigned char PS2_Cmd(unsigned char CMD);
/**/ unsigned char ps2_key_serch(void);
/**/ unsigned char ps2_get_key_state(unsigned char key_id);
/**/ unsigned char ps2_get_anolog_data(unsigned char button);
int rocker_value(unsigned char button);
void PS2_ClearData(void);
void PS2_Vibration(unsigned char motor1, unsigned char motor2);
void PS2_EnterConfing(void);
void PS2_TurnOnAnalogMode(void);
void PS2_VibrationMode(void);
void PS2_ExitConfing(void);
void PS2_SetInit(void);
void PS2_ShortPoll(void);
void PS2_CS(uint8_t BitValue);
void PS2_SCK(uint8_t BitValue);
void PS2_DO(uint8_t BitValue);
void PS2_DI_H(void);
void PS2_DI_L(void);
uint8_t PS2_DI(void);
void PS2_Delay_US(uint32_t nCount);
void PS2_GPIO_Init(void);
#endif
PS_2.h
通过网盘分享的文件:PS2_Test.zip
链接: https://pan.baidu.com/s/1WpG4rWLI3GtejETR_nqz1g?pwd=r9w5 提取码: r9w5