打印

两次DeviceIoControl命令

[复制链接]
2836|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
guoyt|  楼主 | 2011-11-21 13:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
以前从没有做过USB,头给我一个Cypress的Ezusb的控制面板程序,让我读然后模仿写一个自己的应用程序,我看的头大了,当我我模仿写到获取字符串信息时,原程序发送了两次DeviceIoControl命令,为什么呢要两次呢?
沙发
guoyt|  楼主 | 2011-11-21 13:13 | 只看该作者
原代码如下:
char       temp[64]     =   " ";
GET_STRING_DESCRIPTOR_IN   input;
pvBuffer   =   malloc   (sizeof   (Usb_String_Descriptor)   +   128);
if   (bOpenDriver   (&hDevice,   pcDriverName)   !=   TRUE)       //   Open   the   driver
{
        显示信息
}
#ifdef   USING_MFC
input.Index   =   (UCHAR)   pTh-> index;
input.LanguageId   =   27;   //   NOTE:   frameworks   ignores   it   anyway
wsprintf   (temp,   "GetStringDescripter:   Index=%d ",   input.Index);
EzSendMessage   (hOutputBox,   LB_ADDSTRING,   0,   (LPARAM)emp);
#endif
//   Get   the   first   bytes   of   the   descriptor   to   determine   the   size   of
//   the   entire   descriptor.
if   (hDevice   !=   NULL)   
{//   Perform   the   Get-Descriptor   IOCTL
        bResult   =   DeviceIoControl   (hDevice,
                    IOCTL_Ezusb_GET_STRING_DESCRIPTOR,
&input,
sizeof   (GET_STRING_DESCRIPTOR_IN),
pvBuffer,
sizeof   (Usb_String_Descriptor),
(unsigned   long   *)&nBytes,
NULL);
}/*   if   valid   driver   handle   */
if   (bResult!=TRUE)   
{//   This   will   happen   with   no   string   defined   
        EzSendMessage   (hOutputBox,   LB_ADDSTRING,   0,   (LPARAM) "Get   String   Descriptor   Failed ");
        //ShowSystemError(hOutputBox);
        CloseHandle   (hDevice);
        free   (pvBuffer);
}/*   if   */
ulLength   =   GET_STRING_DESCRIPTOR_LENGTH(pvBuffer);
assert   (ulLength   > =   0);
//   Now   get   the   entire   descriptor
pvBuffer   =   realloc   (pvBuffer,   ulLength);
if   (pvBuffer)   
{
      //   Perform   the   Get-Descriptor   IOCTL
      bResult   =   DeviceIoControl   (hDevice,
IOCTL_Ezusb_GET_STRING_DESCRIPTOR,
&input,
ulLength,
pvBuffer,
ulLength,
(unsigned   long   *)&nBytes,
NULL);
if   (bResult==TRUE)   
{
      显示信息
}

使用特权

评论回复
板凳
yufe| | 2011-11-21 13:27 | 只看该作者
驱动用CB写吗?不少介绍都是用VC+DS,我不懂。

使用特权

评论回复
地板
llia| | 2011-11-21 13:29 | 只看该作者
请教一下CB6和DS2.1能配合使用开发WIN2000   和XP的驱动程序吗?

使用特权

评论回复
5
guoyt|  楼主 | 2011-11-21 13:32 | 只看该作者
说真的我也知道,我是用BCB写应用程序这部分,驱动不是我写的,我们用已经有的,我这个地方很晕,谢谢各位支持

使用特权

评论回复
6
guoyt|  楼主 | 2011-11-21 13:32 | 只看该作者
typedef   struct   _GET_STRING_DESCRIPTOR_IN
{
      UCHAR         Index;
      USHORT       LanguageId;
}   GET_STRING_DESCRIPTOR_IN,   *PGET_STRING_DESCRIPTOR_IN;
这是驱动里的GET_STRING_DESCRIPTOR_IN结构

使用特权

评论回复
7
wangpe| | 2011-11-21 13:36 | 只看该作者
第一次取字符串实际需要的长度,因为输出的空间不够,所以只返回长度

使用特权

评论回复
8
wangpe| | 2011-11-21 13:37 | 只看该作者
发了个信息出去了,第二次按这个长度分配空间,重新读

使用特权

评论回复
9
langgq| | 2011-11-21 13:41 | 只看该作者
对,实际上只要分配足够空间,读一次就可以了。

使用特权

评论回复
10
guoyt|  楼主 | 2011-11-21 13:44 | 只看该作者
多谢,通过提示我好象明白了很多

使用特权

评论回复
11
guoyt|  楼主 | 2011-11-21 13:52 | 只看该作者
先结贴了

使用特权

评论回复
12
peggying| | 2011-11-27 07:49 | 只看该作者
USB设备枚举全纪录



在编写这部分程序之前,首先需要了解有关USB协议,重点是USB数据通信结构、11条标准请求命令和标准USB描述符。

因为嵌入式设备的软硬件是密切相关的,所以还需做的准备工作是了解选用的USB芯片及主控MCU的性能。



一.硬件篇

USB芯片

作用:

1. 管理和实现USB物理层的差模信号

2. 以寄存器的形式提供各种端点(如控制端点,中断端点,大批量传输端点,同步传输端点)

3. 提供状态寄存器,配置寄存器,存储寄存器,中断寄存器,控制寄存器

4. 电源管理(提供3.3V的电源)

5. 实现某些协议层功能(如CRC校验/产生,PID校验/产生,同步模式的识别,并行串行转换等)

固件就是以这些硬件资源作为基础来实现USB设备功能的,其中,端点是需要重点学习的对象,需要熟练掌握与之相关的状态寄存器,配置寄存器,存储寄存器,中断寄存器

MCU主控芯片

作用:

1. 实现USB设备的功能

2. 处理USB芯片产生的中断,解析SETUP包,处理标准请求和厂商请求



二.软件篇

USB设备端固件程序,枚举部分是全部程序的基础和重心,只有主机对设备枚举成功后,主机才能和设备进行正常的通信

USB的枚举过程分为4个状态.

  1.接入态

v主机检测到USB设备插上,击活端口,并发送复位命令(保持10ms)

  2.默认态

v主机使用默认地址读取设备描述符 (GET_DESCRIPTOR)

v主机分配给设备一个总在线的唯一地址 (SET_ADDRESS)

  3.地址态

v主机从新的地址获取设备描述符(GET_DESCRIPTOR)

v主机获取所有设备的配置描述符(GET_DESCRIPTOR)

  4.配置态

v主机设置描述符(设备,配置) (SET_CONFIGURATION)

  v主机读取配置状态(可选) (GET_CONFIGURATION)

v主机读取接口状态(可选) (GET_INTERFACE)



三.实践篇

在枚举阶段的固件编写中,我主要参考PHILIPS公司提供的D12型 USB芯片代码(有参考比自己从零开始要容易很多).由于该代码是基于51单片机编写的,与我所用的16位单片机在性能和结构上有较大差异,因此在固件移植过程中,所遇到的问题大多来源于此.



下面是我在移植过程中碰到的四个主要问题。



第一个问题,出现在默认态阶段,主机用默认地址读取设备描述符。当MCU收到USB芯片产生的中断时,无**确读出USB芯片中断寄存器中的值。

这个问题发生在整个枚举过程的一开始,准确的说是第一步,所以我开始怀疑MCU与USB芯片的通信是否真正建立;而在此前,我对它们之间的通信能力一直深信不疑;因为我曾经用MCU发出测试专用指令来读取USB芯片的ID值,并且正确地读到了USB芯片的ID值,从理论上讲MCU与USB的通信已经建立.解决这个问题大约用了半天时间,后来我在MCU读中断寄存器命令后加了一段延时程序,问题得到解决,即中断产生后MCU正确读出了USB芯片中断寄存器的值

我分析原因是MCU发送命令的速度太快,当发送了读USB中断寄存器值命令后,就迅速发送取数据命令,而USB芯片的速度要慢的多,在MCU读数据时还未来得及把数据准备好。之前我读取USB芯片ID值的测试指令是循环发出的,刚开始虽然数据没准备好,但随着指令的循环发出,后面指令自然可以把前面指令准备的数据发出。



第二个问题出现在默认态阶段,主机用默认地址读取设备描述符。MCU收到USB芯片产生的中断,却无法进入到正确的中断服务程序。

在第一个问题解决后,立刻又出现了这一问题。通过串口监控,我很快发现,解析中断信息的程序中,有一段程序的功能是互换一个BYTE数据的高低字节位,这一功能主要是针对51单片机数据存放的结构与USB接口芯片的数据结构高低位颠倒而设置的。通过实验我发现:这款16位单片机也不具有51单片机的这一特点,所以只要去掉这段高低字节互换的程序,问题就解决了。



第三个问题,出现在地址态阶段,主机获取设备描述符。USB设备响应主机要求,发送16字节的设备描述符给主机,主机不能收到。

因为16字节的数据是通过MCU发送到USB芯片,再由USB芯片发送到

主机的。所以需要判断数据是在哪一个环节没有被正确传输的。我利用串口监测到: MCU收到主机的读中断(读取设备描述符命令)后,会送出了16字节的设备描述符到USB芯片;但是用BUS HOUND(一种基于主机端的USB总线监视软件)监视主机端发现:主机并没有收到这16字节的数据。因此我判断问题出在数据从MCU到USB芯片这一环节——即由MCU发出的设备描述符没有被USB芯片接收到。通过实验,我发现USB芯片没有接收到MCU发来的数据是因为USB芯片的速度较慢,对它来说,每一笔由MCU发出的数据在数据线上保持的时间太短,以致USB芯片无法将数据存放到其寄存器中。当改变MCU的相关设置,延长数据在数据线上保持的时间后,问题得以解决。

   

  第四个问题,出现在配置态阶段,主机设置描述符。通过BUS HOUND监视,主机在SET_CONFIGURATION后停止枚举。

  这个问题是我碰到的最难解决的困难,难就难在一直找不出主机停止枚举的原因,不知道问题产生在哪个环节。通过BUS HOUND可以清楚地看到:主机发出了正确的SET_CONFIGURATION命令,接下来USB设备只需回送一个空包(数据为0)通知主机已收到该命令;可是主机的枚举过程进行到这里却戛然而止。为什么?难道是设备没有发出应答空包,或者是主机没有收到应答空包,还是设备回送的空包有错误?我大约用了5天时间来找支持这些假设的理由。不过,除了证明假设不成立外,一无所获。

第六天,在一次检查BUS HOUND的数据时终于有了新发现。我发现:在SET_CONFIGURATION命令之前,主机发出了GET_DESCRIPTOR来获取全部的描述符合集共46字节数据,设备回复的描述符中在第20多字节时多了一个0字节;不过我并不清楚是否是这个问题对下一步产生了影响,在我看来只要主机在枚举过程中发出了下一步枚举命令,那就表明在此之前的枚举都是正确的(后来的事实证明情况并非如此),后一步的枚举不成功应该与前一步无关。但是因为我再也找不到任何可疑之处,所以只好去寻找那一个字节的0是从哪里来的?又经过了一天的摸索,终于找到了问题的症结。我的描述符数据是以结构体的形势存放的,当MCU收到主机发出的获取描述复命令时,就通过指针的指示将结构体中的数据发出。这些数据大多数是CHAR型,少数几个是INT型(症结),对16位单片机来说INT型数据存放是从偶地址开始的,因此,一旦在INT型的数据前有奇数个CHAR型数据,那就天下大乱了,INT型数据会空出一个奇数的地址位,从其后的偶数地址位开始存放数据。这样中间就多出1位的0字节数据。因此当主机那边收到时这笔数据时,从这一位起其后的数据就全部错位了。当我把INT型数据改用2个CHAR型表述时,一切就正常了; 同时,在此之后的SET_CONFIGURATION也能顺利进行下去,直到枚举结束。

看来枚举的 GET_DESCRIPTOR阶段如果主机得不到正确得描述符信息,并不会影响该阶段的枚举,而是对SET_CONFIGURATION阶段产生影响,使枚举无法继续。我想这应该是由主机端驱动程序来决定的。  

非常幸运,利用BUS HOUND观察到的有限数据中,在最后的几字就出现了异常,否则,这个问题不知道要困扰我多久(BUS HOUND可以显示主机端接收到设备发来的数据,但数据的长度仅为32个字节)





写在最后

对设备端固件开发而言,如果有专用的USB总线分析仪是最好不过了,不但能监视到主机端收发的数据,而且可以监视到设备端收发的数据,这样,开发的效率会大大提高,开发时间可以大大降低。但如果开发条件像我一样有限,至少应该保证有串口和BUS HOUND这种USB总线监视工具(从网上可以下载),其中,串口可以在枚举的开始阶段使用,监控MCU的相关数据;BUS HOUND则可以在枚举的后期发挥更大的作用,因为此时传输的数据量较大,串口已不太适合作为监视工具了。

此外,在USB设备的枚举程序编写中,我遇到的最大挑战来自于8位单片机与16位单片机的结构上的差别,比如数据存取速度,数据高低字节的存放次序,数据类型与存放地址相关的特点等。从我前面列出的问题来看,大部分问题均源自于此;因此在学习某种单片机时,就要准确地掌握不同类型的MCU特性,这样就一定可以降低开发难度,缩短开发时间。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1018

主题

9067

帖子

3

粉丝