aar之Hid上位机软件的实现

By admin at 2017-12-10 • 0人收藏 • 689人看过

首先提醒下: aardio官方已经封装好了一个hid库, 这个和我下边写的是一个dll文件.


下面的帖子是我之前学习aar和hid的时候发布在stm32cube中文网的, 因为软件学习专门独立出来一个网站,所以把之前的帖子复制到了这里.

首先,我自己定义了一个用户hid库文件,名字Usbhidapi.aardio

//Usbhidapi用户库
namespace Usbhidapi;
 
var dll = ..raw.loadDll($"\res\hidapi.dll", , "cdecl");
//初始化函数,0成功,-1失败
Hidapi_init= dll.api("hid_init","int(void)");
//释放端口函数,0成功,-1失败
Hidapi_exit= dll.api("hid_exit","int(void)");
//枚举端口
Hidapi_enumerate= dll.api("hid_enumerate","struct(WORD vendor_id, WORD product_id)","cdecl");
//释放枚举端口
Hidapi_free_enumeration= dll.api("hid_free_enumeration","void(struct devs)","cdecl");
//vid和pid方式打开端口
Hidapi_open= dll.api("hid_open","pointer(WORD vendor_id, WORD product_id, string serial_number)","cdecl");
//路径名方式打开
Hidapi_open_path= dll.api("hid_open_path","pointer(STRING path)","cdecl");
//发送数据函数,返回发送成功的长度 或者 发送失败-1
Hidapi_write= dll.api("hid_write","int(POINTER device, pointer data, INT length)","cdecl");
//读取(超时方式)
Hidapi_read_timeout= dll.api("hid_read_timeout","int(POINTER device, pointer data, INT length, INT milliseconds)","cdecl");
//读取(普通方式)
Hidapi_read= dll.api("hid_read","int(POINTER device, pointer data, INT length)","cdecl");
//设置阻塞模式 0 使用,1 不使用
Hidapi_set_nonblocking= dll.api("hid_set_nonblocking","int(POINTER device, int nonblock)","cdecl");
//发送功能报告
Hidapi_send_feature_report= dll.api("hid_send_feature_report","int(POINTER device, pointer data, int length)","cdecl");
//获取功能报告
Hidapi_get_feature_report= dll.api("hid_get_feature_report","int(POINTER device, pointer data, int length)","cdecl");
//关闭端口函数
Hidapi_close = dll.api("hid_close","void(POINTER device)","cdecl");
//获取用户码
Hidapi_get_manufacturer_string= dll.api("hid_get_manufacturer_string","int(POINTER device, ustring& data, INT maxlen)","cdecl");
//获取产品码
Hidapi_get_product_string= dll.api("hid_get_product_string","int(POINTER device, ustring& data, int maxlen)","cdecl");
//获取序列号
Hidapi_get_serial_number_string= dll.api("hid_get_serial_number_string","int(POINTER device, ustring& data, int maxlen)","cdecl")
//获取字符(按照给定的帧首[string_index]获取 1=manufacturer_strin, 2=product_string, 3=serial_number_string),同上面的功能一样
Hidapi_get_indexed_string= dll.api("hid_get_indexed_string","int(POINTER device, int string_index, ustring& data, int maxlen)","cdecl");
//获取错误信息
Hidapi_error= dll.api("hid_error","ustring(POINTER device)","cdecl");

然后就是界面实现和使用逻辑.

名称:main.aardio

import win.ui;
import Usbhidapi;
import console;
/*DSG{{*/
mainForm = win.form(text="aardio form";right=691;bottom=408;border="dialog frame")
mainForm.add(
Rxdata={cls="listbox";left=429;top=57;right=650;bottom=128;edge=1;items={};z=20};
Txdata={cls="edit";text="123456abcd";left=136;top=101;right=304;bottom=134;align="center";edge=1;z=19};
button={cls="button";text="初始化";left=40;top=146;right=140;bottom=181;z=1};
button10={cls="button";text="设置非阻塞";left=40;top=251;right=140;bottom=286;z=10};
button11={cls="button";text="发送功能报告";left=381;top=146;right=481;bottom=181;z=11};
button12={cls="button";text="获取功能报告";left=381;top=199;right=481;bottom=234;z=12};
button13={cls="button";text="关闭";left=553;top=302;right=653;bottom=337;z=13};
button14={cls="button";text="获取用户码";left=219;top=146;right=319;bottom=181;z=14};
button15={cls="button";text="获取产品码";left=219;top=198;right=319;bottom=234;z=15};
button16={cls="button";text="获取序列号";left=219;top=251;right=319;bottom=286;z=16};
button17={cls="button";text="获取首字符";left=219;top=304;right=319;bottom=338;z=17};
button18={cls="button";text="获取错误信息";left=553;top=248;right=653;bottom=283;z=18};
button2={cls="button";text="退出";left=553;top=356;right=653;bottom=392;z=2};
button3={cls="button";text="枚举";left=381;top=304;right=481;bottom=339;z=3};
button4={cls="button";text="释放枚举";left=381;top=357;right=481;bottom=391;z=4};
button5={cls="button";text="普通方式打开";left=40;top=198;right=140;bottom=234;z=5};
button6={cls="button";text="路径名方式打开";left=381;top=252;right=481;bottom=286;z=6};
button7={cls="button";text="发送";left=40;top=304;right=140;bottom=340;z=7};
button8={cls="button";text="读取(超时)";left=219;top=356;right=319;bottom=391;z=8};
button9={cls="button";text="读取";left=40;top=357;right=140;bottom=391;z=9};
static={cls="static";text="发送数据:";left=63;top=109;right=121;bottom=132;transparent=1;z=21};
static2={cls="static";text="接收数据:";left=356;top=106;right=413;bottom=125;transparent=1;z=22};
static3={cls="static";text="UsbHid库调用示例";left=241;top=12;right=436;bottom=50;align="center";font=LOGFONT(h=-24);transparent=1;z=23}
)
/*}}*/
 
hid_device_info = class {
    string path ;
    WORD vendor_id ;
    WORD product_id ;
    ustring serial_number;
    WORD release_number ;
    ustring manufacturer_string ;
    ustring product_string ;
    WORD usage_page ;
    WORD usage;
    int interface_number;
    pointer next ;
};
 
//转换成AISCII
Aiscii=function(data,len){
    var str="";           
        for(i=1;len;1){
            str ++=string.pack(data[i]);
        }
    return str; 
}
 
var Hiddevice;
 
mainForm.button3.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button3.text );
    var devs = hid_device_info();
    var cur_dev;
     
    var devlist = Usbhidapi.Hidapi_enumerate(0,0);
    cur_dev = raw.convert(devlist,hid_device_info());
    while(cur_dev){
        console.dump(cur_dev);
        cur_dev = cur_dev.next;
        if(cur_dev){
            cur_dev = raw.convert(cur_dev,hid_device_info());
        }
        else {
            break;
        }
         
    }
     
     
}
 
mainForm.button6.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button6.text );
    //mainForm.msgbox( "这个功能下面的写的不对,就没测了" );
 
 
/*
    var str = "\\?\hid#vid_5555&pid_5555#6&8282ff9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
    Hiddevice = Usbhidapi.Hidapi_get_feature_report(str);
    if(Hiddevice){
        console.log("路径方式打开成功!")
    }else {
        console.log("路径方式打开失败!")
    }
*/
 
 
}
 
mainForm.button12.oncommand = function(id,event){
    mainForm.msgbox( "硬件电路里面没有使用到控制端点,所以这个功能没有测试" );
    //硬件电路里面没有使用到控制端点,所以这个功能没有测试
/**
    var buf = raw.malloc(1000, 0);
    var len = 100;
    var Rxlength = Usbhidapi.Hidapi_get_feature_report(Hiddevice, buf, len);
    if(Rxlength == -1){
        console.log("读取失败!")
    }else {
        console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
        //console.log(raw.tostring(databuf, 1, Rxlength));
        if(Rxlength == 0){
            return; //没数据退出
        }
        var str = "";
        str = Aiscii(buf,Rxlength);
         
        mainForm.Rxdata.add(str);
        console.log(str);
    }
**/
     
}
 
mainForm.button11.oncommand = function(id,event){
    mainForm.msgbox( "硬件电路里面没有使用到控制端点,所以这个功能没有测试" );
    //硬件电路里面没有使用到控制端点,所以这个功能没有测试
/*
    var Txlength = Usbhidapi.Hidapi_send_feature_report(Hiddevice,'\0'+mainForm.Txdata.text,#mainForm.Txdata.text+1);
    if(Txlength == -1){
        console.log("发送失败!")
    }else {
        console.log("发送成功!!发送长度为:",Txlength)
    }  
 
*/ 
}
 
mainForm.button8.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button8.text );
    var buf = raw.malloc(1000, 0);
    var len,ms = 100,1000;
    var Rxlength = Usbhidapi.Hidapi_read_timeout(Hiddevice, buf, len, ms);
    if(Rxlength == -1){
        console.log("读取失败!")
    }else {
        console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
        //console.log(raw.tostring(databuf, 1, Rxlength));
        if(Rxlength == 0){
            return; //没数据退出
        }
        var str = "";
        str = Aiscii(buf,Rxlength);
         
        mainForm.Rxdata.add(str);
        console.log(str);
    }  
     
}
 
mainForm.button17.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button17.text );
    var buf = raw.malloc(1000);
    //var buf= "";
    var Maxlen = 255;
    var ret,buf2= Usbhidapi.Hidapi_get_indexed_string(Hiddevice,3,buf,Maxlen);
    if(ret == -1){
        console.log("获取首字符失败!")
    }else {
        console.log("获取首字符成功!")
        console.log("首字符为:",buf2);
 
    }  
     
}
 
mainForm.button16.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button16.text );
     
    var buf = raw.malloc(1000);
    //var buf= "";
    var Maxlen = 255;
    var ret,buf2= Usbhidapi.Hidapi_get_serial_number_string(Hiddevice,buf,Maxlen);
    if(ret == -1){
        console.log("获取序列号失败!")
    }else {
        console.log("获取序列号成功!")
        console.log("序列号为:",buf2);
 
    }
}
 
mainForm.button15.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button15.text );
    var buf = raw.malloc(1000);
    //var buf= "";
    var Maxlen = 255;
    var ret,buf2= Usbhidapi.Hidapi_get_product_string(Hiddevice,buf,Maxlen);
    if(ret == -1){
        console.log("获取产品码失败!")
    }else {
        console.log("获取产品码成功!")
        console.log("产品码为:",buf2);
 
    }  
     
}
 
mainForm.button14.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button14.text );
    var buf = raw.malloc(1000);
    //var buf= "";
    var Maxlen = 255;
    var ret,buf2= Usbhidapi.Hidapi_get_manufacturer_string(Hiddevice,buf,Maxlen);
    if(ret == -1){
        console.log("获取用户码失败!")
    }else {
        console.log("获取用户码成功!")
        console.log("用户码为:",buf2);
 
    }
     
}
 
mainForm.button13.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button13.text );
    Usbhidapi.Hidapi_close(Hiddevice);
    console.log("已经关闭");
     
}
 
//var buf = raw.malloc(100, 0);
mainForm.button9.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button9.text );
    var buf = raw.malloc(1000, 0);
    var len = 100;
    var Rxlength = Usbhidapi.Hidapi_read(Hiddevice, buf, len);
    if(Rxlength == -1){
        console.log("读取失败!")
    }else {
        console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
        //console.log(raw.tostring(databuf, 1, Rxlength));
        if(Rxlength == 0){
            return; //没数据退出
        }
        var str = "";
        str = Aiscii(buf,Rxlength);
         
        mainForm.Rxdata.add(str);
        console.log(str);
    }  
     
}
 
 
mainForm.button7.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button7.text );
    var Txlength = Usbhidapi.Hidapi_write(Hiddevice,'\0'+mainForm.Txdata.text,#mainForm.Txdata.text+1);
    if(Txlength == -1){
        console.log("发送失败!")
    }else {
        console.log("发送成功!!发送长度为:",Txlength)
    }  
     
}
 
 
mainForm.button10.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button10.text );
    var ret = Usbhidapi.Hidapi_set_nonblocking(Hiddevice, 1);
    if(ret == -1){
        console.log("设置非阻塞模式失败!")
    }else {
        console.log("设置非阻塞模式成功!")
    }  
     
}
 
 
mainForm.button5.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button5.text );
    Hiddevice = Usbhidapi.Hidapi_open(21845, 21845, null);
    if(Hiddevice){
        console.log("Vid和Pid方式打开成功!")
    }else {
        console.log("Vid和Pid方式打开失败!")
    }
}
 
 
mainForm.button.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.button.text );
     
    var ret = Usbhidapi.Hidapi_init();
    if(ret == -1){
        console.log("初始化失败!")
    }else {
        console.log("初始化成功!")
    }
     
}
mainForm.show() 
return win.loopMessage();

功能演示:

11 个回复 | 最后更新于 2017-12-10
2017-12-10   #1

下面的内容主要是: 写下我写这个库和写hid程序的时候遇到的问题和解决办法!

首先是dll库的声明中某些数据类型写错导致后续获取不到想要的数据

比如:open的C原型为

/** @brief Open a HID device using a Vendor ID (VID), Product ID
            (PID) and optionally a serial number.
 
            If @p serial_number is NULL, the first device with the
            specified VID and PID is opened.
 
            @ingroup API
            @param vendor_id The Vendor ID (VID) of the device to open.
            @param product_id The Product ID (PID) of the device to open.
            @param serial_number The Serial Number of the device to open
                               (Optionally NULL).
 
            @returns
                This function returns a pointer to a #hid_device object on
                success or NULL on failure.
        */
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);

在这里遇到的问题就是这个hid_device* 指针,我试图把这个指针的结构体都弄出来了,因为我在他的文档里面看到了

struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */

所以我理所当然的认为应该定义为一个struct类型,然后列出这个struct类型,下面就是我定义错误的代码:

Hidapi_open= dll.api("hid_open","struct(WORD vendor_id, WORD product_id, string serial_number)","cdecl");

这个错误导致后面获取hid_device这个handle一直失败,陷入了死胡同!

这里正确的理解应该是,这个函数返回一个指针,这个指针里存储了一个handle句柄.

由于后面是直接利用了这个handle句柄,无需我们自己解包,所以,这里就以指针类型输出即可.

正确的定义应该是pointer:

Hidapi_open= dll.api("hid_open","pointer(WORD vendor_id, WORD product_id, string serial_number)","cdecl");

这里定义好之后,那么就是写入,就像我上面说的,写入是利用了这个handle句柄,无需我们参与,所以写入就好声明了.

写入的函数原型是:

/** @brief Write an Output report to a HID device.
 
    The first byte of @p data[] must contain the Report ID. For
    devices which only support a single report, this must be set
    to 0x0. The remaining bytes contain the report data. Since
    the Report ID is mandatory, calls to hid_write() will always
    contain one more byte than the report contains. For example,
    if a hid report is 16 bytes long, 17 bytes must be passed to
    hid_write(), the Report ID (or 0x0, for devices with a
    single report), followed by the report data (16 bytes). In
    this example, the length passed in would be 17.
 
    hid_write() will send the data on the first OUT endpoint, if
    one exists. If it does not, it will send the data through
    the Control Endpoint (Endpoint 0).
 
    @ingroup API
    @param device A device handle returned from hid_open().
    @param data The data to send, including the report number as
        the first byte.
    @param length The length in bytes of the data to send.
 
    @returns
        This function returns the actual number of bytes written and
        -1 on error.
*/
int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);

这里我们可以利用aardio的工具直接生成:

注意复制进去之后要删除掉一些识别不了的命名,比如那个hid_api_call和hid_api_export

变为

int  hid_write(hid_device *device, const unsigned char *data, size_t length);

然后将这个利用工具转换为aardio的声明

hid_write= dll.api("hid_write","int(hid_device &device, const INT byte &data, size_t length)");

这里看到有好几个声明中的数据类型是不常见的,所以我们有需要把他们改成咱们能认识的类型

这里的hid_device&就要改为POINTER device,大写的意思就是不接受NULL空指针,后面的data是咱们要发送的数组,即数据组合,那么可以改为pointer指针类型,一般情况下数组都用指针类型声明.在看后面的size_t这个类型明显是识别不了的,这个length是要写入的长度,所以我们改为INT,大写的意思是无符号32位整型数据.长度不可能是负的,所以是INT,如果是返回值的话,因为可能会返回-1这个错误提示,所以定义为int小写,有符合数据.


2017-12-10   #2

再者就是读取各种string时候遇到的问题:

比如原型为:

/** @brief Get The Manufacturer String from a HID device.
 
    @ingroup API
    @param device A device handle returned from hid_open().
    @param string A wide string buffer to put the data into.
    @param maxlen The length of the buffer in multiples of wchar_t.
 
    @returns
        This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);

然后我按照自己的理解,这个就是返回一个字符串嘛,我就定义成了下面的错误方式:

hid_get_manufacturer_string= dll.api("hid_get_manufacturer_string","int(pointer device, string &string, int maxlen)");

后续这样调用的时候发生了一个问题就是,返回的数据string一直都只是一个字节,就是只显示了一个

在这里一直纳闷了好久,最后无奈,只好从头按照aardio的帮助文档一个类型一个类型的对照改了下,我发现文档的api数据类型中这样写道

这里明明已经写的很明白了,wchar_t*就要转换为ustring这种类型,所以我就改为

Hidapi_get_manufacturer_string= dll.api("hid_get_manufacturer_string","int(POINTER device, ustring& data, INT maxlen)","cdecl");

这里再啰嗦下,ustring&的&的意思也在帮助手册中:

意思是返回.

之后调试就返回了正确的结果了.

这里我只想说明: 官方提供的帮助文档真的很重要!要多看,不要自己主观臆断!!!

2017-12-10   #3

继续:

话说我前面其实下载过很多个hid相关的dll,都调用过,基本上没有成功过,一般都是卡到了枚举这里.所以我其他功能都实现了之后才开始编写这个Hidapi_enumerate枚举端口指令.

函数原型:

/** @brief Enumerate the HID Devices.
 
    This function returns a linked list of all the HID devices
    attached to the system which match vendor_id and product_id.
    If @p vendor_id is set to 0 then any vendor matches.
    If @p product_id is set to 0 then any product matches.
    If @p vendor_id and @p product_id are both set to 0, then
    all HID devices will be returned.
 
    @ingroup API
    @param vendor_id The Vendor ID (VID) of the types of device
        to open.
    @param product_id The Product ID (PID) of the types of
        device to open.
 
    @returns
        This function returns a pointer to a linked list of type
        struct #hid_device, containing information about the HID devices
        attached to the system, or NULL in the case of failure. Free
        this linked list by calling hid_free_enumeration().
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);

这里将dll调用声明没什么可说的,返回的是struct很明显,所以我写作:

//枚举端口
Hidapi_enumerate= dll.api("hid_enumerate","struct(WORD vendor_id, WORD product_id)","cdecl");

好了问题来了,我返回了一个struct的hid_device_info结构体,这个结构体是怎么定义的?

返回的这个结构体里面肯定是枚举出来的一系列数据,数据应该怎么取出来?

这个结构体的原型,文档也提供了

/** hidapi info structure */
struct hid_device_info {
    /** Platform-specific device path */
    char *path;
    /** Device Vendor ID */
    unsigned short vendor_id;
    /** Device Product ID */
    unsigned short product_id;
    /** Serial Number */
    wchar_t *serial_number;
    /** Device Release Number in binary-coded decimal,
        also known as Device Version Number */
    unsigned short release_number;
    /** Manufacturer String */
    wchar_t *manufacturer_string;
    /** Product string */
    wchar_t *product_string;
    /** Usage Page for this Device/Interface
        (Windows/Mac only). */
    unsigned short usage_page;
    /** Usage for this Device/Interface
        (Windows/Mac only).*/
    unsigned short usage;
    /** The USB interface which this logical device
        represents. Valid on both Linux implementations
        in all cases, and valid on the Windows implementation
        only if the device contains more than one interface. */
    int interface_number;
 
    /** Pointer to the next device */
    struct hid_device_info *next;
};

于是,我继续利用aardio提供的工具进行转换:结果如下

struct hid_device_info {
 
            pointer path;
 
            WORD vendor_id;
 
            WORD product_id;
 
            pointer serial_number;
 
            WORD release_number;
 
            pointer manufacturer_string;
 
            pointer product_string;
 
            WORD usage_page;
 
            WORD usage;
 
            int interface_number;
 
            struct pointer next;
        };

咱们看看他转换的,path原来是char*,变成了pointer,这里我也不讨论它对不对了,我直接和帮助文档进行了比对

于是我果断把它改为了string path;

继续看下面的wchar_t *serial_number; 上面我们已经说过,aardio中wchar_t要变成什么?

对,就是ustring ,所以我也改变它为:ustring serial_number;

依次类推,其他类似的都改掉.

最后看

/** Pointer to the next device */
struct hid_device_info *next;

这个是指向下一个设备的指针,他的原型是它自己的struct,这个应该怎么弄啊??

工具把它转换为了一个

struct pointer next;

但是明显是不对的,有两个类型声明.那么应该删掉哪个?

删掉struct,这里我已经说了这个是指向下个设备的指针,指针,指针....

果断改为pointer next;

好了,改后变成了

hid_device_info = class {
    string path ;
    WORD vendor_id ;
    WORD product_id ;
    ustring serial_number;
    WORD release_number ;
    ustring manufacturer_string ;
    ustring product_string ;
    WORD usage_page ;
    WORD usage;
    int interface_number;
    pointer next ;
};

这个可是完全按照aardio帮助文档来写的!!

2017-12-10   #4

之后就是这个结构体怎么使用了.

这里也卡住了好久,因为对aar确实不熟悉.

纠结的过程就不写了.直接写怎么弄出来的.

先打开这个dll官方提供的一个示例代码hidtest.cpp 这个貌似用c++写的,我们可以参考改改

看到了三个重点标注.

一个获取设备列表,第二个把列表进行struct转换,然后判断结构体中有没数据,有数据就输出里面的数据,while最后一句把指针更改为next的指向,即指向下一个设备,然后再while进行判断是不是空数据,不是空的就输出第二个设备,然后继续......

看到这,我猜想:

var cur_dev;//定义当前设备列表
//获取hid设备列表
var devlist = Usbhidapi.Hidapi_enumerate(0,0);
//将这个列表指向的数据强制转化为hid_device_info这个结构体形式
cur_dev = raw.convert(devlist,hid_device_info());
//判断第一个设备存不存在
while(cur_dev){
        //输出当前设备所有的信息
    console.dump(cur_dev);
    //将next指向的下一个设备数据复制到当前设备
    cur_dev = cur_dev.next;
    //判断设备是否存在
    if(cur_dev){
    //存在,则下一个设备数据强制转化为hid_device_info这个结构体形式
        cur_dev = raw.convert(cur_dev,hid_device_info());
    }
    else {//不存在则退出循环
        break;
    }
     
}

ok,这样操作的结果证明猜想的正确性!

同时也学会了怎么使用next这种自己调用自己的形式.

我这里枚举出了9个设备,最后一个是我用开发板做的一个custom_hid,数据完全正确!

为了验证正确性,我也下载了一个网上的usb调试软件,得出的结果和上面的一致

2017-12-10   #5

线程的使用,

利用线程后台读取数据的方法:

import win.ui;
import Usbhidapi;
import console;
/*DSG{{*/
mainForm = win.form(text="AAR_Hid调试";right=384;bottom=357;border="dialog frame")
mainForm.add(
button={cls="button";text="打开";left=288;top=17;right=363;bottom=41;z=5};
button2={cls="button";text="发送";left=288;top=94;right=363;bottom=118;z=10};
hid_pid={cls="edit";text="22352";left=195;top=17;right=270;bottom=41;edge=1;z=4};
hid_vid={cls="edit";text="1155";left=70;top=17;right=145;bottom=41;edge=1;z=3};
receivedata={cls="listbox";left=28;top=181;right=363;bottom=344;edge=1;hscroll=1;items={};vscroll=1;z=8};
senddata={cls="edit";text="du01";left=31;top=97;right=269;bottom=121;autovscroll=false;edge=1;z=7};
static={cls="static";text="Vid:";left=25;top=17;right=65;bottom=37;font=LOGFONT(h=-20);transparent=1;z=1};
static2={cls="static";text="Pid:";left=150;top=17;right=190;bottom=37;font=LOGFONT(h=-20);transparent=1;z=2};
static3={cls="static";text="发送数据:";left=28;top=65;right=119;bottom=89;font=LOGFONT(h=-20);transparent=1;z=6};
static4={cls="static";text="接收数据:";left=28;top=152;right=119;bottom=179;font=LOGFONT(h=-20);transparent=1;z=9}
)
/*}}*/
 
/*
默认仅主窗体支持跨线程调用,
winform.edit.threadCallable();开启窗口上指定控件的跨线程调用功能,
winform.threadCallable();开启窗口上所有控件的跨线程调用功能,
*/
mainForm.threadCallable();
 
//转换为HEX码
var Hex=function(data,len){
    var str="";
    for(i=1;len;1){
            str = str++" "++string.right(string.format("X", data[ i ]),2);//默认转换成有符号字节,因此取最右边两位  HEX码
        }
    return str;                       
}
//转换成AISCII
var Aiscii=function(data,len){
    var str="";           
        for(i=1;len;1){
            str ++=string.pack(data[i]);
        }
    return str; 
}
 
var Hiddevice;
var thrdHandle;
 
mainForm.wndproc = function(hwnd,message,wParam,lParam){
    select( message ) { 
        case 0x10/*_WM_CLOSE*/{
            if(thrdHandle != null){
                raw.closehandle(thrdHandle);
            }  
        }
        else{
             
        }
    }
    //无返回值则继续调用默认回调函数
}
 
 
mainForm.button2.disabled = true;    
         
mainForm.button.oncommand = function(id,event){
    if(owner.text == "打开"){
        var ret = Usbhidapi.Hidapi_init();
        if(ret == -1){
            mainForm.msgbox("初始化失败!")
            //失败则退出
            return;
        }else {
            //console.log("初始化成功!")
        }
         
        Hiddevice = Usbhidapi.Hidapi_open(tonumber(mainForm.hid_vid.text), tonumber(mainForm.hid_pid.text), null);
        if(Hiddevice){
                //console.log("Vid和Pid方式打开成功!")
                //线程中hid读取只能在非阻塞模式使用,否则会死机
                var ret = Usbhidapi.Hidapi_set_nonblocking(Hiddevice, 1);
                if(ret == -1){
                    mainForm.msgbox("设置非阻塞模式失败!")
                    Usbhidapi.Hidapi_close(Hiddevice);
                    return;
                }else {
                    //console.log("设置非阻塞模式成功!")
                }
                 
             
                thrdHandle = thread.create(
                function(mainForm,Hiddevice,Hex){
                    //import console;
                    import Usbhidapi
                      while(true){
                          var buf = raw.malloc(1000, 0);
                        var len = 100;
                        var Rxlength = Usbhidapi.Hidapi_read(Hiddevice, buf, len);
                        if(Rxlength == -1){
                            //console.log("读取失败!")
                        }else {
                            if(Rxlength == 0){
                            continue ; //没数据退出
                            }
                            var str = "";
                            //str = Aiscii(buf,Rxlength);
                            str = Hex(buf,Rxlength);
                            mainForm.receivedata.add(str);
                            mainForm.receivedata.scrollToBottom();
                            //console.log(str);
                            }        
                 
                         }   //end while
            
                    }, mainForm,Hiddevice,Hex     
                 
                );
 
            mainForm.button2.disabled = false;   
            owner.text = "关闭";
        }else {
            mainForm.msgbox("Vid和Pid方式打开失败!")
            return;
        }
         
         
    }elseif(owner.text == "关闭"){
        Usbhidapi.Hidapi_close(Hiddevice);
        owner.text = "打开";
        mainForm.button2.disabled = true;
        //console.log("已经关闭");
        //thread.suspend(thrdHandle);
    }
     
}
 
mainForm.button2.oncommand = function(id,event){
    //发送函数
    var Txlength = Usbhidapi.Hidapi_write(Hiddevice,'\0'+mainForm.senddata.text,#mainForm.senddata.text+1);
    if(Txlength == -1){
        mainForm.msgbox("发送失败!")
    }else {
        //console.log("发送成功!!发送长度为:",#mainForm.senddata.text)
    }      
     
}
 
//listbox的纵向滚动条显示最下方,消息方式
mainForm.receivedata.scrollToBottom = function () {
 
    return ::SendMessageInt(owner.hwnd, 0x115/*_WM_VSCROLL*/, 0x7/*_SB_BOTTOM*/, 0);
 
}
//设置listbox横向滚动条
//::SendMessageInt( mainForm.receivedata.hwnd,0x194/*_LB_SETHORIZONTALEXTENT*/,500,0)
 
mainForm.enableDpiScaling();
mainForm.show();
 
return win.loopMessage();



2017-12-10   #6

hid软件新增:

Ascii码和Hex码相互转换功能,增加窗口固顶功能,添加 打开设备时候显示 设备用户设备名提示功能.

import win.ui;
import Usbhidapi;
import console;
/*DSG{{*/
mainForm = win.form(text="Hid调试工具(By Stm32cube中文网)";right=384;bottom=366;border="dialog frame";max=false)
mainForm.add(
button={cls="button";text="打开";left=288;top=26;right=363;bottom=50;bgcolor=32768;z=5};
button2={cls="button";text="发送";left=288;top=103;right=363;bottom=127;z=10};
guding={cls="checkbox";text="固顶";left=28;top=336;right=76;bottom=356;z=14};
hid_pid={cls="edit";text="22352";left=195;top=26;right=270;bottom=50;edge=1;z=4};
hid_vid={cls="edit";text="1155";left=70;top=26;right=145;bottom=50;edge=1;z=3};
receivedata={cls="listbox";left=28;top=167;right=363;bottom=330;edge=1;hscroll=1;items={};vscroll=1;z=8};
sel_ascii={cls="radiobutton";text="ASCII";left=184;top=78;right=244;bottom=99;checked=1;font=LOGFONT(h=-16);z=12};
sel_hex={cls="radiobutton";text="HEX";left=120;top=78;right=180;bottom=99;font=LOGFONT(h=-16);z=11};
senddata={cls="edit";text="du01";left=31;top=106;right=269;bottom=130;autovscroll=false;edge=1;z=7};
static={cls="static";text="Vid:";left=25;top=26;right=65;bottom=46;font=LOGFONT(h=-20);transparent=1;z=1};
static2={cls="static";text="Pid:";left=150;top=26;right=190;bottom=46;font=LOGFONT(h=-20);transparent=1;z=2};
static3={cls="static";text="发送数据:";left=28;top=74;right=119;bottom=98;font=LOGFONT(h=-20);transparent=1;z=6};
static4={cls="static";text="接收数据:";left=28;top=138;right=119;bottom=165;font=LOGFONT(h=-20);transparent=1;z=9};
user_name={cls="static";left=82;top=339;right=363;bottom=359;align="right";color=32768;transparent=1;z=13}
)
/*}}*/
 
mainForm.guding.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.guding.text );
    if(mainForm.guding.checked){
        win.setTopmost(mainForm.hwnd)
    }else {
        win.setTopmost(mainForm.hwnd,false)
    }
     
     
     
}
//转换为HEX码
var Hex=function(data,len){
    var str="";
    for(i=1;len;1){
            str = str++" "++string.right(string.format("X", data[ i ]),2);//默认转换成有符号字节,因此取最右边两位  HEX码
        }
    return string.trim(str);                      
}
 //hex转ascii
 var Ascii=function(data){
   var data = string.replace(data," ","");
    var str="";           
        for(i=1;#data;2){
            str =str ++ string.pack( eval("0x"++data[[i]]++data[[i+1]] ));
        }
    return str; 
}
 
//初始化单选对象
var sel_Obj = mainForm.sel_ascii.checked ? "ascii" : "hex";
 
//ascii单选被点击
mainForm.sel_ascii.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.sel_ascii.text );
    if(sel_Obj != "ascii"){
        sel_Obj = "ascii";
        var showdata = Ascii(mainForm.senddata.text);
        mainForm.senddata.text = showdata;
    }
     
}
 
 
//hex单选被点击
mainForm.sel_hex.oncommand = function(id,event){
    //mainForm.msgbox( mainForm.sel_hex.text );
    if(sel_Obj != "hex"){
        sel_Obj = "hex";
        var editdata = string.replace(mainForm.senddata.text," ","");
        var showdata = Hex(editdata,#editdata);
        mainForm.senddata.text = showdata;
    }
     
}
 
 
/*
默认仅主窗体支持跨线程调用,
winform.edit.threadCallable();开启窗口上指定控件的跨线程调用功能,
winform.threadCallable();开启窗口上所有控件的跨线程调用功能,
*/
mainForm.threadCallable();
 
 
var Hiddevice;
var thrdHandle;
 
mainForm.wndproc = function(hwnd,message,wParam,lParam){
    select( message ) { 
        case 0x10/*_WM_CLOSE*/{
            if(thrdHandle != null){
                raw.closehandle(thrdHandle);
            }  
        }
        else{
             
        }
    }
    //无返回值则继续调用默认回调函数
}
 
 
mainForm.button2.disabled = true;    
         
mainForm.button.oncommand = function(id,event){
    if(owner.text == "打开"){
        var ret = Usbhidapi.Hidapi_init();
        if(ret == -1){
            mainForm.msgbox("初始化失败!")
            //失败则退出
            return;
        }else {
            //console.log("初始化成功!")
        }
         
        Hiddevice = Usbhidapi.Hidapi_open(tonumber(mainForm.hid_vid.text), tonumber(mainForm.hid_pid.text), null);
        if(Hiddevice){
                //console.log("Vid和Pid方式打开成功!")
                //线程中hid读取只能在非阻塞模式使用,否则会死机
                var ret = Usbhidapi.Hidapi_set_nonblocking(Hiddevice, 1);
                if(ret == -1){
                    mainForm.msgbox("设置非阻塞模式失败!")
                    Usbhidapi.Hidapi_close(Hiddevice);
                    return;
                }else {
                    //console.log("设置非阻塞模式成功!")
                }
                 
             
                thrdHandle = thread.create(
                function(mainForm,Hiddevice,Hex){
                    //import console;
                    import Usbhidapi
                      while(true){
                          var buf = raw.malloc(1000, 0);
                        var len = 100;
                        var Rxlength = Usbhidapi.Hidapi_read(Hiddevice, buf, len);
                        if(Rxlength == -1){
                            //console.log("读取失败!")
                        }else {
                            if(Rxlength == 0){
                            continue ; //没数据退出
                            }
                            var str = "";
                            //str = Aiscii(buf,Rxlength);
                            str = Hex(buf,Rxlength);
                            mainForm.receivedata.add(str);
                            mainForm.receivedata.scrollToBottom();
                            //console.log(str);
                            }        
                 
                         }   //end while
            
                    }, mainForm,Hiddevice,Hex     
                 
                );
             
            mainForm.button2.disabled = false;   
            owner.text = "关闭";
            var namebuf = raw.malloc(1000);
            //var buf= "";
            var Maxlen = 255;
            var ret,buf2= Usbhidapi.Hidapi_get_manufacturer_string(Hiddevice,namebuf,Maxlen);        
            if(ret != -1){
                mainForm.user_name.text = "HID设备: " ++ buf2 ++ " 已打开!";
                mainForm.user_name.disabled = false;
            }
             
             
        }else {
            mainForm.msgbox("Vid和Pid方式打开失败!")
            return;
        }
         
         
    }elseif(owner.text == "关闭"){
        Usbhidapi.Hidapi_close(Hiddevice);
        owner.text = "打开";
        mainForm.user_name.text = "";
        mainForm.user_name.disabled = true;
        mainForm.button2.disabled = true;
        //console.log("已经关闭");
        //thread.suspend(thrdHandle);
    }
     
}
 
mainForm.button2.oncommand = function(id,event){
    //发送函数
    var trandata = (sel_Obj=="ascii") ? mainForm.senddata.text : Ascii(mainForm.senddata.text);
    var Txlength = Usbhidapi.Hidapi_write(Hiddevice,'\0'+trandata,#trandata+1);
    if(Txlength == -1){
        mainForm.msgbox("发送失败!")
    }else {
        //console.log("发送成功!!发送长度为:",#mainForm.senddata.text)
    }      
     
}
 
//listbox的纵向滚动条显示最下方,消息方式
mainForm.receivedata.scrollToBottom = function () {
 
    return ::SendMessageInt(owner.hwnd, 0x115/*_WM_VSCROLL*/, 0x7/*_SB_BOTTOM*/, 0);
 
}
//设置listbox横向滚动条
//::SendMessageInt( mainForm.receivedata.hwnd,0x194/*_LB_SETHORIZONTALEXTENT*/,500,0)
 
mainForm.enableDpiScaling();
mainForm.show();
 
return win.loopMessage();


2017-12-10   #7

更新: 增加弹出菜单功能[删除],新增列表框内容保存到txt功能,新增右键菜单清空数据功能:

添加下列代码即可:

import fsys.dlg;
exp_to_txt=function(){
    var path = fsys.dlg.save(
        "*.txt|*.txt|",
        io.fullpath("") 
    )
    var str="";
    for(i=1;mainForm.receivedata.count;1){
       str = str ++ mainForm.receivedata.getItemText(i) ++ '\r\n';
    }
   var ret = string.save(path,str);
   if(ret){
         mainForm.msgbox("保存成功!")
   }else {
         mainForm.msgbox("保存失败!")
   }
    
}
 
//创建弹出菜单
mainForm.popmenu = win.ui.popmenu(mainForm); 
mainForm.popmenu.add('删除选中行',function(id){ 
        mainForm.receivedata.delete();
} ) 
mainForm.popmenu.add('清空列表内容',function(id){ 
        mainForm.receivedata.clear();
} ) 
mainForm.popmenu.add('全部数据导出到TXT',function(id){ 
        exp_to_txt();
} )

wps表格和图表操作:

//Excel 图表
import wps.et;
 
var excel = wps.et( true );
if( !excel ) error("该示例需要安装wps");
 
var book  = excel.WorkBooks.Add()
var sheet = book.Worksheets(1)
excel.Visible = true
  
for row=1;30 begin
  sheet.Cells(row, 1).Value2 = math.floor(math.random(1*19) * 100)
end
  
var chart = excel.Charts.Add()
chart.ChartType = 4 //xlLine
var range = sheet.Range("A1:A30")
chart.SetSourceData(range,2)


2017-12-10   #8

更新:

增加导出数据到excel表:

exp_to_wps=function(){
    var path = fsys.dlg.save(
        "*.csv|*.csv|",
        io.fullpath("") 
    )
    var str="";
    for(i=1;mainForm.receivedata.count;1){
       str = str ++ mainForm.receivedata.getItemText(i) ++ '\r\n';
    }
    str = string.replace(str," ",",");
   var ret = string.save(path,str);
   if(ret){
         mainForm.msgbox("保存成功!")
   }else {
         mainForm.msgbox("保存失败!")
   }
    
}
//创建弹出菜单
mainForm.popmenu = win.ui.popmenu(mainForm); 
mainForm.popmenu.add('删除选中行',function(id){ 
        mainForm.receivedata.delete();
} ) 
mainForm.popmenu.add('清空列表内容',function(id){ 
        mainForm.receivedata.clear();
} ) 
mainForm.popmenu.add('全部数据导出到TXT',function(id){ 
        exp_to_txt();
} ) 
mainForm.popmenu.add('全部数据导出到wps',function(id){ 
        exp_to_wps();
} )

2017-12-10   #9

更新:

增加发送前对输入数据进行判断,1:是否是hex规定的字符,2:是否hex输入的是偶数个

mainForm.button2.oncommand = function(id,event){
    //发送函数
    if(sel_Obj == "hex"){
        var strd = string.replace(mainForm.senddata.text,"\s","");
        var tt = string.find(strd,"[^0-9a-fA-F]");
        if(tt != null){
            mainForm.msgboxErr("输入了非十六进制字符!")
            return;
        }else {
            if((#strd%2) != 0){   //非偶数
                mainForm.msgboxErr("Hex格式输入错误!")
                return;
            }
        }
         
    }
    var trandata = (sel_Obj=="ascii") ? mainForm.senddata.text : Ascii(mainForm.senddata.text);
    var Txlength = Usbhidapi.Hidapi_write(Hiddevice,'\0'+trandata,#trandata+1);
    if(Txlength == -1){
        mainForm.msgbox("发送失败!")
    }else {
        //console.log("发送成功!!发送长度为:",#mainForm.senddata.text)
    }      
     
}


2017-12-10   #10

更新:

发布为exe之后,发现问题: 打开设备然后关闭设备,再次点击打开就会提示打开出错....

经过多次修改和调试后,发现是因为死在了read读取的线程中hid_read函数地方,初步判断是read貌似在线程中模式是阻塞模式,但是我线程中添加设置为非阻塞还是同样的问题,现在不得而知,

解决办法是: read换成超时模式,read_timeout

另外增加线程的打开和关闭判断,使用了thread.set(标志)和get(标志)

另外将hid_open获取到的Hiddevice,也用thread.set(HHiddevice,Hiddevice)和get来传递,利于判断非空.

thread.set("handlexx",null);
 
mainForm.button.oncommand = function(id,event){
    if(owner.text == "打开"){
    thread.set("opedclose", true);
        var ret = Usbhidapi.Hidapi_init();
        if(ret == -1){
            mainForm.msgbox("初始化失败!")
            //失败则退出
            return;
        }else {
            //console.log("初始化成功!")
        }
         
        Hiddevice = Usbhidapi.Hidapi_open(tonumber(mainForm.hid_vid.text), tonumber(mainForm.hid_pid.text), null);
        if(Hiddevice){
                thread.set("handlexx",Hiddevice );
                //console.log("Vid和Pid方式打开成功!")
                //线程中hid读取只能在非阻塞模式使用,否则会死机
                var ret = Usbhidapi.Hidapi_set_nonblocking(Hiddevice, 0);
                if(ret == -1){
                    mainForm.msgbox("设置阻塞模式失败!")
                    Usbhidapi.Hidapi_close(Hiddevice);
                    return;
                }else {
                    //console.log("设置非阻塞模式成功!")
                }
                 
             
                thrdHandle = thread.create(
                function(mainForm,Hex){
                    //import console;
                    import Usbhidapi
                      while(thread.get("opedclose")){
                          var buf = raw.malloc(1000, 0);
                        var len = 65;
                        var devhandle = thread.get("handlexx");
                        if(devhandle == null){
                            break ;
                        }
                        var Rxlength = Usbhidapi.Hidapi_read_timeout(devhandle, buf, len ,1);//1ms超时
                        if(Rxlength == -1){
                            //console.log("读取失败!")
                        }else {
                            if(Rxlength == 0){
                            continue ; //没数据退出
                            }
                            var str = "";
                            //str = Aiscii(buf,Rxlength);
                            str = Hex(buf,Rxlength);
                            mainForm.receivedata.add(str);
                            mainForm.receivedata.scrollToBottom();
                            //console.log(str);
                            }        
                 
                         }   //end while
            
                    }, mainForm,Hex       
                 
                );
             
            mainForm.button2.disabled = false;   
            owner.text = "关闭";
            var namebuf = raw.malloc(1000);
            //var buf= "";
            var Maxlen = 255;
            var ret,buf2= Usbhidapi.Hidapi_get_manufacturer_string(Hiddevice,namebuf,Maxlen);        
            if(ret != -1){
                mainForm.user_name.text = "HID设备: " ++ buf2 ++ " 已打开!";
                mainForm.user_name.disabled = false;
            }
             
             
        }else {
            mainForm.msgbox("Vid和Pid方式打开失败!")
            return;
        }
         
         
    }elseif(owner.text == "关闭"){
        thread.set("opedclose",false);
        thread.set("handlexx",null )
        thread.waitOne(thrdHandle);
        Usbhidapi.Hidapi_close(Hiddevice);
        owner.text = "打开";
        mainForm.user_name.text = "";
        mainForm.user_name.disabled = true;
        mainForm.button2.disabled = true;
        //console.log("已经关闭");
        //thread.suspend(thrdHandle);
    }
     
}


2017-12-10   #11

刚刚aardio作者一鹤前辈发布了一个usbhid封装库,

同样是利用usbhidapi这个dll,代码风格征服了我,强!

网址:

 http://mp.weixin.qq.com/s/BQSByyfP6TM7zRwNid3AlQ



下面我把老大发的api库给大家看看:

namespace hid;
 
class device{
    ctor( vendorId,productId,serialNumber){
        if( type(vendorId) === type.string ){
            this.hDevice = topointer( api.hid_open_path(path) );
            ..table.gc(this,"close");
        }
        elseif( type(vendorId) == type.pointer ){
            this.hDevice = vendorId;
        }
        else {
            if(serialNumber) serialNumber = ..string.toUnicode(serialNumber); 
            this.hDevice = api.hid_open(vendorId,productId,serialNumber);
            ..table.gc(this,"close");
        }
        if(!this.hDevice) return null;
    }; 
    setNonblocking = function(enabled){
        return api.hid_set_nonblocking(this.hDevice,!!enabled); 
    }
    errorMessage = function(){
        var e = api.hid_error(this.hDevice);
        if(e) return ..string.fromUnicode(topointer(e),,-1);
    }
    read = function(len=255){
        var data = ..raw.buffer(len);
        var len = api.hid_read(this.hDevice,data,len);
        if(len<0) return null,this.errorMessage();
        if(len){
            return ..string.left(data,len);
        }
    }
    readTimeout = function(milliseconds,len=255){
        var data = ..raw.buffer(len);
        var len = api.hid_read_timeout(this.hDevice,data,len,milliseconds);
        if(len<0) return null,this.errorMessage();
        if(len){
            return ..string.left(data,len);
        }
    }
    write = function(data,len,reportId){
        if(!len) len = #data;
        var p = ..raw.realloc(len+1);
        p = ..raw.concat(p,reportId : '\0',1);
        p = ..raw.concat(p,data,len);
         
        var buf = ..raw.buffer(len+1);
        var len = api.hid_write(this.hDevice,data,len+1);
        p = ..raw.realloc(0,p);
         
        if(len<0) return null,this.errorMessage();
        return len;
    }
    getFeatureReport = function(reportId,len=255){
        var data = ..raw.buffer(len+1);
        data[1] = reportId;
        var len = api.hid_get_feature_report(this.hDevice,data,len+1);
        if( len < 0 )  return null,this.errorMessage();
        if( len > 0) return string.sub(data,2,len);
    }
    sendFeatureReport = function(data,len,reportId){
        if(!len) len = #data;
        var p = ..raw.realloc(len+1);
        p = ..raw.concat(p,reportId : '\0',1);
        p = ..raw.concat(p,data,len);
         
        var buf = ..raw.buffer(len+1);
        var len = api.hid_send_feature_report(this.hDevice,data,len+1);
        p = ..raw.realloc(0,p);
         
        if(len<0) return null,this.errorMessage();
        return len;
    }
    getManufacturerString= function(len=255){
       var data = ..raw.buffer(len*2);
       var err = api.hid_get_manufacturer_string(this.hDevice,data,len);
        if( err )  return null,this.errorMessage(); 
        return ..string.fromUnicode(data);
    }
    getProductString= function(len=255){
       var data = ..raw.buffer(len*2);
       var err = api.hid_get_product_string(this.hDevice,data,len);
        if( err )  return null,this.errorMessage(); 
        return ..string.fromUnicode(data);
    }
    getSerialNumberString= function(len=255){
       var data = ..raw.buffer(len*2);
       var err = api.hid_get_serial_number_string(this.hDevice,data,len);
        if( err )  return null,this.errorMessage(); 
        return ..string.fromUnicode(data);
    }
    getIndexedString= function(idx,len=255){
       var data = ..raw.buffer(len*2);
       var err = api.hid_get_indexed_string(this.hDevice,idx,data,len);
        if( err )  return null,this.errorMessage(); 
        return ..string.fromUnicode(data);
    }
    close = function(){
        if(this.hDevice){
            api.hid_close(this.hDevice);
            this.hDevice = null;
        };
    }
    @_meta;
}
  
namespace device{
 
    //根据HIDAPI开源许可证可自由使用hidapi.dll组件:https://github.com/signal11/hidapi 
    api = ..raw.loadDll($"~\lib\hid\.res\hidapi.dll","hidapi.dll","cdecl");
    api.hid_init();
 
    each = function(vendorId,productId){
     
        var devs  = topointer( api.hid_enumerate(vendorId,productId) );
        devs = ..gcdata(  
            _topointer = devs; 
            _gc = function(){
                if(devs){
                    api.hid_free_enumeration(devs);
                    devs = null;
                }
            } 
        )
        var next = devs;
         
        return function(){
            if(next){
                var deviceInfo = ..raw.convert(next,{
                    string path;
                    WORD vendorId;
                    WORD productId;
                    ustring serialNumber;
                    WORD releaseNumber;
                    ustring manufacturerString; 
                    ustring productString; 
                    WORD usagePage;
                    WORD usage;
                    int interfaceNumber;
                    pointer next;
                });
                next = deviceInfo.next;
                return deviceInfo;
            }
            else {
                api.hid_free_enumeration(devs);
                devs = null;
            }
             
        }
    } 
     
    _meta = {
        _serialize = function(kernelCall){
            if(kernelCall) {
                return "global.import('hid') : hid.device( topointer(" + tonumber(owner.hDevice) + ") ) "
             
            }
        };
    }
}
 
/**intellisense()
hid = USB HID 通信接口
hid.device(.(vendorId,productId,serialNumber) = 打开设备返回句柄
hid.device(.(path) = 打开设备返回句柄,参数为each迭代器返回的deviceInfo.path
hid.device() = !stdhiddevice.
hid.each(vid,pid) = @for deviceInfo in hidapi.each() {
     
}
hid.device.each() = !hidapideviceInfo.
end intellisense**/
 
 
/**intellisense(!stdhiddevice)
setNonblocking(true) = 启用非阻塞模式
errorMessage() = 返回错误信息
read() = 读数据,可选使用参数@1指定缓冲区长度
readTimeout(__) = 读数据,参数@1指定毫秒单位的超时值,\n可选使用参数@1指定缓冲区长度
write(.(数据,长度,报告ID) = 写数据,除参数@1以外其他参数为可选参数
getFeatureReport() = 读功能报告,可选使用参数@1指定缓冲区长度
sendFeatureReport(.(数据,长度,报告ID) = 写功能报告,除参数@1以外其他参数为可选参数
getManufacturerString() = 读制造商字符串,可选使用参数@1指定缓冲区长度
getProductString() = 获取产品字符串,可选使用参数@1指定缓冲区长度
getSerialNumberString() = 获取序列号字符串,可选使用参数@1指定缓冲区长度
getIndexedString() = 获取索引字符串,可选使用参数@1指定缓冲区长度
close() = 关闭对象
end intellisense**/
 
/**intellisense(!hidapideviceInfo)
path = 设备路径
vendorId = 厂商ID
productId = 产品ID
serialNumber = 序列号
releaseNumber = 设备版本号
manufacturerString = 制造商,字符串
productString = 产品,字符串
usagePage = 使用页
usage = 使用ID
interfaceNumber = 接口编号
end intellisense**/

调用示例:

import console; 
import hid;
 
for(deviceInfo in hid.device.each() ){
    console.dump(deviceInfo)
    var device = hid.device(deviceInfo.vendorId,deviceInfo.productId)
    if(device){ 
        console.log(device.getProductString());
    }
}
 
console.pause();


登录后方可回帖

登 录
信息栏
本站永久域名:HtmLayout.Cn
纯私人站,当笔记本用的,学到哪写到哪,目前正在学aardio+halcon机器视觉.
Sciter中文在线文档Sciter在线学习文档
本 站 主 站:Stm32cube中文网
Aardio 官方站:Aardio官方
Aardio最新功能:Aardio官方更新日志
aardio在线手册Aardio在线手册
C大Aardio论坛:Aar爱好者论坛
简码教程网:简码编程
AARDIO语言QQ群:70517368
赞助商:才仁机械
下载站:非凡软件站
Loading...