linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标

文章目录

  • 前言
    • 本实验基于alinx Zynq 7010开发板。目的是通过usb_slave连接到PC上,让开发板作为一个鼠标从设备接入电脑,并可以通过linux上命令操作移动、点击鼠标等。
  • 一、内核配置
    • 1.petalinux
    • 2.修改设备树
  • 二、鼠标配置
    • 1.创建配置
    • 2.配置字符串
    • 3.配置功能项functions
      • protocol
      • report_desc
    • 4.配置config
    • 5.启用Gadget
    • 6.查看结果
    • 7.一键脚本
    • 8.连接电脑
  • 三、操作鼠标
  • 四、鼠标操作封装
    • 1.使用C语言进行封装
  • 总结


前言

本实验基于alinx Zynq 7010开发板。目的是通过usb_slave连接到PC上,让开发板作为一个鼠标从设备接入电脑,并可以通过linux上命令操作移动、点击鼠标等。

一、内核配置

1.petalinux

在PC上使用VMWare,在ubuntu下创建petalinux工程,编译内核,vmware、vivdado、petalinux的安装详见alinx官方教程course4-linux实验中的步骤
创建petalinux工程,在工程目录下打开终端,输入命令准备编译内核

source /opt/pkg/petalinux/settings.sh
source /opt/Xilinx/Vivado/2017.4/settings64.sh

linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第1张图片
然后编译内核:

petalinux-config -c kernel

linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第2张图片
进入Devicedrivers -> USB Support,勾选USB Gadget Support(按Y)
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第3张图片
进入USB Gadget Support,开启选项如下:
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第4张图片
编译内核:

petalinux-build

生成镜像

petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga --u-boot --force

将SD卡插入电脑连接到VMWare虚拟机,把petalinux工程中的images/linux中的BOOT.BIN和image.ub拷入SD卡的FAT分区中即可
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第5张图片
将SD卡插入开发板,开机进入linux,检查有无usb_gadget配置项

ls /sys/kernel/config

如果出现usb_gadget,说明配置成功,可以进行后续步骤

2.修改设备树

我在linux上进行ls /sys/class/udc时遇到问题,没有udc,因此又在网上查找资料,重新回去弄了半天,最后发现的解决办法是在devicetree中,把usb0的dr_mode=“host”改为peripheral,该方法来自AMD xilinx论坛,原问题petalinux-20164-usb-failed?
devicetree文件在(petalinux项目名)/project-spec/meta-user/recipes-bsp/device-tree/files里:
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第6张图片
修改:
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第7张图片

二、鼠标配置

1.创建配置

cd到usb_gadget的目录下

cd /sys/kernel/config/usb_gadget/

使用mkdir方法创建一项配置

mkdir xjs_gadget

进入创建的配置中查看

cd xjs_gadget
ls

可以看到如下结果:
在这里插入图片描述

2.配置字符串

字符串是当设备连接到主机上时,主机上显示的设备名称、生产商之类的信息。

以下是需要配置的部分字符串参数及其含义。其中idProduct和Vendor必须是4位十六进制数,如果想要开发的USB设备在连接主机时具有专属名称,可以向USB协会提交申请。如果只是用来测试也可以填我用的这个。而strings下的字符串可以按自己的需要填写。

xjs_gadget
├── idProduct=0xa4ac# 产品id
├── idVendor =0x03FD# 产品厂商id
└── strings  # 用于主机显示的相关文本
	└── 0x409# 语言标识符(EN)
		├── manufacturer ="flyingrt" #生产商名称
        ├── product      ="flyingrt_mouse"   #产品名称
        └── serialnumber ="0001"       #产品序列号

我的命令如下:

echo "0x03FD" > idVendor
echo "0xa4ac" > idProduct
mkdir strings/0x409
echo "flyingrtx" > strings/0x409/manufacturer 
echo "0001" > strings/0x409/serialnumber 

3.配置功能项functions

USB Gadget通过function配置实际的功能。每个function由protocol、subclass、report_desc、report_length这四项组成1。同一个USB Gadget可以通过一条线同时支持多个function(例如同时模拟鼠标和键盘)
首先建立功能项:

mkdir functions/hid.ms

使用ls查看,可以看到下面目录:
在这里插入图片描述

protocol

protocol指定HID设备使用的协议,对于键盘设备其值为1,鼠标设备其值为2。subclass指定子类。

report_desc

USB-IF官网
USB-IF官网提供了HID description Tool可以编辑和生成描述符文件,并且附带了许多常见设备的示例。下载其网站提供的工具dt2_4.zip
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第8张图片
解压后运行Dt.exe
选择file -> open ,打开其提供的mouse文件,即可查看:
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第9张图片
可以使用file -> save as 保存为一个txt文件
我这里通过下面这段c代码向report_desc和report_length中写入二进制数据

#include 
#include 
#include 
#include 
#include 

#define REPORT_DESC_SIZE 50

int main() {
    int fd;
    char *device = "/sys/kernel/config/usb_gadget/xjs_gadget/functions/hid.ms/report_desc";
    char report_desc[REPORT_DESC_SIZE];

    // 鼠标报告描述,这是一个示例,请根据你的设备规范修改
    unsigned char mouse_report_desc[] = {
        0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
        0x09, 0x02,        // Usage (Mouse)
        0xa1, 0x01,        // Collection (Application)
        0x09, 0x01,        //   Usage (Pointer)
        0xa1, 0x00,        //   Collection (Physical)
        0x05, 0x09,        //     Usage Page (Button)
        0x19, 0x01,        //     Usage Minimum (0x01)
        0x29, 0x03,        //     Usage Maximum (0x03)
        0x15, 0x00,        //     Logical Minimum (0)
        0x25, 0x01,        //     Logical Maximum (1)
        0x95, 0x03,        //     Report Count (3)
        0x75, 0x01,        //     Report Size (1)
        0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        0x95, 0x01,        //     Report Count (1)
        0x75, 0x05,        //     Report Size (5)
        0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
        0x09, 0x30,        //     Usage (X)
        0x09, 0x31,        //     Usage (Y)
        0x09, 0x38,        //     Usage (Wheel)
        0x15, 0x81,        //     Logical Minimum (-127)
        0x25, 0x7f,        //     Logical Maximum (127)
        0x75, 0x08,        //     Report Size (8)
        0x95, 0x03,        //     Report Count (3)
        0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
        0xc0,              //   End Collection
        0xc0               // End Collection
    };

    // 打开报告描述文件
    fd = open(device, O_WRONLY);
    if (fd < 0) {
        perror("Unable to open report_desc file");
        return 1;
    }

    // 将鼠标报告描述写入文件
    int desc_size = sizeof(mouse_report_desc);
    int res = write(fd, mouse_report_desc, desc_size);
    if (res < 0) {
        perror("Error writing report_desc file");
        close(fd);
        return 1;
    }

    // 关闭文件
    close(fd);

    // 设置报告描述长度
    fd = open("/sys/kernel/config/usb_gadget/xjs_gadget/functions/hid.ms/report_length", O_WRONLY);
    if (fd < 0) {
        perror("Unable to open report_length file");
        return 1;
    }

    // 将报告描述长度写入文件
    char length_str[10];
    snprintf(length_str, sizeof(length_str), "%d", desc_size);
    res = write(fd, length_str, strlen(length_str));
    if (res < 0) {
        perror("Error writing report_length file");
        close(fd);
        return 1;
    }

    // 关闭文件
    close(fd);

    return 0;
}

首先新建一个.c文件,将上面代码拷入其中

nano gen.c

然后编译c文件

gcc gen.c -o gen

最后运行

./gen

还需要写入protocol

echo "2" > functions/hid.ms/protocol

4.配置config

在configs目录下可以通过mkdir $configName创建Gadget的一个配置目录,名称可自定义。

mkdir c.1
root@zynq:/sys/kernel/config/usb_gadget/xjs_gadget/configs# cd c.1
root@zynq:/sys/kernel/config/usb_gadget/xjs_gadget/configs/c.1# ls

可以看到如下结果:
在这里插入图片描述
配置信息及其目录结构如下,其中string也是支持多语言的字符串。

configs
└── ${configName}
	├──MaxPower=120 #最大电流,120mA
    └── strings
        └── 0x409
        	└── configuration "config1" #配置描述字符串
echo "120" > configs/c.1/MaxPower 
mkdir  configs/c.1/strings/0x409
echo "this is conf" > configs/c.1/strings/0x409/configuration 

然后对于每个需要启用的function创建软链接,将配置的functions链接到当前使用的config,这样才能使用其功能:

ln -s functions/hid.ms/ configs/c.1/

5.启用Gadget

这里需要用到udc,通过下面命令查看当前有多少个udc。我遇到的问题见内核配置。

ls /sys/class/udc

写入UDC即可开启运行。

echo ci_hdrc.0 >UDC

6.查看结果

ls /dev/hid*

7.一键脚本

由于基于configs的配置,在系统重启后就会重置,所以可以把这些写成一个脚本,每次开机运行后就可以激活hid鼠标

nano start_mouse.sh

脚本内容如下,即上述所有操作:

#!/bin/bash

cd /sys/kernel/config/usb_gadget/
mkdir xjs_gadget
cd xjs_gadget/
echo "0x03FD" > idVendor
echo "0xa4ac" > idProduct
mkdir strings/0x409
echo "flyingrtx" > strings/0x409/manufacturer 
echo "0001" > strings/0x409/serialnumber 
mkdir functions/hid.ms
echo "2" > functions/hid.ms/protocol 
echo "3" > functions/hid.ms/report_length 
mkdir configs/c.1
echo "120" > configs/c.1/MaxPower 
mkdir  configs/c.1/strings/0x409
echo "this is conf" > configs/c.1/strings/0x409/configuration
cd /home
./gen
cd /sys/kernel/config/usb_gadget/xjs_gadget/
cd /sys/kernel/config/usb_gadget/xjs_gadget/
ln -s functions/hid.ms/ configs/c.1/
echo ci_hdrc.0 >UDC
ls /dev/hid*
chmod 666 /dev/hidg0
cd /home

8.连接电脑

使用usb_slave口连接电脑,设备管理器中应当可以看到一个hid鼠标出现
linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标_第10张图片

三、操作鼠标

我们通过往/dev/hidg0文件中写入二进制数据实现鼠标操作
鼠标发送给PC的数据每次4个字节
BYTE1 BYTE2 BYTE3 BYTE4
定义分别是:
BYTE1 –

   |--bit7:   1   表示   Y   坐标的变化量超出-256   ~   255的范围,0表示没有溢出  
   |--bit6:   1   表示   X   坐标的变化量超出-256   ~   255的范围,0表示没有溢出  
   |--bit5:   Y   坐标变化的符号位,1表示负数,即鼠标向下移动  
   |--bit4:   X   坐标变化的符号位,1表示负数,即鼠标向左移动  
   |--bit3:     恒为1  
   |--bit2:     1表示中键按下  
   |--bit1:     1表示右键按下  
   |--bit0:     1表示左键按下  

BYTE2 – X坐标变化量,与byte的bit4组成9位符号数,负数表示向左移,正数表右移。用补码表示变化量
BYTE3 – Y坐标变化量,与byte的bit5组成9位符号数,负数表示向下移,正数表上移。用补码表示变化量
BYTE4 – 滚轮变化。
可以这样调试:

echo -e -n "\x02\xa0\x00\x00" > /dev/hidg0  # 第一个00000010右键按下,x移动
echo -e -n "\x00\xa0\x00\x00" > /dev/hidg0  # 仅x移动,byte1 00000000无操作,从而释放右键,完成一次右击

对于坐标移动,当其最高位为1时,向负方向移动
例如下面两个x坐标的移动数值:

00001000 "\x08\xd0\x00\x00" \xd0是1101 0000,鼠标左移, 不点击,不滚轮
00001000 "\x08\x50\x00\x00" \x50是0101 0000,鼠标右移, 不点击,不滚轮

使用python进行写入hidg0的操作:

data = b"\x02\xa0\x00\x00"

with open('/dev/hidg0', 'wb') as f:
    f.write(data)

data = b"\x00\xa0\x00\x00"

with open('/dev/hidg0', 'wb') as f:
    f.write(data)

此时应该可以看到电脑上的鼠标向左移动,并且点击了一次右键

四、鼠标操作封装

1.使用C语言进行封装

#include 
#include 
#include 
uint8_t byte1(int mid, int left, int right) {
    uint8_t result = 0x00;
    if (left) {
        result += 0x01;
    }
    if (right) {
        result += 0x02;
    }
    if (mid) {
        result += 0x04;
    }
    return result;
}

int8_t byte2(int x) {
    if (x >= 128) {
        x = 127;
    }
    else if (x <= -128) {
        x = -127;
    }
    return (int8_t)x;
}

int8_t byte3(int y) {
    if (y >= 128) {
        y = 127;
    }
    else if (y <= -128) {
        y = -127;
    }
    return (int8_t)y;
}


void data_gen(int x, int y, int left, int mid, int right, uint8_t* result) {
    result[0] = byte1(mid, left, right);
    result[1] = byte2(x);
    result[2] = byte3(y);
    result[3] = 0x00;
}

int main(int argc, char* argv[]) {

    int x = 0, y = 0, left = 0, mid = 0, right = 0;

    if (argc > 1) {
        x = atoi(argv[1]);
    }
    if (argc > 2) {
        y = atoi(argv[2]);
    }
    if (argc > 3) {
        left = atoi(argv[3]);
    }

    uint8_t result[4];

    data_gen(x, y, left, mid, right, result);

    FILE* filePointer;

    filePointer = fopen("/dev/hidg0", "wb");

    if (filePointer == NULL) {
        printf("无法打开文件或创建文件。\n");
        return 1;
    }
    fwrite(result, sizeof(uint8_t), sizeof(result), filePointer);

    // 关闭文件
    fclose(filePointer);

    return 0;
}

这段代码实现的意思是move x y left(是否点击左键, 1点击, 0不点击),默认0,0,0
使用示例:

g++ move.cpp -o move  # 编译
./move 50 0  # 鼠标左移50,y轴不移动,不点左键

总结

通过上述操作,可以实现一个简易的鼠标操作。后续会进行更加完善的python操作封装。

你可能感兴趣的:(linux嵌入式开发,zynq,linux,计算机外设,运维,python,开发语言)