USB HID之Gamepad游戏手柄代码生成流程(基于STM32F407ZG,附源码)

前提:

准备一个STM32F407ZG的探索者开发板,一个双轴按键摇杆传感器模块(可自行在某宝上搜索)。

游戏手柄点位控制图:

实验目的:

利用USB外设实现游戏手柄的简易功能,通过USB将设备与电脑连接,简单控制小游戏。本次实验采用5个按键分别控制游戏手柄的X键、Y键、A键、B键、右侧摇杆键,使用两路ADC检测右侧摇杆的x坐标和y坐标,进而控制前后左右。

一、使用STM32CubeMX生成HID代码

1.1.打开STM32CubeMX,并点击ACCESS TO MCU SELECTOR

1.2.选择芯片型号

1.3.添加5个按键(暂时先用5个测试),依据原理图配置上下拉,分别用于控制游戏手柄的 X键、Y键、A键、B键、右操作摇杆键。

1.4.配置RCC,选择外部晶振作为时钟源

1.5.配置两路ADC(暂时先用2路测试)

1.6.选择USB接口

1.7.配置USB设备

1.8.配置时钟

1.9.配置Project Manager

1.10.生成工程

二、修改usbd_hid.c

2.1.将默认的报告描述符改成需要的游戏手柄报告描述符:找到usbd_hid.c中的HID_MOUSE_ReportDesc数组,并按需求将以下代码将其替换:

__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x05,                    // USAGE (Game Pad)
    0xa1, 0x01,                    // COLLECTION (Application)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x10,                    //     USAGE_MAXIMUM (Button 16)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x10,                    //     REPORT_COUNT (16)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x32,                    //     Usage (Z)
    0x09, 0x33,                    //     Usage (Rx)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //     LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //     END_COLLECTION
    0xc0                           // END_COLLECTION
};

替换结果: 

2.2.将HID_MOUSE_REPORT_DESC_SIZE改成HID_MOUSE_ReportDesc的实际大小

三、修改main.c

在main.c中增加编写按键扫描与处理以及ADC通道值获取功能并调用,最后将获取的按键值和8位的ADC值发送到主机,具体代码如下:

#include "main.h"
#include "adc.h"
#include "usb_device.h"
#include "gpio.h"
#include "usbd_hid.h"

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

extern USBD_HandleTypeDef hUsbDeviceFS;

/* KEY端口定义 */
#define KEY_X           HAL_GPIO_ReadPin(KEY_X_GPIO_Port, KEY_X_Pin)     
#define KEY_Y           HAL_GPIO_ReadPin(KEY_Y_GPIO_Port, KEY_Y_Pin)     
#define KEY_A           HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin)   
#define KEY_B           HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin)
#define KEY_RIGHT       HAL_GPIO_ReadPin(KEY_RIGHT_GPIO_Port, KEY_RIGHT_Pin)

#define KEYX_PRES       1              /* KEY_X 按下 */
#define KEYY_PRES       2              /* KEY_Y 按下 */
#define KEYA_PRES       3              /* KEY_A 按下 */
#define KEYB_PRES       4              /* KEY_B 按下 */
#define KEYRIGHT_PRES   5              /* KEY_RIGHT 按下*/

/*
* sendbuf[0]~sendbuf[1]:按键
* sendbuf[2]:左侧摇杆x坐标
* sendbuf[3]:左侧摇杆y坐标
* sendbuf[4]:右侧摇杆x坐标
* sendbuf[5]:右侧摇杆y坐标
*/
unsigned char sendbuf[6]={0x00,0x00,0x80,0x80,0x80,0x80};

uint8_t key_scan(void)
{
    uint8_t keyval = 0;

    if (KEY_X == 0)  keyval = KEYX_PRES;
    if (KEY_Y == 1)  keyval = KEYY_PRES;
    if (KEY_A == 0)  keyval = KEYA_PRES;
    if (KEY_B == 0)  keyval = KEYB_PRES;
    if (KEY_RIGHT == 0) keyval = KEYRIGHT_PRES;

    return keyval;              /* 返回键值 */
}
void key_handle(void)
{
    uint8_t key;
    
    sendbuf[0] = 0x00;
    sendbuf[1] = 0x00;
    
    key = key_scan();
    switch (key)
    {
        case KEYY_PRES:          //up       
        {
            sendbuf[0] = 0x08;
            break;
        }
        case KEYA_PRES:         //down    
        {
            sendbuf[0] = 0x01;
            break;
        }
        case KEYX_PRES:         //left         
        {
            sendbuf[0] = 0x04;
            break;
        }
        case KEYB_PRES:         //right            
        {
            sendbuf[0] = 0x02;
            break;
        }
        case KEYRIGHT_PRES:     //右操作摇杆键
        {
            sendbuf[1] = 0x08;
            break;
        }
        default: break;
    }
}

void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    /* 配置对应ADC通道 */
    ADC_ChannelConfTypeDef adc_channel;
    adc_channel.Channel = ch;               /* 设置ADCX对通道ch */
    adc_channel.Rank = rank;                /* 设置采样序列 */
    adc_channel.SamplingTime = stime;       /* 设置采样时间 */
    HAL_ADC_ConfigChannel( adc_handle, &adc_channel);   
}

uint32_t adc_get_result(uint32_t ch)
{
    adc_channel_set(&hadc1, ch, 1, ADC_SAMPLETIME_480CYCLES);   /* 设置通道,序列和采样时间 */
    HAL_ADC_Start(&hadc1);                                       /* 开启ADC */
    HAL_ADC_PollForConversion(&hadc1, 10);                       /* 轮询转换 */

    return (uint16_t)HAL_ADC_GetValue(&hadc1);                   /* 返回最近一次ADC1规则组的转换结果 */
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC1_Init();
    MX_USB_DEVICE_Init();
    while (1)
    {
        key_handle();
        sendbuf[4] = (uint8_t)adc_get_result(ADC_CHANNEL_10);
        sendbuf[5] = (uint8_t)adc_get_result(ADC_CHANNEL_11);
        USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&sendbuf,sizeof(sendbuf));
    }
}

四、测试功能

在线测试点击:游戏手柄测试

实验现象:使用USB连接线将设备与电脑相连,点击 游戏手柄测试,按下任意按键或者摇动右侧摇杆,可看到网页中相应的按键或摇杆会跟随实际游戏手柄动作而动作。

程序源码:

通过GitHub分享:GitHub - Freddysssss/STM32F407ZG_USB_HID_Gamepad

通过网盘分享的文件:STM32F407ZG_USB_HID_Gamepad.rar
链接: https://pan.baidu.com/s/1QkR1bPzMPpaE-9A2zF6j3A?pwd=gw6u 提取码: gw6u

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值