一 PS2 手柄介绍
PS2采用的是SPI通信协议,SPI是串行外设接口的缩写,是一种高速的、全双工、同步的通信总线,并且在芯片的管脚上只占用四根线(DI、DO、CS、CLK),节约了芯片的管脚,同时为PCB的布局上节省空间。
二 cubemx配置
1.RCC中打开HSE
外部晶振是8HZ,主频选择168Mhz
这里选择SPI3,是因为SPI3是挂载在APB1下的,这里可以仔细看看clock configuration的配置。然后prescaler选择256,最大分频,目的是为了降低速度。
我的主频是选择168MHz,尝试过如果选择SPI1,也就是挂载在APB2上的话,ps2手柄无法正常读取。(可能是速率太快)
PS2_SPI时序分析

2.看一个数据采集是发生在第一个跳变沿还是第二个跳变沿,发生在第一个跳变沿泽始终相位CPHA=0,第二个跳变沿则是CPHA = 1 。这里看蓝色箭头,发生在了第一个跳变沿。
3.判断低位先行(LSB)还是高位先行(MSB),这里只要看数据,一般都是8位,这里看红红色方框,一开始的是bit0,所以是低位先行。
0 1 2 3 4 5 6 7就是低位先行
7 6 5 4 3 2 1 0就是高位先行
CPOL = 1:空闲时,时钟线是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA = 0:数据在第1个跳变沿(下降沿)采样
判断方法:CS低电平时是工作状态,CS高电平则进入空闲状态,图中可以看出CS高电平时CLK是高电平,所以CPOL=1;(选择High)
第一个下降沿是指蓝色的箭头,通俗的意思就是DO和DI的移出和移入在CLK的第一个下降沿之前就是CPHA=0;否则就是看绿色的箭头CPHA=1;
再看红色的方框,bit0到bit7是从左到右,也是就低位先行LSB
模拟SPI读取分析
//读取手柄数据
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;
DELAY_TIME;
CLK_L;
DELAY_TIME;
CLK_H;
if(DI)
Data[byte] = ref|Data[byte];
}
delay_us(16);
}
CS_H;
}
硬件SPI
void PS2_Get(void) //接受ps2数据
{
short i = 0;
HAL_GPIO_WritePin(SPI3_CS_GPIO_Port,SPI3_CS_Pin,GPIO_PIN_RESET); //拉高,开始通讯
HAL_SPI_TransmitReceive(&hspi3,&cmd[0],&PS2data[0],1,0xffff); // 发送0x01,请求接受数据
vTaskDelay(1);
HAL_SPI_TransmitReceive(&hspi3,&cmd[1],&PS2data[1],1,0xffff); // 发送0x42,接受0x01(PS2表示开始通信)
vTaskDelay(1);
HAL_SPI_TransmitReceive(&hspi3,&cmd[2],&PS2data[2],1,0xffff); // 发送0x00,接受ID(红绿灯模式)
vTaskDelay(1);
for(i = 3;i <9;i++)
{
HAL_SPI_TransmitReceive(&hspi3,&cmd[2],&PS2data[i],1,0xffff); // 接受数据
vTaskDelay(1);
}
HAL_GPIO_WritePin(SPI3_CS_GPIO_Port,SPI3_CS_Pin,GPIO_PIN_SET); //拉低,准备下次通讯
}
void All_Button(void) //给每一个按键赋值,实现全按键无冲突
{
uint8_t loc = 1;
uint8_t set = 0;
uint8_t but = PS2data[3];
for(loc = 8;loc > 0;loc--) //位运算读取前八位
{
loc -= 1;
All_But[set] = (PS2data[3]&(1<<loc))>>loc;
loc += 1;
set++;
}
for(loc = 8;loc > 0;loc--) //位运算读取后八位
{
loc -= 1;
All_But[set] = (PS2data[4]&(1<<loc))>>loc;
loc += 1;
set++;
}
for(set = 0;set < 16;set++) //因为协议上按键按下为0,未按下为1,故需要对其进行反转
{
if(All_But[set] == 1) All_But[set] = 0;
else All_But[set] = 1;
}
}