原创 基于STM32的USB程序开发笔记(七) - 结束

2008-11-25 10:08 7367 11 5 分类: MCU/ 嵌入式
第七篇:XP下USB驱动开发的最终完成
这是我进行的唯一一次驱动开发,对DDK以及DriverStudio知之甚少
,驱动代码部分不做阐述,在这我将STM32-USB驱动-应用程序串联起来说明。

在VC6环境下,连接USB驱动部分我写了个类CUSBAPI来封装该操作,在USBAPI.h文件中:

#define FILE_DEVICE_EZUSB  0x8000



#define EZUSB_IOCTL(index) \

    CTL_CODE(FILE_DEVICE_EZUSB, index, METHOD_BUFFERED, FILE_READ_DATA)



#define ReadFrom_EP2 \

    CTL_CODE(FILE_DEVICE_EZUSB, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS)

#define WriteTo_EP1 \

    CTL_CODE(FILE_DEVICE_EZUSB, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

这部分定义的是DeviceIoControl()函数所需要的I/O控制代码,此定义在DriverWizard生成的interface.h文件中,在这可包含interface.h也可以复制过来进行定义。

typedef struct _NODE_ENUDEVICEINTERFACE
{
    CHAR    pDeviceInterfaceSymbolicName[MAX_PATH];        // Index for this node

    struct _NODE_ENUDEVICEINTERFACE *pNext;
}
NODE_ENUMDI,
*PNODE_ENUMDI;
这个链表用以存储USB设备的接口名称,
需要另外说明的一点,在USBAPI.c文件中定义了:

DEFINE_GUID(GUID_DEVICEINTERFACE,

    0xE075C5B2, 0xE7FB, 0x4186, 0xA1, 0x39, 0x0B, 0x3F, 0xD2, 0x05, 0xE7, 0x6E);

需要通过这个
GUID_DEVICEINTERFACE取得该接口名称,有了该接口名称后就可以通过CreateFile()获取该接口的句柄,进而可以通过ReadFile()/WriteFile()或者DeviceIoControl()读写USB设备。获取一个设备可能有多个接口,在该类中建立了一个循环链表用以存储该信息。

typedef struct _STRUCT_IO
{
    HWND hTargetWnd;

    HANDLE hDevice;

    DWORD dwIoControlCode;

    PCHAR pInBuffer;
    DWORD dwInSize;

    PCHAR pOutBuffer;
    DWORD dwOutSize;

    LPDWORD lpBytesReturned;
}
STRUCT_IO,
*PSTRUCT_IO;
这个结构中的
hTargetWnd定义了消息对象的窗口句柄,用以向该窗口发送读写数据完成的消息;hDevice即USB设备接口的句柄;其他的含义很明了,就不说明了。

#define CORESTATUS_SUCCESS                        0x0000L
#define CORESTATUS_DESTROY                        0x0001L
#define CORESTATUS_READWRITE_EVENT_ERROR        0x0002L
#define CORESTATUS_READWRITE_THREAD_ERROR        0x0003L
#define CORESTATUS_IOCONTROL_EVENT_ERROR        0x0004L
#define CORESTATUS_IOCONTROL_THREAD_ERROR        0x0005L
这些是定义的类状态,应用程序可以获取这些状态。

#define MSG_READWRITE_COMPLETION                WM_USER+0x0010
#define MSG_IOCONTROL_COMPLETION                WM_USER+0x0011
这些定义的是自定义消息码,应用程序识别此消息码可得知读写操作已完成。

#define ERROR_HANDLE_WINDOW                        0x1000L
#define ERROR_HANDLE_DEVICE                        0x1001L
#define ERROR_BUFFER_LENGTH                        0x1002L
#define ERROR_BUFFER_ISNULL                        0x1003L
#define ERROR_READWRITE_BUSY                    0x1004L
#define ERROR_IOCONTROL_BUSY                    0x1005L
这些定义的进行读写操作时,进行的一些参数检查并返回的状态。

下面这些是类成员函数以及变量,USBAPI类内建立了两个独立线程,这样在对USB设备进行读写时,就不会堵塞应用程序的窗口线程,读写操作完成后由
消息MSG_READWRITE_COMPLETION和MSG_IOCONTROL_COMPLETION通知应用程序。详细代码请参考源程序。
// **********************************************************************************************************
// Class members definition
// **********************************************************************************************************
class CUSBAPI 
{
public:
    CUSBAPI();
    virtual ~CUSBAPI();   

public:

    DWORD EnumDeviceInterface(LPGUID pGUID);
    HANDLE OpenDeviceInterface(PCHAR pDeviceInterfaceSymbolicName);

    DWORD Execute_ReadFile(
            HWND    hWnd,
            HANDLE    hDevice,
            PCHAR    pInBuffer,
            DWORD    dwInSize,
            LPDWORD lpBytesReturned
            );

    DWORD Execute_WriteFile(
            HWND    hWnd,
            HANDLE    hDevice,
            PCHAR    pOutBuffer,
            DWORD    dwOutSize,
            LPDWORD lpBytesReturned
            );

    DWORD Execute_IoControl(
            HWND    hWnd,
            HANDLE    hDevice,
            DWORD    dwIoControlCode,
            PCHAR    pInBuffer,
            DWORD    dwInSize,
            PCHAR    pOutBuffer,
            DWORD    dwOutSize,
            LPDWORD lpBytesReturned
            );

    BOOL Node_HeadCreate(VOID);
    VOID Node_HeadDelete(VOID);
    VOID Node_RemoveAll(VOID);
    BOOL Node_Append(PCHAR pDeviceInterfaceSymbolicName);
    VOID Node_Remove(PCHAR pDeviceInterfaceSymbolicName);   
    PNODE_ENUMDI Node_Find(PCHAR pDeviceInterfaceSymbolicName);

public:
    GUID GUID_Device;

    PNODE_ENUMDI pEnumDeviceNode;
    PNODE_ENUMDI pEnumDeviceHead;

    HANDLE hEvent_ReadWrite;
    HANDLE hEvent_IoControl;

    HANDLE hThread_ReadWrite;
    HANDLE hThread_IoControl;

    DWORD dwThreadID_ReadWrite;
    DWORD dwThreadID_IoControl;

    STRUCT_IO ReadWrite;
    STRUCT_IO IoControl;

    BOOL bExecuting_IoControl;
    BOOL bExecuting_ReadWrite;

    DWORD dwCoreStatus;
};

#endif

在应用程序中定义变量:CUSBAPI ezUSB;
读写操作函数也就三种:
Execute_IoControl()Execute_ReadFile()、Execute_WriteFile()

执行这些函数后,将与USB的驱动程序挂钩,分别响应:
Execute_IoControl() -> NTSTATUS ezUSBDevice::DeviceControl(KIrp I)
Execute_ReadFile() -> NTSTATUS ezUSBDevice::Read(KIrp I)
Execute_WriteFile() -> NTSTATUS ezUSBDevice::Write(KIrp I)

其中NTSTATUS ezUSBDevice::DeviceControl(KIrp I)根据I.IoctlCode()区别类型,按照此示例说明:
ReadFrom_EP2->Execute_IoControl()->NTSTATUS ezUSBDevice::ReadFrom_EP2_Handler(KIrp I)
WriteTo_EP1->Execute_IoControl()->NTSTATUS ezUSBDevice::WriteTo_EP1_Handler(KIrp I)

这样应用程序与USB驱动之间就建立了通讯渠道,在驱动函数中:
NTSTATUS ezUSBDevice::DeviceControl(KIrp I)

NTSTATUS ezUSBDevice::Read(KIrp I)

NTSTATUS ezUSBDevice::Write(KIrp I)
执行一些操作就可以与STM32的USB设备进行通讯了,此时就需要很好的掌握DriverStudio封装的各种类库了。
DriverStudio向导生成的框架,一般就只需要更改这三个函数接口,当然,对于DriverStudio向导的一个BUG不可不知:
    // Initialize each Pipe object
    EP1_OUT.Initialize(m_Lower, 1, 64);   
    EP2_IN.Initialize(m_Lower, 82, 64);
Initialize()函数第二参数是端点地址,在这是16进制表示,这里需要补上0x:
    // Initialize each Pipe object

    EP1_OUT.Initialize(m_Lower, 0x01, 64);   

    EP2_IN.Initialize(m_Lower, 0x82, 64);
忘记此处修改的后果是,执行EP2_IN操作将会使系统直接蓝屏。
这三个函数接口中涉及到读写操作方式,比如说buffer或者direct io,具体有什么区别,请从网络搜寻。

USB驱动负责底层通过端口地址及方式与STM32连接后,
EP1_OUT:USB主机向USB设备发送数据,void CTR_OUT1(void)函数响应
EP2_IN:USB主机请求USB设备发送数据,void CTR_IN2(void)函数响应
示例中CTR_OUT1()接收2Bytes数据,CTR_IN2()发送2Bytes数据,分别控制LED1-4和定时获取Joystick的状态:

void CTR_OUT1(void)
{
  unsigned short portc;
  unsigned short wCount;
 
  wCount = GetBuffDescTable_RXCount(ENDP1);

  if(wCount == 2)
  {
    //portc = GPIO_ReadInputData(GPIOC);

    BufferCopy_PMAToUser((unsigned char *)&portc, GetBuffDescTable_RXAddr(ENDP1), 2);

    GPIO_Write(GPIOC, (GPIO_ReadInputData(GPIOC)&0xFF0F)|(portc&0x00F0));

  }

  SetEPR_RXStatus(ENDP1,EP_RX_VALID);
  SetEPR_TXStatus(ENDP1,EP_TX_STALL); 
}

void CTR_IN2(void)
{
  unsigned short portd = GPIO_ReadInputData(GPIOD) & 0xF800;  // 11-15

    // Copy the transfer buffer to the endpoint0's buffer
    BufferCopy_UserToPMA( (unsigned char *)&portd,   // transfer buffer
                          GetBuffDescTable_TXAddr(ENDP2),           // endpoint 0 TX address
                          2);

  SetBuffDescTable_TXCount(ENDP2, 2);
  SetEPR_RXStatus(ENDP2,EP_RX_DIS);
  SetEPR_TXStatus(ENDP2,EP_TX_VALID);
}

至此,整个基于STM32的USB开发过程的介绍大致说了一遍,详细情况请参考源代码,这篇学习笔记到此结束了,谢谢这段时间关心与支持的朋友们,水平有限欢迎朋友们一起探讨学习。
2008-11-25

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户190532 2009-2-13 23:59

源码在什么地方啊 :) 没找到哦

用户187401 2008-12-7 18:09

向LBXXX先生学习,向LBXXX先生致敬!
相关推荐阅读
用户1206224 2010-11-22 08:05
基于STM32的智能交通信号机
基于STM32的智能交通信号机,有兴趣探讨的朋友可以跟我联系...
用户1206224 2010-03-26 21:30
STM32 USB 源代码及笔记下载 修复贴
今年比较忙,很少来空间,看到大家的留言,故发布此STM32 USB 源代码及笔记下载修复贴STM32 USB固件以及上位机驱动、应用程序源程序:笔记:...
用户1206224 2010-03-26 21:28
STM32 SD卡FATFS移植
最近试玩了STM32用SPI方式操作SD卡,这样就涉及到文件系统,搜索到几种支持FAT16/32的文件,综合考虑,最后选择了完全开源、免费的FATFS,版本是R0.07e,该版本支持长文件名:Wind...
用户1206224 2009-06-02 08:53
基于STM32的USB程序开发笔记(一)
第一篇:需要准备的一些资料1:STM32的参考手册,这对于设备底层USB的硬件配置以及事件驱动机制的了解尤为重要,你需要了解各个寄存器的功能以及如何操作,比如CNTR、ISTR、EPnR、DADDR等...
用户1206224 2009-06-02 08:51
STM32的16路ADC的USB高速采集
STM32的16路ADC的USB高速采集前提:本例基于STM32F103VBT6平台功能说明:16路AD转换并将结果通过USB发送至PC,PC端驱动为定制编写,并附带一款测试软件。附件说明:1:STM...
用户1206224 2009-03-28 09:50
第二版STM32F103ZET6最小系统板实物照片
第二版STM32F103ZET6最小系统板实物照片,PCB工艺不是好,手工焊接后也很脏。第二版硬件上没有问题,各项都调试通过,但综合多种因素,该套小板原则上不会制作并对外,但还是非常感谢大家这段时间对...
EE直播间
更多
我要评论
2
11
关闭 站长推荐上一条 /3 下一条