【STM32F4系列】【HAL库】【自制库】ps2手柄模块驱动

21 篇文章 10 订阅
20 篇文章 28 订阅

外观和电气连接

外观

手柄外观如下

在这里插入图片描述

在这里插入图片描述

接收器外观

这是接收器和底座

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4iYzkU5O-1672150811066)(图片/3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmF2PFAo-1672150811067)(图片/4.png)]

电气连接

需要4根连接线

单片机输出是CLK DO CS

单片机输入是DI

电源电压是3.3-5v

注意模块和单片机共地

模块不支持高速,最大时钟周期约为4us左右

因此使用软件模拟时序的方式来与模块通信

只需要将模块的4根线与单片机软件中对应的GPIO连接起来就行了

#define PS2_CS_GPIOx GPIOA
#define PS2_CS_Pin GPIO_PIN_4

#define PS2_CLK_GPIOx GPIOA
#define PS2_CLK_Pin GPIO_PIN_5

#define PS2_DO_GPIOx GPIOA
#define PS2_DO_Pin GPIO_PIN_7

#define PS2_DI_GPIOx GPIOA
#define PS2_DI_Pin GPIO_PIN_6

软件设计

HAL初始化

需要 3个推挽输出模式的GPIO 分别是 CLK DO CS

1个上拉输入模式 DI

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnFxvz6d-1672150811067)(图片/5.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORM1j06t-1672150811068)(图片/6.png)]

协议分析

cs拉低开始单片机与模块的通信,拉高则通信结束

DO,DI在在CLK时钟的下降沿完成数据的发送和读取

本协议是类似SPI的协议

通信一共9个字节

这里的DODI指的是单片机的GPIO名称

Byte功能
0DO->0x01 DI <-(无意义)
1DO->0x42 DI<-(红灯0x73 无灯0X41)
2DO->0xFF DI<-0X5A
3-8DO->0xFF DI<-(按键数据)

按键数据如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IpAyuWLb-1672150811068)(图片/7.png)]

按键分析

手柄发送来的有两种模式的数据

无灯模式(MOD LED不亮)发送的摇杆是数字量,摇杆左右两侧的四向按键功能和摇杆重合

即如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTfNvHpV-1672150811069)(图片/8.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wJ8AWDsg-1672150811069)(图片/9.png)]

红灯模式(MODE LED亮红灯)

模块传输的是摇杆的模拟量

外侧大按键独立

代码

为了方便建立了一个结构体来存储按键的数据,代码有注释,请根据设备自行测试是哪个按键即可

typedef struct
{
    uint8_t A_D;                                       //模拟(红灯)为1 数字(无灯)为0
    int8_t Rocker_RX, Rocker_RY, Rocker_LX, Rocker_LY; //摇杆值(模拟状态为实际值0-0xFF)(数字态为等效的值0,0x80,0xFF)
    //按键值0为未触发,1为触发态
    uint8_t Key_L1, Key_L2, Key_R1, Key_R2;                //后侧大按键
    uint8_t Key_L_Right, Key_L_Left, Key_L_Up, Key_L_Down; //左侧按键
    uint8_t Key_R_Right, Key_R_Left, Key_R_Up, Key_R_Down; //右侧按键
    uint8_t Key_Select;                                    //选择键
    uint8_t Key_Start;                                     //开始键
    uint8_t Key_Rocker_Left, Key_Rocker_Right;             //摇杆按键

} PS2_TypeDef;
extern PS2_TypeDef PS2_Data;
#include "PS2.h"
uint8_t PS2_RawData[9] = {0};
PS2_TypeDef PS2_Data = {0};
void PS2_CS(uint8_t Val)
{
    if (Val)
        HAL_GPIO_WritePin(PS2_CS_GPIOx, PS2_CS_Pin, GPIO_PIN_SET);
    else
        HAL_GPIO_WritePin(PS2_CS_GPIOx, PS2_CS_Pin, GPIO_PIN_RESET);
}
void PS2_CLK(uint8_t Val)
{
    if (Val)
        HAL_GPIO_WritePin(PS2_CLK_GPIOx, PS2_CLK_Pin, GPIO_PIN_SET);
    else
        HAL_GPIO_WritePin(PS2_CLK_GPIOx, PS2_CLK_Pin, GPIO_PIN_RESET);
}
void PS2_DO(uint8_t Val)
{
    if (Val)
        HAL_GPIO_WritePin(PS2_DO_GPIOx, PS2_DO_Pin, GPIO_PIN_SET);
    else
        HAL_GPIO_WritePin(PS2_DO_GPIOx, PS2_DO_Pin, GPIO_PIN_RESET);
}
uint8_t PS2_Read_DI()
{
    return HAL_GPIO_ReadPin(PS2_DI_GPIOx, PS2_DI_Pin);
}
void PS2_Delay()
{
    for (int i = 0; i < 0xBf; i++)
        __nop();
}
uint8_t PS2_ReadWrite_Byte(uint8_t TxData)
{
    uint8_t TX = TxData;
    uint8_t RX = 0;
    for (int i = 0; i < 8; i++)
    {
        if (TX & 0x01)
            PS2_DO(1);
        else
            PS2_DO(0);
        TX >>= 1;
        PS2_CLK(1);
        PS2_Delay();
        PS2_CLK(0);
        RX >>= 1;
        RX |= (PS2_Read_DI() << 7);
        PS2_Delay();
        PS2_CLK(1);
        PS2_Delay();
    }
    return RX;
}

void PS2_Decode()
{
    if (PS2_RawData[2] == 0x5A)
    {
        PS2_Data.Key_Select = (~PS2_RawData[3] >> 0) & 0x01; //选择键
        PS2_Data.Key_Start = (~PS2_RawData[3] >> 3) & 0x01;  //开始键

        //左侧按键
        PS2_Data.Key_L_Up = (~PS2_RawData[3] >> 4) & 0x01;
        PS2_Data.Key_L_Right = (~PS2_RawData[3] >> 5) & 0x01;
        PS2_Data.Key_L_Down = (~PS2_RawData[3] >> 6) & 0x01;
        PS2_Data.Key_L_Left = (~PS2_RawData[3] >> 7) & 0x01;

        //后侧按键
        PS2_Data.Key_L2 = (~PS2_RawData[4] >> 0) & 0x01;
        PS2_Data.Key_R2 = (~PS2_RawData[4] >> 1) & 0x01;
        PS2_Data.Key_L1 = (~PS2_RawData[4] >> 2) & 0x01;
        PS2_Data.Key_R1 = (~PS2_RawData[4] >> 3) & 0x01;

        //右侧按键
        PS2_Data.Key_R_Up = (~PS2_RawData[4] >> 4) & 0x01;
        PS2_Data.Key_R_Right = (~PS2_RawData[4] >> 5) & 0x01;
        PS2_Data.Key_R_Down = (~PS2_RawData[4] >> 6) & 0x01;
        PS2_Data.Key_R_Left = (~PS2_RawData[4] >> 7) & 0x01;

        if (PS2_RawData[1] == 0x41)
        { //无灯模式(摇杆值八向)
            PS2_Data.Rocker_LX = 127 * (PS2_Data.Key_L_Right - PS2_Data.Key_L_Left);
            PS2_Data.Rocker_LY = 127 * (PS2_Data.Key_L_Up - PS2_Data.Key_L_Down);

            PS2_Data.Rocker_RX = 127 * (PS2_Data.Key_R_Right - PS2_Data.Key_R_Left);
            PS2_Data.Rocker_RY = 127 * (PS2_Data.Key_R_Up - PS2_Data.Key_R_Down);
        }
        else if (PS2_RawData[1] == 0x73)
        { //红灯模式(摇杆值模拟)

            //摇杆按键
            PS2_Data.Key_Rocker_Left = (~PS2_RawData[3] >> 1) & 0x01;
            PS2_Data.Key_Rocker_Right = (~PS2_RawData[3] >> 2) & 0x01;

            //摇杆值
            PS2_Data.Rocker_LX = PS2_RawData[7] - 0x80;
            PS2_Data.Rocker_LY = -1 - (PS2_RawData[8] - 0x80);
            PS2_Data.Rocker_RX = PS2_RawData[5] - 0x80;
            PS2_Data.Rocker_RY = -1 - (PS2_RawData[6] - 0x80);
        }
    }
}
void PS2_Read_Data(void)
{
    PS2_CS(0);
    PS2_RawData[0] = PS2_ReadWrite_Byte(0x01); // 0
    PS2_RawData[1] = PS2_ReadWrite_Byte(0x42); // 1
    for (int i = 2; i < 9; i++)
        PS2_RawData[i] = PS2_ReadWrite_Byte(0xff);
    PS2_CS(1);
    PS2_Decode();
}

手柄的效果还可以,对得起25元的价格

可以二次开发做成遥控车遥控器或者电脑手柄

成品

Github

  • 6
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值