找回密码
 注册

微信扫码登录

使用验证码登录

只需一步,快速开始

胜天工科技销售各种数字电视信号调制卡胜天工科技销售各种数字电视信号码流卡

【游客、新手、注册会员的区别】 【积分策略和会员晋级说明】 【发帖和附件上传规则】 【如何下载感兴趣的资料】 【如何获取梦游币】 【侵权资料处理及免责说明】
查看: 3337|回复: 1

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

[复制链接]
  • TA的每日心情
    开心
    2016-9-26 15:28
  • 签到天数: 56 天

    [LV.5]常住居民I

    发表于 2009-5-4 20:53:40 | 显示全部楼层 |阅读模式
    分享到:
    消息来自- 北京
    第七篇: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);
    }
    踩过的脚印

    该用户从未签到

    发表于 2014-2-23 15:56:30 | 显示全部楼层
    消息来自- 北京
    不错,看看!
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|数字电视开发网 ( 京ICP备16008897号-5 )

    GMT+8, 2026-5-14 08:34 , Processed in 0.121781 second(s), 24 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表