您现在的位置: USB开发网 > USB开源项目 > Easy USB 51 Programer Plus
- Easy USB 51 Programer Plus

真正的实例-如何编写应用程序

------分隔线----------------------------

  虽然利用DriverStudio的工程向导为我们生成了测试程序示例,但它大量使用了DriverStudio自己的封装库,现在来介绍在不使用DriverStudio封装库的情况下如何编写测试程序。

1、打开设备的两种方式

  我们知道打开设备用CreateFile函数,其第一个参数应指向欲打开的USB设备,它可以是Device Interface,也可以是Device Symbolic Link(设备连接符或叫设备名)。要么用Interface打开USB设备,要么用Symbolic Link打开(不知正确与否),在利用DriverStudio工程向导生成时USB驱动程序的过程中可选择应用程序打开此USB设备的方式(见下图),在这时就确定了将来该用何种方式来打开设备。

1)用Interface方式打开设备

  首先取得设备的GUID(什么是GUID?请看这篇文章:USB编程之二(常见设备类型的GUID)),然后利用设备GUID来得到设备Interface,最后就可以用CreateFile函数来打开设备了。

  GUID由驱动程序定义,如上一节的驱动程序实例中对GUID的定义如下:

 
  1. #define Easy_USB_51_ProgramerDevice_CLASS_GUID \    
  2.  { 0x47589a1e, 0xad02, 0x455e, { 0xa9, 0xf7, 0xcc, 0xb2, 0xd5, 0xe, 0x68, 0x54 } }   

 

  还可以利用第三方工具取得设备GUID,如USBView,还有DriverStudio自带的Symbolic Viewer),下面是用Symbolic Viewer查得的上一节驱动实例的GUID

我们这里利用DEFINE_GUID宏来定义设备GUID,内容如下:

 
  1. DEFINE_GUID(Easy_USB_51_ProgramerDevice_CLASS_GUID,    
  2.     0xC713541C, 0x3C65, 0x474A, 0x8E, 0xBC, 0x25, 0x87, 0x51, 0x49, 0xD2, 0x05);    

DEFINE_GUID宏在initguid.h中定义的,所以需要在加入对此文件的引用:

 
  1. #include "initguid.h"   

  另外,我们将要用到的一些函数需要setupapi.h和setupapi.lib,它们是VC6自带的,所以需要添加对这两个文件的引用:

①#include "Setupapi.h"

②选择菜单“Project”->“Settings”,切换到“Link”页,在“Object/library modules”中填入:setupapi.lib

  好了,现在贴出以Interface方式打开设备的代码:

 
  1. //以Device Interface方式打开设备    
  2. void CWR_CtrlLEDDlg::OnOpendevByInterface()     
  3. {    
  4.     LONG                                ii = 0;    
  5.     HDEVINFO                            hDeviceInfo;    
  6.     DWORD                               bufferSize;    
  7.     SP_DEVICE_INTERFACE_DATA            interfaceData;    
  8.     PSP_DEVICE_INTERFACE_DETAIL_DATA    deviceDetail;    
  9.    
  10.      // Find all devices that have our interface    
  11.     hDeviceInfo = SetupDiGetClassDevs(    
  12.                     (LPGUID)&Easy_USB_51_ProgramerDevice_CLASS_GUID,        
  13.                     NULL,    
  14.                     NULL,    
  15.                     DIGCF_PRESENT | DIGCF_DEVICEINTERFACE    
  16.                     );    
  17.     if (hDeviceInfo == INVALID_HANDLE_VALUE)    
  18.     {    
  19.         AfxMessageBox("SetupDiGetClassDevs failed");    
  20.         return;    
  21.     }    
  22.    
  23.     // Setup the interface data struct    
  24.     interfaceData.cbSize = sizeof(interfaceData);    
  25.    
  26.     for (ii = 0;    
  27.         SetupDiEnumDeviceInterfaces(    
  28.             hDeviceInfo,    
  29.             NULL,    
  30.             (LPGUID)&Easy_USB_51_ProgramerDevice_CLASS_GUID,                
  31.             ii,    
  32.             &interfaceData);    
  33.         ++ii)    
  34.     {    
  35.         // Found our device instance    
  36.         if (!SetupDiGetDeviceInterfaceDetail(    
  37.             hDeviceInfo,    
  38.             &interfaceData,    
  39.             NULL,    
  40.             0,    
  41.             &bufferSize,    
  42.             NULL))    
  43.         {    
  44.             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)    
  45.             {    
  46.                 TRACE("Error: couldn't get interface detail\n");    
  47.                     
  48.                 continue;    
  49.             }    
  50.         }    
  51.             
  52.         // Allocate a big enough buffer to get detail data    
  53.         deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(bufferSize);    
  54.         if (deviceDetail == NULL)    
  55.         {    
  56.             TRACE("Error: Buffer allocation failed\n");    
  57.             continue;    
  58.         }    
  59.             
  60.         // Setup the device interface struct    
  61.         deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);    
  62.             
  63.         // Try again to get the device interface detail info    
  64.         if (!SetupDiGetDeviceInterfaceDetail(    
  65.             hDeviceInfo,    
  66.             &interfaceData,    
  67.             deviceDetail,    
  68.             bufferSize,    
  69.             NULL,    
  70.             NULL))    
  71.         {    
  72.             TRACE("Error: SetupDiGetDeviceInterfaceDetail failed\n");    
  73.             free(deviceDetail);    
  74.             continue;    
  75.         }          
  76.                 
  77.             
  78.         SetupDiDestroyDeviceInfoList(hDeviceInfo);    
  79.             
  80.         // Open handle to device    
  81.         m_hDev = CreateFile(    
  82.             deviceDetail->DevicePath,    
  83.             GENERIC_READ | GENERIC_WRITE,    
  84.             FILE_SHARE_READ | FILE_SHARE_WRITE,    
  85.             NULL,    
  86.             OPEN_EXISTING,    
  87.             0,    
  88.             NULL    
  89. );          
  90.             
  91.         // Free our allocated buffer    
  92.         free(deviceDetail);    
  93.             
  94.         if (m_hDev == INVALID_HANDLE_VALUE)    
  95.         {    
  96.             AfxMessageBox("Error: CreateFile failed\n");    
  97.             return;    
  98.         }    
  99.             
  100.         AfxMessageBox("设备成功打开!");    
  101.             
  102.         GetDlgItem(IDC_BTN_OPENDEV_BY_INTERFACE)->EnableWindow(FALSE);    
  103.         GetDlgItem(IDC_BTN_OPENDEV_BY_NAME)->EnableWindow(FALSE);    
  104.             
  105.         EnableControl(TRUE);    
  106.     }    
  107. }   

2)用Symbolic Link方式打开设备

  Driver Studio通过KUnitizedName函数生成设备的Symbolic Link,我们再利用CreateFile来打开设备,其一般形式为:

 
  1. hDevice = CreateFile("\\\\.\\OMNIPORT3",    
  2.          GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,    
  3.          NULL,     
  4. OPEN_EXISTING,     
  5. FILE_ATTRIBUTE_NORMAL ,    
  6. NULL);    

其中前缀“\\\\.\\”表示Createfile试图打开一个设备,然后紧跟设备名,其设备名可以利用DDK提供的“Device Tree”工具来查询,也可以查看DriverStudio向导生成的代码中KUnitizedName实例代码(位于AddDevice功能函数内),如果第一个参数为:L”TestDevice”,则设备名一般应为TestDevice0

  在上一节实例中生成的驱动程序是以Interface方式打开的,现在我们重新配置此项目,在配置向导第“Step 12 of 14”中,打开设备(Open Device)选择“Symbolic Link”选项,并填写Symbolic Link的值为:Easy_USB_51_Programer_SymbolicDevice:

下载编译后的驱动

下载驱动源码 (需要放到C:\Easy_USB_51_Programer_Symbolic目录)

  安装驱动程序,然后就可以用Symbolic Link的方式打开设备了,而Symbolic Link的值就应该为Easy_USB_51_Programer_SymbolicDevice0,这个值在配置向导第“Step 12 of 14”中已经填好(但其后要加“0”),通过查看驱动源程序也可以得到(位于AddDevice功能函数的KUnitizedName实例代码的第一个参数),也可用第三方工具如Symbolic Link Viewer查询,下图是利用DriverStudio提供的Symbolic Link Viewer工具查询到的Symbolic Link值:

  好了,现在贴出以Symbolic Link方式打开设备的代码:

 
  1. //利用Symbolic link来打开设备    
  2. void CWR_CtrlLEDDlg::OnOpendevBySymbolicLink()     
  3. {    
  4.     char *sLinkName = "\\\\.\\Easy_USB_51_Programer_SymbolicDevice0";    
  5.    
  6.     m_hDev  = CreateFile(sLinkName,    
  7.                       GENERIC_READ | GENERIC_WRITE,    
  8.                       FILE_SHARE_READ,    
  9.                       NULL,    
  10.                       OPEN_EXISTING,    
  11.                       0,    
  12.                       NULL);        
  13.         
  14.     if (m_hDev == INVALID_HANDLE_VALUE)    
  15.     {    
  16.         AfxMessageBox("Error: CreateFile failed");    
  17.         return;    
  18.     }    
  19.    
  20.     AfxMessageBox("设备成功打开!");    
  21. }   

2、读写设备

  读写设备除了可以通过WriteFile和ReadFile函数以外,还可以利用DeviceIoControl函数。WriteFile最终会调用驱动程序的IRP_MJ_READ实例代码(DriverStudio里是Write实例函数),ReadFile最终会调用驱动程序的IRP_MJ_ WRITE实例代码(DriverStudio里是Read实例函数),而DeviceIoControl最终会调用驱动程序的IRP_MJ_DEVICE_CONTROL实例代码(DriverStudio里是DeviceControl)。

  DeviceIoControl的第二个参数为操作代码,在驱动程序里的定义形如IOCTL_XXXX格式,对于自定义的设备操作代码,一般用CTL_CODE宏定义,我们需要拷贝上一节驱动程序中对IOCTL_LED和IOCTL_GET_KEY两个操作代码的定义(在使用前需要包含头文件:#include "Winioctl.h")

 
  1. #define IOCTL_LED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA)    
  2. #define IOCTL_GET_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)   

  下面放上利用MFC编写的一个基于对话的程序,它主要实现与上一节中测试程序相似的功能(通过端点2读写数据,通过控制端点控制扩展板EXT-BOARD-A上的LED状态,通过控制端点读取扩展板EXT-BOARD-A上的按键的状态),另外还测试了两种打开设备的方式(设备安装驱动程序后就决定了设备只能以某一种方式打开,用不正确的方式打开会报错)。

                           应用程序界面

(呵呵,我已经投PCB了,所以没有用手工制作的那个原始东东来展示)

编译好的应用程序
应用程序源码

注意:这节的实例还需要再改一下设备程序,把之前main函数里的控制LED的代码删除,即

  1. P0  = EpBuf[0];                //由接收到的第一个数据控制LED扩展板EXT-BOARD-A上LED的状态   

这句需要删除,而通过控制端点控制LED和读取按键状态的代码在《厂商自定义USB设备固件程序及特性》一节里已经介绍了。

设备固件源码

------分隔线----------------------------
联系我们
  • Q Q: 1148374829 点击这里给我发消息
  • 旺旺:jhoneqhsieh 点击这里给我发消息
  • 电话:(0)15923141204
  • 淘宝网店