通过前面的学习,大家应该对USB固件程序结构有了比较深的认识,现在再来详细说说固件里决定设备识别成厂商自定义USB设备的地方有哪些,或者说厂商自定义USB设备的固件特性有哪些。

  之前不止一次说过学习USB固件编程的要点是要学会各种描述符及主机命令以及设备的枚举过程,理解设备枚举过程是主线,在这一过程中需要掌握主机各种命令及USB相关描述符(本站《USB命令请求及描述符详解(速查手册)》和《用USB Monitor监视USB枚举(配置、识别)过程(USB枚举过程分析)》对此进行了总结),一般USB接口芯片生产商都提供有固件范例,但USB设备类型非常多,不可能都有相应的示例,我们设计固件时可以直接在范例的基础上修改,修改的地方也主要是各种描述符以及命令的处理上(有些设备类甚至都不需要修改命令处理代码)。影响设备是否被识别成厂商自定义USB设备固件程序的地方主要在以下几个地方:

1、设备描述符

  • bDeviceClass 字段,设备类码,此段为0xFF时,设备的类型由厂商定义
  • bDeviceSubClass字段,设备子类掩码,如果bDeviceClass字段的值为0xFF,此字段的值也必须为0xFF
  • bDevicePortocol字段,设备协议码,如果此字段的值为0xFF,此设备使用厂商定义的协议

  修改好的设备描述符:

//设备描述符
code USB_DEVICE_DESCRIPTOR DeviceDescr =
{
    sizeof(USB_DEVICE_DESCRIPTOR),  //设备描述符长度,= 12H
    USB_DEVICE_DESCRIPTOR_TYPE,     //设备描述符类型,= 01H
    0x00,0x01,                      //协议版本,= 1.10
    0xFF,                           //测试设备类型 ,=0xFF为厂商自定义设备
    0xFF,                           //设备协议
    EP0_PACKET_SIZE,                //端点0最大数据包大小,= 10H
    0x71,0x04,                      //PHILIPS公司的设备ID
    0x00,0x09,                      //设备制造商定的产品ID
    0x00,0x01,                      //设备系列号
    , , ,                        //索引
                                   //可能的配置数
}; 

2、接口描述符

  • bInterfaceClass字段,接口所属于类别,为0xFF时代表厂商自定义
  • bInterfaceSubClass字段,接口子类码,只说明了当bInterfaceClass为零时此字段也必须为零,这里暂时也设为0xFF
  • bInterfaceProtocol字段,接口协议,对于厂商自定义设备并没有说明,这时暂时设为0xFF

  修改好的接口描述符:

//接口描述符
{
    sizeof(USB_INTERFACE_DESCRIPTOR),           //接口描述符长度,= 09H
    USB_INTERFACE_DESCRIPTOR_TYPE,              //接口描述符类型,= 04H
    ,                                          //接口数,只有1个
    ,                                          //可选配置,只有1个
    NUM_ENDPOINTS,                              //除端点0的端点索引数目,= 04H
    0xFF,                                       //接口别类,0xFF=厂商自定义
    0xFF,                                       //子类代码
    0xFF,                                       //协议代码
                                               //字符串描述符索引
}, 

3、对USB主机命令的处理

  在《USB命令请求及描述符详解(速查手册)》一文的表1列出了USB主机命令结构,其中bmRequestType字段的第5至第6位(D5~D6)命令种类(标准、类或者厂商自定义),当为厂商自定义命令时,此时bRequest字段的值就是厂商自定义的命令ID,wValue字段可以传送两个字节长度的数据,当然厂商自定义的命令也可能没有带数据,或者带的数据不止两个字节,如果带的数据不止两个字节,那么应该在wLength字段指明所带数据长度,而真正的数据将在紧接着的数据阶段传输。

  厂商自定义命令有哪些?那当然是按设备需要具备的功能,由厂商自己来定义了,比如此开源项目,我打算实现的自定义USB设备可以通过主机来控制电路板上的八个LED,还可以读取板子上的按键状态,这两个功能都可以用厂商定义命令来实现。比如可以定义USB命令的bRequest字段为0时代表控制LED状态,为1时读取按键状态。控制LED状态时肯定主机要带数据来表明LED应该转换的状态,我们可以用一个字节的八个位分别对应八个LED的状态,那么所带数据就可以放在wValue字段里。而读取按键状态需要两步,第一步是主机发自定义USB命令,命令ID(即bRequest字段的值)为1,当设备接收到这个命令后,应该立即将当前按键状态通过控制端点发送给主机,按键状态可以用一个字节的低两位表示,主机发送厂商自定义命令是通过Windows API函数DeviceIoControl来完成的,其功能类似于另一个API函数WriteFile,DeviceIoControl发送命令时,如果设备有数据返回,DeviceIoControl函数的第5个参数会带回设备返回的数据,所以在读取按键状态时,调用DeviceIoControl函数后可以查询DeviceIoControl函数里的第5个参数得知按键状态。

下面是需要修改的命令处理相关代码:

1)Chap_9.c文件中的control_handler函数是判断命令别类(即判断是标准、类或者厂商自定义)的,需要增加厂商自定义命令调用接口,可以参考标准请求代码的写法,内容如下:

/*************************************************************
** 函数名称: void control_handler(void)
** 功能描述: 主机命令类型服务程序
**************************************************************/
void control_handler(void)
{
    INT8U type, req,wValue;  

    type = ControlData.DeviceRequest.bmRequestType & USB_REQUEST_TYPE_MASK;
                                                //读取请求代码
    req = ControlData.DeviceRequest.bRequest & USB_REQUEST_MASK;
    wValue = ControlData.DeviceRequest.wValue;
    if (type == USB_STANDARD_REQUEST)
        (*StandardDeviceRequest[req])();        //标准请求处理
    else if (type == USB_VENDOR_REQUEST)            //厂商请求
        (*VendorDeviceRequest[req])(wValue);
    //else if(type == USB_CLASS_REQUEST)
    //  (*ClassDeviceRequest[req])();           //类请求,如大容量类
    else
        stall_ep0();                    //无效请求,返回STALL
} 

  以上代码中,当判断到USB命令数据包里的bRequest字节为USB_VENDOR_REQUEST(0x02)时调用(*VendorDeviceRequest[req])(wValue) 一句,VendorDeviceRequest是在代码区定义的一个函数指定组数,这种调用方法可以根据数组下标(req)来决定调用哪个函数,VendorDeviceRequest函数指针数组的定义如下(同样是参考标准请求代码的写法):

//USB厂商请求入口地址指针表
code void (*VendorDeviceRequest[]) (INT16U wValue) =
{
    control_led,
    get_key_state
};  

  当req的值(即命令ID)为0时调用control_led函数,当req的值为1时调用get_key_state函数,同时把wValue作为参数传递给这两个函数(当然参数对get_key_state函数没用),以下是这两个函数的代码:

sbit K1 = P3^;
sbit K2 = P3^;      

/****************************************
** 函数名称: void control_led(INT16U wValue)
** 功能描述: 控制扩展板EXT-BOARD-A上的LED
** 参    数:INT16U wValue->控制值,低字节有效,如wValue为0x0000时LED全灭,wValue为0x00FF时LED全亮
*****************************************/
void control_led(INT16U wValue)
{
    P0 =    wValue % ;       //返回一个空的数据表示执行完毕      

    single_transmit(, );      //返回一个空的数据表示执行完毕
}      

/****************************************
** 函数名称: void get_key_state(INT16U wValue)
** 功能描述: 取得扩展板EXT-BOARD-A上按键状态
** 参    数:INT16U wValue->无意义
*****************************************/
void get_key_state(INT16U wValue)
{
    INT8U ucKeyState[],i;        

    ucKeyState[]   = 0x00;
    K1  = ;
    K2  = ;
    ;i<;i++);      

    if(~K1) //K1按下
    {
        ucKeyState[]   |= 0x01;
    }      

    if(~K2) //K2按下
    {
        ucKeyState[]   |= 0x02;
    }      

    single_transmit(ucKeyState,);
}   

另外还需要在Chap_9.h中增加以下内容,对以上新加函数作申明:

extern code void (*VendorDeviceRequest[])(INT16U wValue);
extern void control_led(INT16U wValue);
extern void get_key_state(INT16U wValue); 

  至此,厂商自定义USB设备的代码修改工作已经完成,除了以上说的USB设备固件里影响厂商自定义USB设备特性的地方外,如果设备还用到了非控制端点外的其它端点,还应该定义相应端点描述符,以及用这些端对接收到的数据处理代码以及通过这些端点发送数据的代码。

  厂商自定义的命令必须通过控制管道传输,自定义USB设备也可以通过非控制管道传输数据,所以我们也可能过非控制管道的数据来控制LED状态和读取按键状态,只不过需要自己来定义数据包里各个字节的含义,这种用法有点像传统串口通信一样需要定义数据帧格式,比如可以把数据包的第一个字节固定为命令字节,后面是数据。

  厂商自定义设备必须有相应的主机驱动程序对设备进行支持,所以光完成上面的工作还不行,还得写相应的主机设备驱动程序才行,除了主机驱动程序,也许还需要相应的主机应用程序,下面的文章我将会一步一步介绍怎么来实现USB设备的Windows驱动程序以及Windows应用程序。

特别备注:当设备被指定为一个USB协议定义里的标准设备类别而主机操作系统没有提供相关驱动程序,这时操作系统也会提示安装驱动程序,如果您所写的驱动程序并没有按USB协议定义的标准设备规范来,那么其实这个设备也等同于一个厂商自定义设备,本开源项目的最初版本定义设备类别是一种测试设备类(bDeviceClass = 0xDC),但写的驱动程序程序却并不符合特定设备类的特性,所以也相当于是一种自定义USB设备,以前发布的EASY USB 51 PROGRMAER第一个版本的时候就是按这种方法做的厂商自定义USB设备,最新PLUS版本对这种不严格的方法进行了更正。

本节完成后的USB固件源码

厂商自定义USB设备固件程序及特性的更多相关文章

  1. 厂商自定义USB设备类概述

    USB协会将常用具有相同/相似功能的设备归为一类,并制定了相关的设备类规范,这样就能保障只要依照同样的规范标准,即使不同的厂商开发的USB设备也可以使用同样的驱动程序,而且操作系统中无须为每种设备提供 ...

  2. 基于libUSB的USB设备固件更新程序(下载数据)(转)

    源:基于libUSB的USB设备固件更新程序(下载数据) 本文紧接上一篇日志:基于libUSB-Win32的USB设备固件更新程序(前言),相关背景以及起因等,此处不再赘述,如感兴趣请移步. libU ...

  3. C# 上位机的USB设备拔插检测

    我们做USB通信时,通信成功后,往往要检测USB设备的拔插状态,这里就USB拔插进行一下说明. 参考:https://www.imooc.com/article/17438 先说明一下,我这里只是用C ...

  4. USB2.0学习笔记连载(十四):USB驱动安装及固件程序的编写

    在之前的博客中已经讲过,驱动程序最核心的两个文件,一个是xxx.sys文件,一个是xxx.inf文件,主机是寻找xxx.inf文件. 在下面的文件中有相关关于USB驱动的说明.对于用户来说,xxx.s ...

  5. C# 实现自定义的USB设备与上位机进行通信(上位机部分)

    因为以前没用过USB,对USB也不了解,于是上网查了很多资料,不过网上的资料都是零零散散,不清不楚的,于是我自己总结了一下,下面几个链接是网上这么多零散资料里,我觉得比较有参考意义的. USB设备连接 ...

  6. C#:基于WMI查询USB设备信息 及 Android设备厂商VID列表

    /* ---------------------------------------------------------- 文件名称:WMIUsbQuery.cs 作者:秦建辉 MSN:splashc ...

  7. Android应用程序无法读写USB设备的解决方法

    假设android系统中的API或者apk无法读写usb设备.可能是没有加入读写usb的权限,须要依照例如以下方法进行设置: 1. 在android.hardware.usb.host.xml文件里加 ...

  8. usb设备(移动硬盘或U盘),弹出时提示“有进程或程序占用,无法弹出”。解决办法

    测试环境:Win7(其他Windows系统环境,也可参考) 总结办法来源,https://bbs.csdn.net/topics/392297251?page=1文章中热心网友的评论指引 1. 控制面 ...

  9. USB设备的基本概念

    在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的.一个USB设备由3个功能模块组成:USB总线接口.USB逻辑设 ...

随机推荐

  1. 并行编译加快VS C++项目的编译速度

    最近编译的项目都比较大,话说自己的电脑配置还行,但编译所花的时间还是很长,遇到需要重新编译整个项目的时候真的有回宿舍睡一觉的冲动.昨天一不小心被我发现了一款软件Xoreax IncrediBuild ...

  2. Linux系统编程(17)——正则表达式进阶

    C的变量和Shell脚本变量的定义和使用方法很不相同,表达能力也不相同,C的变量有各种类型,而Shell脚本变量都是字符串.同样道理,各种工具和编程语言所使用的正则表达式规范的语法并不相同,表达能力也 ...

  3. apache+php+mysql常见集成环境安装包

    http://www.thinksaas.cn/group/topic/33/ apache+php+mysql是常见php环境,在windows下也称为WAMP,对于初学者自选版本搭建总是会遇到一些 ...

  4. UESTC_贪吃蛇 CDOJ 709

    相信大家都玩过贪吃蛇游戏吧. 在n×m的迷宫中,有着一条长度不超过9的贪吃蛇,它已经将所有的食物吃光了,现在的目标是移动到出口. 它走的时候不能碰到自己的身体,也不能碰到墙壁.(如果贪吃蛇的长度> ...

  5. hdu4393 Throw nails(只用模拟前面500来次,后面根据速度、位置、id值排序即可)

                                                                                                         ...

  6. linux下mysql数据库的操作

    本文主要针对linux下mysql数据库的安装,以及数据库的创建和简单的数据库操作进行说明. ①.Mysql数据库的安装: 数据库的安装分为源码安装和rpm安装. 当然对于老手来说需要进行一些自定义的 ...

  7. Cuckoo hash算法分析

    一 基本思想: cuckoo hash是一种解决hash冲突的方法,其目的是使用简单的hash 函数来提高hash table的利用率,同时保证O(1)的查询时间 基本思想是使用2个hash函数来处理 ...

  8. Linux如何实现开机启动程序详解

    我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Linux开机引导的步骤. 加载内核LILO 启动之 ...

  9. 本人的cocos2d-x之路

        大学基本上算是混着过去了- -,说起学到的东西,感觉真的不多.然后吧.在大四这年在大妈的带动下,来到了一家棋牌游戏公司,详细就不说了.刚进去的时候真的是啥也不懂.先是看了项目代码,自己捉摸了1 ...

  10. CSS清除浮动_清除float浮动

    2.clear:both清除浮动为了统一样式,我们新建一个样式选择器CSS命名为“.clear”,并且对应选择器样式为“clear:both”,然后我们在父级“</div>”结束前加此di ...