STM32 USB Host 鼠标和键盘驱动 -- 原创

本文详细介绍了STM32 USB主机环境下,如何驱动鼠标和键盘。针对鼠标,由于其特殊报文格式,需要修改`usbh_hid_mouse.c`和`usbh_hid_mouse.h`文件,调整结构体并更新接收缓冲区。对于键盘,主要是修改解析函数以处理多按键情况,并通过历史记录来确定最新按键。提供的驱动适用于特定类型的鼠标和键盘,可作为参考。

STM32 USB 主机的鼠标和键盘驱动

本文基于 <STM32 USB Host 连接鼠标和键盘>样例, 继续详细说明鼠标和键盘的驱动.
首先介绍鼠标, 当前鼠标的格式有很多有先发送按键的有先发送移动的, 我的鼠标是先发送按键的, 且是鼠标键盘公用一个USB接口, 然后键盘的特殊字符(例如调节音量, 播放控制等)通过鼠标发送, 当为鼠标报文时, 第一个字符为1, 当发送键盘特殊报文时, 第一个字符为0, 具体格式是:

  1. 鼠标报文
地址说明
[0]报文类型, 1为鼠标报文, 0为键盘特殊字符报文
[1]按键状态, 包括0位左键, 1位右键,2位中键
[2]X轴移动值
[3]Y轴移动值
[4]滚轮旋转值
  1. 键盘特殊报文
地址说明
[0]报文类型, 1为鼠标报文, 0为键盘特殊字符报文
[1]键盘特殊字符
[2]
[3]
[4]

这种格式与usbh_hid_mouse.c中给的格式完全不同, 所以要对整个文件都要修改.

  1. usbh_hid_mouse.h

因为当前鼠标多了一个滚轮和中键, 所以要修改结构体HID_MOUSE_Info_TypeDef:

typedef struct _HID_MOUSE_Info
{
/* [ */
  uint8_t   type;
  uint8_t   buttons[3];
/* ] */
  int8_t    x;
  int8_t    y;
/* [ */
  int8_t    z;
  uint8_t   key;
/* ] */
}
HID_MOUSE_Info_TypeDef;
  1. usbh_hid_mouse.c

因为我的鼠标报文最大5个字节, 而默认是4个字节, 所以我把接收缓冲区改成了数组


/** @defgroup USBH_HID_MOUSE_Private_Variables
  * @{
  */
HID_MOUSE_Info_TypeDef    mouse_info;
uint32_t                  mouse_report_data[2];
uint32_t                  mouse_rx_report_buf[2]; // 这个buf在最新的USBH库中已经添加, 但不是数组, 之前是需要自己创建的, 防止接收与提取的buf冲突

然后是添加和修改按键和移动数据的顺序:

/* Structures defining how to access items in a HID mouse report */
/* Access message status. */
static const HID_Report_ItemTypedef prop_type = {
  (uint8_t *)(void *)mouse_report_data + 0, /*data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFF,  /*max value read can return*/
  0,     /*min vale device can report*/
  0xFF,  /*max value device can report*/
  1      /*resolution*/
};

/* Access the special key, when the message status is 3. */
static const HID_Report_ItemTypedef prop_key = {
  (uint8_t *)(void *)mouse_report_data + 1, /*data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFF,  /*max value read can return*/
  0,     /*min vale device can report*/
  0xFF,  /*max value device can report*/
  1      /*resolution*/
};

/* Access button 1 state. */
static const HID_Report_ItemTypedef prop_b1 = {
  (uint8_t *)(void *)mouse_report_data + 1, /*data*/
  1,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  0,     /*signed?*/
  0,     /*min value read can return*/
  1,     /*max value read can return*/
  0,     /*min value device can report*/
  1,     /*max value device can report*/
  1      /*resolution*/
};

/* Access button 2 state. */
static const HID_Report_ItemTypedef prop_b2 = {
  (uint8_t *)(void *)mouse_report_data + 1, /*data*/
  1,     /*size*/
  1,     /*shift*/
  0,     /*count (only for array items)*/
  0,     /*signed?*/
  0,     /*min value read can return*/
  1,     /*max value read can return*/
  0,     /*min value device can report*/
  1,     /*max value device can report*/
  1      /*resolution*/
};

/* Access button 3 state. */
static const HID_Report_ItemTypedef prop_b3 = {
  (uint8_t *)(void *)mouse_report_data + 1, /*data*/
  1,     /*size*/
  2,     /*shift*/
  0,     /*count (only for array items)*/
  0,     /*signed?*/
  0,     /*min value read can return*/
  1,     /*max value read can return*/
  0,     /*min vale device can report*/
  1,     /*max value device can report*/
  1      /*resolution*/
};

/* Access x coordinate change. */
static const HID_Report_ItemTypedef prop_x = {
  (uint8_t *)(void *)mouse_report_data + 2, /*data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFF,  /*max value read can return*/
  0,     /*min vale device can report*/
  0xFF,  /*max value device can report*/
  1      /*resolution*/
};

/* Access y coordinate change. */
static const HID_Report_ItemTypedef prop_y = {
  (uint8_t *)(void *)mouse_report_data + 3, /*data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFF,  /*max value read can return*/
  0,     /*min vale device can report*/
  0xFF,  /*max value device can report*/
  1      /*resolution*/
};

/* Access z coordinate change. */
static const HID_Report_ItemTypedef prop_z = {
  (uint8_t *)(void *)mouse_report_data + 4, /*data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFF,  /*max value read can return*/
  0,     /*min vale device can report*/
  0xFF,  /*max value device can report*/
  1      /*resolution*/
};

接下来是修改初始化函数USBH_HID_MouseInit,
就是对刚修改的数组初始化, 并将地址存到HID_Handle->pData

/**
  * @brief  USBH_HID_MouseInit
  *         The function init the HID mouse.
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_HID_MouseInit(USBH_HandleTypeDef *phost)
{
  uint32_t i;
  HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData[phost->pActiveClass->CurrInterface];

  mouse_info.x = 0U;
  mouse_info.y = 0U;
  mouse_info.z = 0U;
  mouse_info.buttons[0] = 0U;
  mouse_info.buttons[1] = 0U;
  mouse_info.buttons[2] = 0U;

  for (i = 0U; i < (sizeof(mouse_report_data) / sizeof(uint32_t)); i++)
  {
    mouse_report_data[i] = 0U;
    mouse_rx_report_buf[i] = 0U;
  }

  if (HID_Handle->length > sizeof(mouse_report_data))
  {
    HID_Handle->length = sizeof(mouse_report_data);
  }
  HID_Handle->pData = (uint8_t *)(void *)mouse_rx_report_buf;
  USBH_HID_FifoInit(&HID_Handle->fifo, phost->device.Data, HID_QUEUE_SIZE * sizeof(mouse_report_data));

  return USBH_OK;
}

最后是修改接收报文的解析函数USBH_HID_MouseDecode

/**
  * @brief  USBH_HID_MouseDecode
  *         The function decode mouse data.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_HID_MouseDecode(USBH_HandleTypeDef *phost)
{
  HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData[phost->pActiveClass->CurrInterface];

  if (HID_Handle->length == 0U)
  {
    return USBH_FAIL;
  }
  /*Fill report */
  if (USBH_HID_FifoRead(&HID_Handle->fifo, &mouse_report_data, HID_Handle->length) ==  HID_Handle->length)
  {
    /*Decode report */
    mouse_info.type           = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_type, 0U);
    if (mouse_info.type == 1) {
        mouse_info.buttons[0] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_b1, 0U);
        mouse_info.buttons[1] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_b2, 0U);
        mouse_info.buttons[2] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_b3, 0U);
        mouse_info.x          = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_x, 0U);
        mouse_info.y          = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_y, 0U);
        mouse_info.z          = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_z, 0U);
    } else {
        mouse_info.key        = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_key, 0U);
    }

    return USBH_OK;
  }
  return   USBH_FAIL;
}

这样鼠标驱动就完成了, 剩下的就是解析完以后的HID回调函数要做的事了, 例如向emwin发送鼠标事件或自己写鼠标事件, 这些就跟应用强相关了

然后是键盘驱动, 键盘格式基本都是通用的, 只有一些特殊字符不同, 主要就是修改解析函数就行了,
USBH_HID_KeybdDecode
键盘的数据解析主要就是在同时按下多个按键的时候比较麻烦, 如果是一指禅的话, 库自带的驱动是可以使用的. 我是想把键盘当做重要的输入工具处理的, 打字、输入命令啥的, 所以还是需要对多按键进行处理的, 主要思路是通过history数组记录上次输入的按键, 然后比较这次和上次的不同, 得到最新按下的按键

uint8_t keybd_len = 0;
uint8_t history[6] = { 0 };
uint8_t history_len = 0;
uint8_t capsLk_led = 0;
/**
  * @brief  USBH_HID_KeybdDecode
  *         The function decode keyboard data.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_HID_KeybdDecode(USBH_HandleTypeDef *phost)
{
    USBH_StatusTypeDef status = USBH_FAIL;
    uint8_t x;
    uint8_t len = 1;
    HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *)phost->pActiveClass->pData[phost->pActiveClass->CurrInterface];
    if (HID_Handle->length == 0U)
        return USBH_FAIL;
    /*Fill report */
    if (USBH_HID_FifoRead(&HID_Handle->fifo, &keybd_report_data, HID_Handle->length) == HID_Handle->length) {
        keybd_info.lctrl  = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_lctrl,  0U);
        keybd_info.lshift = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_lshift, 0U);
        keybd_info.lalt   = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_lalt,   0U);
        keybd_info.lgui   = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_lgui,   0U);
        keybd_info.rctrl  = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_rctrl,  0U);
        keybd_info.rshift = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_rshift, 0U);
        keybd_info.ralt   = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_ralt,   0U);
        keybd_info.rgui   = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_rgui,   0U);

        for (x = 0U; x < sizeof(keybd_info.keys); x++) {
            keybd_info.keys[x] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)&imp_0_key_array, x);
            if (keybd_info.keys[x])
                len = x + 1;
        }
        keybd_len = len;
        if ((keybd_len == history_len == 1) &&
            (keybd_info.keys[0] == history[0]))
            return USBH_FAIL;
        if (keybd_len >= history_len) {
            if (find_new_value(history, &history_len, keybd_info.keys, keybd_len) == 1)
                memcpy(keybd_info.keys, history, keybd_len);
            if (keybd_info.keys[keybd_len - 1] == KEY_CAPS_LOCK)
                capsLk_led = ~capsLk_led;
            status = USBH_OK;
        } else if ((keybd_len == 1) && (keybd_info.keys[0] == 0)) {
            memset(history, 0, 6);
            history_len = keybd_len;
        }
        if ((keybd_info.keys[0] != 0) && (status == USBH_FAIL)) {
            memcpy(history, keybd_info.keys, keybd_len);
            history_len = keybd_len;
        }
    }
    return status;
}

查找新按键函数


static int find_new_value(uint8_t *out, uint8_t *out_len, const uint8_t *in, uint8_t in_len)
{
    int res;
    int n, m;

    if (in_len < *out_len)
        return -1;
    if (in_len == *out_len == 1) {
        out[0] = in[0];
        return 0;
    }
    for (n = 0; n < in_len; n++) {
        res = 0;
        for (m = 0; m < *out_len; m++) {
            if (out[m] == in[n]) {
                res |= 1;
                break;
            }
        }
        if (res == 0)
            out[(*out_len)++] = in[n];
        if (in_len == *out_len)
            break;
    }
    return 1;
}

最后是返回当前按键值USBH_HID_GetASCIICode, 最新值储存在数组末尾, 并根据Shift和capsLK状态输出不同值
USBH_HID_GetKeyCode直接返回按键值, 主要是检测是否为功能按键

/**
  * @brief  USBH_HID_GetASCIICode
  *         The function decode keyboard data into ASCII characters.
  * @param  phost: Host handle
  * @param  info: Keyboard information
  * @retval ASCII code
  */
uint8_t USBH_HID_GetASCIICode(HID_KEYBD_Info_TypeDef *info)
{
    uint8_t output;
    uint8_t keys = info->keys[keybd_len - 1];
    if ((info->lshift == 1U) || (info->rshift == 1U)) {
        if ((capsLk_led) &&
            ((keys >= KEY_A) &&
            (keys <= KEY_Z)))
            output = HID_KEYBRD_Key[HID_KEYBRD_Codes[keys]];
        else
            output = HID_KEYBRD_ShiftKey[HID_KEYBRD_Codes[keys]];
    } else {
        if ((capsLk_led) &&
            ((keys >= KEY_A) &&
            (keys <= KEY_Z)))
            output = HID_KEYBRD_ShiftKey[HID_KEYBRD_Codes[keys]];
        else
            output = HID_KEYBRD_Key[HID_KEYBRD_Codes[keys]];
    }
    return output;
}

uint8_t USBH_HID_GetKeyCode(HID_KEYBD_Info_TypeDef *info)
{
    uint8_t output;

    output = info->keys[keybd_len - 1];
    return output;
}

此驱动并不适用所有键盘、鼠标, 仅做参考.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值