基于STM32的USB程序开发笔记
更新时间:2024-01-27 03:23:01 阅读量: 教育文库 文档下载
- STM32程序开发推荐度:
- 相关推荐
http://www.hkaco.com
基于STM32的USB程序开发笔记
第一篇:需要准备的一些资料 .............................................................................................................. 1 基于STM32的USB程序开发笔记(三) ——STM32 USB固件函数的一些介绍 ............................ 4 基于STM32的USB程序开发笔记(四)——USB设备的枚举(上)............................................. 12 第五篇:USB设备的枚举(下) ....................................................................................................... 15 第六篇:XP下USB驱动开发的初步准备工作 ................................................................................ 23 第七篇:XP下USB驱动开发的最终完成 ........................................................................................ 30
第一篇:需要准备的一些资料
1:STM32的参考手册,这对于设备底层USB的硬件配置以及事件驱动机制的了解尤为重要,你需要了解各个寄存器的功能以及如何操作,比如CNTR、ISTR、EPnR、DADDR等等,如果你想学习USB,这个
手册是必须的。
2:USB2.0 协议,这个资料同样必不可少,如果因为英语阅读能力而苦苦寻找中文版的USB2.0协议,建议不要这么做,现在网络中的所谓的中文版的USB2.0协议不是官方撰写的,大多数是一些热心朋友自己翻译的,却不是很全面,如果你在为寻找这类的资料而无所获时,建议认真塌实的看看官方英文版的USB2.0协议,官方协议阐述的十分详细,650多页,一字一句的了解全部协议不太可行,可针对性的重点理解,比如对第9章USB Device Framework的详细理解对于你的USB Device固件开发不可缺少(这里就是
STM32)。
3:ST提供的USB固件库,这个类库较为散乱,但不可不参考
以下是最近这段时间的成果,包含固件、驱动以及应用程序,固件部分有些功能是不被支持的,如SR_SetDescriptor()、 SR_SynchFrame()等等,在此说明战士不支持非故意如此,而是还没时间仔细深入
编写完善,因为这些目前不被支持的部分目前不被使用到。
后序将接着对各个部分进行一些说明,希望朋友们多多支持,同时欢迎朋友们讨论。 如果你使用的是万利的STM3210B-LK1开发板,则可以烧写hex文件后进行测试。
下载该文件请参阅:
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=1808061&bbs_page_no=1&bbs_id=3020
第2篇:STM32 USB固件函数的驱动原理
首先需要了解一个概念:
USB 设备(DEVICE)从来只是被动触发,USB主机(HOST)掌握主动权,发送什么数据,什么时候发送,是给设备数据还是从设备请求数据,都是由USB 主机完成的,USB设备只是配合主机完成设备的枚举、数据方向和大小。根据数据特性再决定该不该回复该如何回复、该不该接收该如何接收这些动作。 了解这些,再仔细查看STM32的参考手册USB部分以及STM32的中断向量表,从中可以找到两个中断:
/*******************************************************************************
http://www.hkaco.com
* Function Name : USB_HP_CAN_TX_IRQHandler
* requests. * Input : None * Output : None * Return : None
* Description : This function handles USB High Priority or CAN TX interrupts
*******************************************************************************/
void USB_HP_CAN_TX_IRQHandler(void)
{ USB_HPI();
}
/*******************************************************************************
* Function Name : USB_LP_CAN_RX0_IRQHandler
* Description : This function handles USB Low Priority or CAN RX0 interrupts
* requests. * Input : None * Output : None * Return : None
*******************************************************************************/
void USB_LP_CAN_RX0_IRQHandler(void)
{ USB_LPI();
}
即 USB的高、低优先权中断处理函数,这也是整个STM32 USB的事件驱动源,USB_HPI()与USB_LPI()既而转向usb_core(.c,.h)进行相关处理。中断传输(interrupt)、控制传输(control)、大流量传输(bulk)由USB_LPI()响应,大流量传输(bulk)同样可能响应USB_HPI(),同步传输 (isochronous)只响应USB_HPI()。
这样响应USB的所有请求只需要关注usb_core.c文件中的 USB_LPI()与USB_HPI()函数。由于本人也是对USB刚刚有所了解,因而在本例笔记中USB_HPI()函数未做任何处理,在此开源希望大家能完善与纠
正错误并能共享喜悦。以下是USB_LPI()函数:
// *****************************************************************************
// Function Name : USB_LPI.
// Description : Low Priority Interrupt's service routine.
// Input : // Output : // Return :
// *****************************************************************************
void USB_LPI(void)
{
unsigned short wValISTR = GetISTR();
#if(CNTR_MASK & ISTR_RESET) // Reset
http://www.hkaco.com
if(wValISTR & ISTR_RESET & vwInterruptMask)
{
SetISTR(CLR_RESET); INT_ISTR_RESET();
} #endif
#if(CNTR_MASK & ISTR_DOVR) // DMA Over/Underrun
if(wValISTR & ISTR_DOVR & vwInterruptMask)
{
SetISTR(CLR_DOVR); INT_ISTR_DOVR();
} #endif
#if(CNTR_MASK & ISTR_ERR) // Error if(wValISTR & ISTR_ERR & vwInterruptMask)
{
SetISTR(CLR_ERR); INT_ISTR_ERROR();
} #endif
#if(CNTR_MASK & ISTR_WKUP) // Wakeup if(wValISTR & ISTR_WKUP & vwInterruptMask)
{
SetISTR(CLR_WKUP); INT_ISTR_WAKEUP();
} #endif
#if(CNTR_MASK & ISTR_SUSP) // Suspend if(wValISTR & ISTR_SUSP & vwInterruptMask)
{
INT_ISTR_SUSPEND();
SetISTR(CLR_SUSP); // must be done after setting of CNTR_FSUSP
} #endif
#if(CNTR_MASK & ISTR_SOF) // Start Of Frame if(wValISTR & ISTR_SOF & vwInterruptMask)
{
SetISTR(CLR_SOF); INT_ISTR_SOF();
http://www.hkaco.com
} #endif
#if(CNTR_MASK & ISTR_ESOF) // Expected Start Of Frame
if(wValISTR & ISTR_ESOF & vwInterruptMask)
{
SetISTR(CLR_ESOF); INT_ISTR_ESOF();
} #endif
#if(CNTR_MASK & ISTR_CTR) // Correct Transfer if(wValISTR & ISTR_CTR & vwInterruptMask)
{
INT_ISTR_CTR();
} #endif }
// *****************************************************************************
// Function Name : USB_HPI.
// Description : High Priority Interrupt's service routine.
// Input : // Output : // Return :
// *****************************************************************************
void USB_HPI(void)
{ }
可以看出,在USB_LPI()函数中,根据STM32 USB的中断状态寄存器(ISTR)的标志位的状态以及定义的USB控制寄存器中断事件屏蔽码,响应各自的中断事件,比如 INT_ISTR_RESET()响应USB的复位中断,一般可在此函数内进行USB的寄存器的初始化;INT_ISTR_CTR()响应一次正确的数据传输中断,故
名思意,在完成一次正确的数据传输操作后,就会响应此函数。
具体含义请仔细查阅STM32参考手册,下篇将针对这些响应函数进行逐一的详细介绍。
基于STM32的USB程序开发笔记(三) ——STM32 USB固件函数的一些介绍
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: void ISTR_CTR(void); void ISTR_SOF(void);
void ISTR_ESOF(void); void ISTR_DOVR(void); void ISTR_ERROR(void); void ISTR_RESET(void);
http://www.hkaco.com
void ISTR_WAKEUP(void); void ISTR_SUSPEND(void);
这些处理函数使能由定义CNTR_MASK决定: // CNTR mask control
#define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \\ CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \\
其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。
// ***************************************************************************** // Function Name : INT_ISTR_RESET
// Description : ISTR Reset Interrupt service routines. // Input : // Output : // Return :
// ***************************************************************************** void INT_ISTR_RESET(void) {
// Set the buffer table address SetBTABLE(BASEADDR_BTABLE);
// Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); Clr_StateOut(ENDP0);
// Set the endpoint data buffer address: ENDP0 RX SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR);
// Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0);
SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR);
// Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK);
// Set the endpoint address: ENDP0
http://www.hkaco.com
SetEPR_Address(ENDP0, ENDP0);
// --------------------------------------------------------------------- // TODO: Add you code here
// --------------------------------------------------------------------- // Set the endpoint type: ENDP1 SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1);
// Set the endpoint data buffer address: ENDP1 RX SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR);
// Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0);
SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR);
// Initialize the RX/TX status: ENDP1 SetEPR_RXStatus(ENDP1, EP_RX_VALID); SetEPR_TXStatus(ENDP1, EP_TX_DIS);
// Set the endpoint address: ENDP1 SetEPR_Address(ENDP1, ENDP1);
SetEPR_Type(ENDP2, EP_INTERRUPT); Clr_StateOut(ENDP2);
// Set the endpoint data buffer address: ENDP2 RX SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR);
// Set the endpoint data buffer address: ENDP2 TX SetBuffDescTable_TXCount(ENDP2, 0);
SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR);
// Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); SetEPR_TXStatus(ENDP2, EP_TX_VALID);
// Set the endpoint address: ENDP2 SetEPR_Address(ENDP2, ENDP2);
http://www.hkaco.com
// --------------------------------------------------------------------- // End of you code
// ---------------------------------------------------------------------
SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; vsDeviceInfo.bCurrentInterface = 0x00; vsDeviceInfo.bCurrentAlternateSetting = 0x00; vsDeviceInfo.uStatusInfo.w = 0x0000; }
在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。
值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address #define PMAAddr (0x40006000L)
// Buffer Table address register
#define BTABLE ((volatile unsigned *)(RegBase + 0x50))
// ***************************************************************************** // Packet memory area: Total 512Bytes
// ***************************************************************************** #define BASEADDR_BTABLE 0x0000
// ***************************************************************************** // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR
http://www.hkaco.com
// PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT //
// PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT
// ***************************************************************************** //
// PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer //
// ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) // ENP0
#define ENDP0_PACKETSIZE 0x40
#define ENDP0_RXADDR BASEADDR_DATA
#define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) // ENP1
#define ENDP1_PACKETSIZE 0x40
#define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) // ENP2
http://www.hkaco.com
#define ENDP2_PACKETSIZE 0x40
#define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) // ENP3
#define ENDP3_PACKETSIZE 0x40
#define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) // ENP4
#define ENDP4_PACKETSIZE 0x40
#define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) // ENP5
#define ENDP5_PACKETSIZE 0x40
#define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) // ENP6
#define ENDP6_PACKETSIZE 0x40
#define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) // ENP7
#define ENDP7_PACKETSIZE 0x40
#define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE)
这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。
// ***************************************************************************** // Function Name : INT_ISTR_CTR
// Description : ISTR Correct Transfer Interrupt service routine. // Input : // Output : // Return :
// ***************************************************************************** void INT_ISTR_CTR(void) {
unsigned short wEPIndex; unsigned short wValISTR;
http://www.hkaco.com
unsigned short wValENDP;
while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) {
// Get the index number of the endpoints wEPIndex = wValISTR & ISTR_EP_ID;
if(wEPIndex == 0) {
// Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK);
// Transfer direction
if((wValISTR & ISTR_DIR) == 0) {
// DIR=0: IN
// DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); CTR_IN0(); return; } else {
// DIR=1: SETUP or OUT
// DIR=1 implies that CTR_TX or CTR_RX always 1 wValENDP = GetEPR(ENDP0); if((wValENDP & EP_CTR_TX) != 0) {
ClrEPR_CTR_TX(ENDP0); CTR_IN0(); return; }
else if((wValENDP & EP_SETUP) != 0) {
ClrEPR_CTR_RX(ENDP0); CTR_SETUP0(); return; }
else if((wValENDP & EP_CTR_RX) != 0) {
ClrEPR_CTR_RX(ENDP0); CTR_OUT0(); return;
} } }
// Other endpoints else {
http://www.hkaco.com
wValENDP = GetEPR(wEPIndex);
SetEPR_RXStatus(wEPIndex, EP_RX_NAK); SetEPR_TXStatus(wEPIndex, EP_TX_NAK);
if((wValENDP & EP_CTR_TX) != 0) {
ClrEPR_CTR_TX(wEPIndex); switch(wEPIndex) {
case ENDP1: CTR_IN1(); break; case ENDP2: CTR_IN2(); break; case ENDP3: CTR_IN3(); break; case ENDP4: CTR_IN4(); break; case ENDP5: CTR_IN5(); break; case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; } }
if((wValENDP & EP_CTR_RX) != 0) {
ClrEPR_CTR_RX(wEPIndex); switch(wEPIndex) {
case ENDP1: CTR_OUT1(); break; case ENDP2: CTR_OUT2(); break; case ENDP3: CTR_OUT3(); break; case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; case ENDP6: CTR_OUT6(); break; case ENDP7: CTR_OUT7(); break; default: break; } } } }
}
http://www.hkaco.com
INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个需要查看STM32的参考手册以及USB协议才能更好了解为何如此。到这里STM32 USB里数据传输事件就指向了各个对应的端点。下篇着重说明USB设备的枚举。
基于STM32的USB程序开发笔记(四)——USB设备的枚举(上)
USB设备能否工作,枚举步骤,用“乡村爱情”里的话说,“必须的!”,网上也有很多资料,圈圈就提供了一份详细的枚举过程,但对STM32是怎么响应的没有说明,一会详细道来,先上圈圈的提供的那个枚举图示,希望圈圈支持,如果不妥,请与我联系,谢谢。 我将此转换成了PDF文件,方便查看。 首先说明一个变量,定义在usb_core.c中: volatile DEVICE_INFO vsDeviceInfo;
看意思就知道他的作用了,DEVICE_INFO是个结构,定义在usb_type.h中: // ***************************************************************************** // DEVICE_INFO
// ***************************************************************************** typedef struct _DEVICE_INFO {
unsigned char bDeviceAddress;
unsigned char bCurrentFeature; unsigned char bCurrentConfiguration; unsigned char bCurrentInterface; unsigned char bCurrentAlternateSetting;
WORD_2BYTE uStatusInfo;
DEVICE_STATE eDeviceState; RESUME_STATE eResumeState; CONTROL_STATE eControlState;
SETUP_DATA SetupData;
TRANSFER_INFO TransInfo; }
DEVICE_INFO, *PDEVICE_INFO;
在枚举过程中,就是如何处理好SETUP事件,如果STM32 USB接收到正确的SETUP事件,将响应函数CTR_SETUP0(),SETUP事件是特殊的OUT事件,数据方向 Host->Device,SETUP事件数据长度固
http://www.hkaco.com
定为8,数据定义在DEVICE_INFO.SetupData,其数据结构是(定义在 usb_type.h中): typedef struct _SETUP_DATA {
unsigned char bmRequestType; // request type unsigned char bRequest; // request code
WORD_2BYTE wValue; WORD_2BYTE wIndex; WORD_2BYTE wLength; }
SETUP_DATA, *PSETUP_DATA;
WORD_2BYTE是定义的一个共用体: typedef union _WORD_2BYTE {
unsigned short w; struct {
unsigned char LSB; unsigned char MSB; }b; }
WORD_2BYTE;
为什么将SETUP数据结构中的wValue,wIndex,wLength如此定义? 1:USB协议中所有数据传输都是依照低位在先的原则 2:高地位字节可能功能复用
这样在后续的程序编写中就变得十分方便,ST提供的USB固件方法同样如此,但这方面的处理让人有些摸不着头脑,详情可参阅。至于具体的SETUP数据结构含义如何,还是要具备基本知识:了解USB协议
CTR_SETUP0() 函数将SETUP数据提取出来,SETUP数据结构有0长度和非0长度的数据结构,详细参阅USB2.0官方协议第9章。在这将两种区别开来分别执行 SETUP0_NoData()和SETUP0_Data()函数,并返回结果,根据返回结果再响应USB主机
// ***************************************************************************** // Function Name : CTR_SETUP0 // Description : // Input : // Output : // Return :
// ***************************************************************************** void CTR_SETUP0(void) {
RESULT eResult;
http://www.hkaco.com
BufferCopy_PMAToUser( (unsigned char *)&vsDeviceInfo.SetupData, GetBuffDescTable_RXAddr(ENDP0), GetBuffDescTable_RXCount(ENDP0));
if(vsDeviceInfo.SetupData.wLength.w == 0) {
eResult = SETUP0_NoData(); } else {
eResult = SETUP0_Data(); }
switch(eResult) {
case RESULT_SUCCESS: break;
case RESULT_LASTDATA: break;
case RESULT_ERROR: case RESULT_UNSUPPORT:
SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_STALL); break; } }
SETUP0_Data() 和SETUP0_NoData()函数支持的所有USB请求类型只有罗列的这些,有多少种组合都定义在USB协议中,程序根据请求代码,再去执行对应函数,这样做的目的就是让程序结构明了。其中注释为\的部分表明此部分功能已完成。对于未完成部分,希望大家在交流中完善。
// ***************************************************************************** // Routine Groups: SETUP_Data
// ***************************************************************************** RESULT SETUP0_Data(void) {
// SetupData.bRequest: request code switch(vsDeviceInfo.SetupData.bRequest) {
http://www.hkaco.com
case SR_GET_STATUS: return SR_GetStatus(); // done case SR_GET_DESCRIPTOR: return SR_GetDescriptor(); // done case SR_SET_DESCRIPTOR: return SR_SetDescriptor(); // unsupport case SR_GET_CONFIGURATION: return SR_GetConfiguration(); // done case SR_GET_INTERFACE: return SR_GetInterface(); // unsupport case SR_SYNCH_FRAME: return SR_SynchFrame(); // unsupport
default: return RESULT_UNSUPPORT; } }
// ***************************************************************************** // Routine Groups: SETUP_NoData
// ***************************************************************************** RESULT SETUP0_NoData(void) {
// SetupData.bRequest: request code switch(vsDeviceInfo.SetupData.bRequest) {
case SR_CLEAR_FEATURE: return SR_ClearFeature(); // unsupport case SR_SET_FEATURE: return SR_SetFeature(); // unsupport case SR_SET_ADDRESS: return SR_SetAddress(); // done case SR_SET_CONFIGURATION: return SR_SetConfiguration(); // done case SR_SET_INTERFACE: return SR_SetInterface(); // unsupport
default: return RESULT_UNSUPPORT; } }
下篇将介绍上述的各个请求函数如何响应主机。
第五篇:USB设备的枚举(下)
SETUP事件正确接收后,根据该事件提供的请求类型进行对主机的响应。SETUP数据结构的wLength字段说明的是请求返回或者提供的数据长度。
如果判断出的请求信息错误或者说不被支持,STM32 USB设备需要中断此次请求: SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_STALL);
正确获取到请求信息后,如果wLength为0,设备需要发送一个0长度数据包以响应主机: // ***************************************************************************** // Function Name : SETUP0_Trans0Data // Description : // Input :
http://www.hkaco.com
// Output : // Return :
// ***************************************************************************** RESULT SETUP0_Trans0Data(void) {
// Send 0-length data frame as ACK to host SetBuffDescTable_TXCount(ENDP0, 0); SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_VALID);
return RESULT_SUCCESS; }
如果wLength不为0,设备则需要根据请求的数据长度发送数据包以响应主机:
// ***************************************************************************** // Function Name : SETUP0_TransData // Description : // Input : // Output : // Return :
// ***************************************************************************** RESULT SETUP0_TransData(void) {
unsigned short wLength = vsDeviceInfo.TransInfo.wLength; unsigned short wOffset = vsDeviceInfo.TransInfo.wOffset;
unsigned short wMaxSize = vsDeviceInfo.TransInfo.wPacketSize;
if(wLength) {
if(wLength> wMaxSize) {
wLength = wMaxSize; }
// Copy the transfer buffer to the endpoint0's buffer
BufferCopy_UserToPMA( vsDeviceInfo.TransInfo.pBuffer+wOffset, // transfer buffer GetBuffDescTable_TXAddr(ENDP0), // endpoint 0 TX address wLength);
SetBuffDescTable_TXCount(ENDP0, wLength); SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_VALID);
// Update the data lengths
http://www.hkaco.com
vsDeviceInfo.TransInfo.wLength -= wLength; vsDeviceInfo.TransInfo.wOffset += wLength;
return RESULT_LASTDATA; }
return RESULT_SUCCESS; }
如果发送的数据长度大于端点设置的最大数据包长度,数据将分割为若干次发送,记录发送数据的状态包含在结构体TRANSFER_INFO中:
// ***************************************************************************** // TRANSFER_INFO
// ***************************************************************************** typedef struct _TRANSFER_INFO {
unsigned short wLength; // total lengths data will be transmit unsigned short wOffset; // number of data be transmited unsigned short wPacketSize; // endpoints packet max size unsigned char* pBuffer; // address of data buffer }
TRANSFER_INFO, *PTRANSFER_INFO;
TRANSFER_INFO.wLength记录发送的数据长度,如果非0,表示有数据需要被发送。 TRANSFER_INFO.wOffset记录已发送的数据长度,用以确定数据缓冲TRANSFER_INFO.pBuffer的偏移量。
需要了解的一点:USB主机向USB设备正确发送一请求后(这部分的处理由硬件完成),USB主机将间隔若干次的向USB设备索取响应数据,STM32 USB TX状态为NAK说明不响应USB主机,USB主机在超时后退出此次请求;TX状态为STLL说明中断此次请求,USB主机将无条件退出请求;TX状态为 VALID说明设备已准备好数据发送,USB主机将从USB设备读取数据。
以非0长度数据请求的GET_DESCRIPTOR请求为例的响应过程:
CTR_SETUP0()->SETUP0_Data()->SR_GetDescriptor()->SETUP0_TransData()
RESULT SR_GetDescriptor(void) {
// RequestType: device->host, standard request and device recipient
if(vsDeviceInfo.SetupData.bmRequestType == RT_D2H_STANDARD_DEVICE) {
// SetupData.wValue.b.MSB: descriptor type // SetupData.wValue.b.LSB: descriptor index switch(vsDeviceInfo.SetupData.wValue.b.MSB) {
http://www.hkaco.com
case DESCRIPTOR_DEVICE: return SR_GetDescriptor_Device(); case DESCRIPTOR_CONFIG: return SR_GetDescriptor_Config(); case DESCRIPTOR_STRING: return SR_GetDescriptor_String();
default: return RESULT_UNSUPPORT; } }
return RESULT_UNSUPPORT; }
GET_DESCRIPTOR请求属于USB协议中的标准请求(standard request)并且数据方向为设备至主机(device->host),分设备描述符、配置描述符、字符串描述符三种。已设备描述符为例:
RESULT SR_GetDescriptor_Device(void) {
// Assigned the device descriptor to the transfer vsDeviceInfo.TransInfo.wOffset = 0;
vsDeviceInfo.TransInfo.wPacketSize = ENDP0_PACKETSIZE; vsDeviceInfo.TransInfo.pBuffer = DescBuffer_Device.pBuff; vsDeviceInfo.TransInfo.wLength = DescBuffer_Device.wLen; vsDeviceInfo.eControlState = CS_GET_DESCRIPTOR;
if(vsDeviceInfo.TransInfo.wLength> vsDeviceInfo.SetupData.wLength.w) {
vsDeviceInfo.TransInfo.wLength = vsDeviceInfo.SetupData.wLength.w; }
return SETUP0_TransData(); }
这里说明了发送数据的长度、缓冲、偏移、端点包大小以及当前的控制状态,并说明了如果发送的数据长度超出请求的数据长度,则将舍弃超出的部分。数据配置好后,调用SETUP0_TransData()进行数据发送。
在USB主机查询到USB设备准备就绪后,将读取出这些数据,完成后,USB设备将产生IN事件,此时将响应CTR_IN0()函数:
// ***************************************************************************** // Function Name : CTR_IN // Description : // Input : // Output : // Return :
// ***************************************************************************** void CTR_IN0(void) {
http://www.hkaco.com
switch(vsDeviceInfo.eControlState) {
case CS_GET_DESCRIPTOR:
if(SETUP0_TransData() == RESULT_SUCCESS) {
SetEPR_TXStatus(ENDP0, EP_TX_NAK); SetEPR_RXStatus(ENDP0, EP_RX_VALID); } break;
case CS_SET_ADDRESS:
SetEPR_TXStatus(ENDP0, EP_TX_NAK); SetEPR_RXStatus(ENDP0, EP_RX_VALID);
SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_ADDRESSED; break;
case CS_SET_CONFIGURATION:
SetEPR_TXStatus(ENDP0, EP_TX_NAK); SetEPR_RXStatus(ENDP0, EP_RX_VALID);
vsDeviceInfo.eDeviceState = DS_CONFIGURED; break;
default: break; } }
再这如果响应GET_DESCRIPTOR请求发送的数据如果全部发送完毕,SETUP0_TransData()返回RESULT_SUCCESS,并设置TX状态为NAK;否则返回RESULT_LASTDATA,将继续发送剩余的数据直到数据全部被发送。至此,整个的GET_DESCRIPTOR请求过程完成。 0长度的数据请求在发送0长度数据响应后,因为不存在可能还未传送的数据,因而IN事件后直接结束此次请求。
在数据方向为USB主机->USB设备时,如果正确接收到数据,将响应CTR_OUT0()函数,处理过程类同CTR_IN0()函数。
在USB设备的枚举过程中,USB的一些描述符数据结构需要了解,具体在USB协议中有详细的说明,在usb_desc(.c/.h)文件中,定义了这些结构,这些结构是特定的:
设备描述符:长度、格式固定,其中VENDOR_ID与PRODUCT_ID决定上位机驱动的识别。设备分属类别决定了设备的性质,如果为自定义USB设备,设备分属类别值为0,同时上位机驱动必须配合编写;如果为标准USB设备,则必须使用这些标准设备的驱动、数据结构等等,条件是你必须了解这些标准设备的一些信息,好处是省去一些麻烦的驱动编写。
http://www.hkaco.com
const unsigned char cbDescriptor_Device[DESC_SIZE_DEVICE] = {
DESC_SIZE_DEVICE, // bLength: 18 DESCRIPTOR_DEVICE, // descriptor type
0x00, // bcdUSB LSB: USB release number -> USB2.0 0x02, // bcdUSB MSB: USB release number -> USB2.0
0x00, // bDeviceClass: Class information in the interface descriptors 0x00, // bDeviceSubClass: 0x00, // bDeviceProtocol:
0x40, // bMaxPacketSize0: LowS(8), FullS(8,16,32,64), HighS(64)
LOWORD(VENDOR_ID), // idVendor LSB: HIWORD(VENDOR_ID), // idVendor MSB:
LOWORD(PRODUCT_ID), // idProduct LSB: HIWORD(PRODUCT_ID), // idProduct MSB:
LOWORD(DEVICE_VERSION), // bcdDevice LSB: HIWORD(DEVICE_VERSION), // bcdDevice MSB:
0x01, // iManufacturer: Index of string descriptor describing manufacturer 0x02, // iProduct: Index of string descriptor describing product
0x03, // iSerialNumber: Index of string descriptor describing the device serial number
0x01 // bNumConfigurations: number of configurations };
配置描述符:前9个字节格式固定,后面紧跟的各种描述结构跟实际配置有关,每增加一种描述结构,该描述结构的第一字节说明了结构的长度,第二直接说明了结构的类型。在配置描述符中一般包含配置描述、接口描述、端点描述,如果需要同样可增加自定义的描述。使用标准USB设备类别时,配置描述符的结构也必须满足此类标准设备的数据结构。 const unsigned char cbDescriptor_Config[DESC_SIZE_CONFIG] = {
// Descriptor of configuration 0x09, // lengths
DESCRIPTOR_CONFIG, // descriptor type
DESC_SIZE_CONFIG, // Total configuration descriptor lengths LSB 0x00, // Total configuration descriptor lengths MSB
0x01, // bNumInterfaces: Total number of interfaces
http://www.hkaco.com
0x01, // bConfigurationValue: Configuration value
0x00, // iConfiguration: Index of string descriptor describing the configuration
0xA0, // bmAttributes: bus powered // bit 4...0 : Reserved, set to 0
// bit 5 : Remote wakeup (1:yes) // bit 6 : Self power (1:yes) // bit 7 : Reserved, set to 1
0x32, // bMaxPower: this current is used for detecting Vbus = 100mA
// Descriptor of interface 0x09,
DESCRIPTOR_INTERFACE,
0x00, // bInterfaceNumber: Number of Interface 0x00, // bAlternateSetting: Alternate setting
0x02, // bNumEndpoints: Number of endpoints except EP0 0x00, // bInterfaceClass: 0x00, // bInterfaceSubClass: 0x00, // nInterfaceProtocol:
0x00, // iInterface: Index of string descriptor describing the interface
// Descriptor of endpoint1 OUT 0x07,
DESCRIPTOR_ENDPOINT,
0x01, // bEndpointAddress
// bit 3...0 : the endpoint number // bit 6...4 : reserved
// bit 7 : 0(OUT), 1(IN)
0x03, // bmAttributes
// bit 1...0 : Transfer type
// 00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT) // bit 3...2 : Synchronization type
// 00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous) // bit 5...4 : Endpoint Usage type
// 00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved)
http://www.hkaco.com
// bit 7...6 : Reserved, must be zero
0x40, // packet size LSB 0x00, // packet size MSB
0x20, // polling interval time: 32ms
// Descriptor of endpoint2 IN 0x07,
DESCRIPTOR_ENDPOINT,
0x82, // bEndpointAddress
// bit 3...0 : the endpoint number // bit 6...4 : reserved
// bit 7 : 0(OUT), 1(IN)
0x03, // bmAttributes
// bit 1...0 : Transfer type
// 00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT) // bit 3...2 : Synchronization type
// 00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous) // bit 5...4 : Endpoint Usage type // 00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved) // bit 7...6 : Reserved, must be zero
0x40, // packet size LSB 0x00, // packet size MSB
0x20 // polling interval time: 32ms };
字符串描述符:定义了与设备有关的一些信息,常见的为以下四种,如果有需要,同样可以定义自己的字符串描述符。
const unsigned char cbDescriptor_StringLangID[DESC_SIZE_STRING_LANGID] = {
DESC_SIZE_STRING_LANGID, // bLength
DESCRIPTOR_STRING, // bDescriptorType = String Descriptor
0x09, // LangID LSB:
0x04 // LangID MSB: 0x0409(U.S. English) };
const unsigned char cbDescriptor_StringVendor[DESC_SIZE_STRING_VENDOR] =
http://www.hkaco.com
{
DESC_SIZE_STRING_VENDOR, // bLength
DESCRIPTOR_STRING, // bDescriptorType = String Descriptor
// String: \
'L',0, 'a',0, 'B',0, 'i',0, 'X',0, 'i',0, 'a',0, 'o',0, 'X',0, 'i',0, 'a',0, 'o',0, 'X',0, 'i',0, 'n',0 };
const unsigned char cbDescriptor_StringProduct[DESC_SIZE_STRING_PRODUCT] = {
DESC_SIZE_STRING_PRODUCT, // bLength
DESCRIPTOR_STRING, // bDescriptorType = String Descriptor
// String: \
'S',0, 'T',0, 'M',0, '3',0, '2',0, ' ',0, 'e',0, 'z',0, 'U',0, 'S',0, 'B',0, '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0, 'V',0, '1',0, '.',0, '0',0, '1',0 };
const unsigned char cbDescriptor_StringSerial[DESC_SIZE_STRING_SERIAL] = {
DESC_SIZE_STRING_SERIAL, // bLength
DESCRIPTOR_STRING, // bDescriptorType = String Descriptor
// String: \
'e',0, 'z',0, 'U',0, 'S',0, 'B',0, '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0,
'D',0, 'e',0, 'm',0, 'o',0, ' ',0, '2',0, '0',0, '0',0, '8',0, '/',0, '1',0, '1',0, '/',0, '1',0, '8',0 };
了解这些描述符的用法以及作用,最好的方法的是编写自定义的USB上位机驱动以及应用程序,这样你可以深刻了解USB设备与主机间的数据交换方式以及实现手段,下篇将开始介绍USB上位机驱动以及应用程序的编写以及开发环境的建立,STM32 USB设备的固件程序如有什么疑问,请朋友们多花几分种时间留言、讨论,共同学习与进步。
第六篇:XP下USB驱动开发的初步准备工作
必须的先决条件:
1: XP DDK(Driver Development Kits),可从MS网站下载。(Windows 2000下请使用Windows 2000 DDK),具备后安装DDK,如果你觉得只需要利用DDK就可以开发驱动,那么接下来的内容完全可以不看,在这讨论的是利用DriverStudio的 DriverWinziard生成的驱动框架。因为纯粹利用DDK开发驱动将是项十分艰巨的工作,需要你了解太多的系统知识,开发全部基于C语言,而且底层驱动处理稍微不当,就容易让你系统直接挂了(WINDOWS著名的蓝屏)。DDK提
http://www.hkaco.com
供了一些驱动代码,有兴趣的朋友可以参考参考。
2: Compuware DriverStudio V3.2:Compuware出品,是进行驱动开发最常见的平台,他封装了大部分设备驱动所必须的基本框架,以C++形式生成VC6.0或者 VS2002、2003、2005工程,用户一般只需要对该工程进行一些修改就可以完成最终目的。
3:代码开发环境VC6.0,这个大家都知道
安装了Windows XP DDK
(原文件名:1.JPG)
安装了DriverStudio
(原文件名:2.JPG)
之后,DriverStudio以插件形式嵌入到VC6:
http://www.hkaco.com
(原文件名:3.JPG)
这样,就可以进行驱动开发了,首先打开DriverWizard生成需要设计的驱动框架,以USB驱动为例需要特殊说明的是:
这里填写的Vendor ID和Product ID必须与USB设备固件程序里设备描述符里的Vendor ID和Product ID一致,这两个ID用以寻找配对的驱动。
(原文件名:4.JPG)
http://www.hkaco.com
这里添加的是USB端点特性,在生成的程序框架中,管道名称(Pipe Name)将作为程序的内部变量成员,派属KUsbPipe类,端点操作函数都集成在此类中。在这设置USB设备Enpoint 1为接收端口,Enpoint 2为发送端口。
(原文件名:5.JPG)
这里添加USB的控制操作方式,在NT平台下应用程序控制设备只能通过ReadFile()/WriteFile()和DeviceIoControl () 两做方式,执行ReadFile()/WriteF
http://www.hkaco.com
ile()将响应IRP_MJ_READ/IRP_MJ_WRITE请求,在这添加了 ReadFrom_EP2和WriteTo_EP1两个IRP_MJ_DEVICE_CONTROL请求代码,在执行DeviceIoControl() 时,可以根据请求这两个请求代码进行区别,DriverWizard生成的框架中将增添两个函数:
NTSTATUS ezUSBDevice::ReadFrom_EP2_Handler(KIrp I); NTSTATUS ezUSBDevice::WriteTo_EP1_Handler(KIrp I);
(原文件名:6.JPG)
如果DriverStudio首次安装后,请先利用VC6打开\\Compuware\\DriverStudio\\DriverWorks\\source\\ VdwLibs.dsw工程,然后按照以下方法编译:VC6主菜单->Bulid->Batch Bulid,按图示设置后点击Rebuild All,编译成功后关闭此项目:
http://www.hkaco.com
(原文件名:7.JPG)
DriverWizard 框架生成完成后,就可以在设定的路径下找到你设置的工程,至此就可以使用VC6打开工程。打开后请先打开VC6中DriverStudio插件:VC6主菜单->DriverStudio->DDK Build Settings,选择DDK目录:
http://www.hkaco.com
(原文件名:8.JPG)
至此就可以编译该工程了,如果提示找不到库:ntstrsafe.lib,请删除此库:
http://www.hkaco.com
(原文件名:9.JPG)
到此,对利用DriverStudio进行USB驱动开发的开发环境的设置做了一些着重的介绍,具体工程如出现一些特殊情况请利用网络资源收集,下篇将介绍USB应用程序与驱动之间的数据交换。
第七篇: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)
http://www.hkaco.com
#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,
http://www.hkaco.com
*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);
http://www.hkaco.com
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;
http://www.hkaco.com
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);
http://www.hkaco.com
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
http://www.hkaco.com
2);
SetBuffDescTable_TXCount(ENDP2, 2); SetEPR_RXStatus(ENDP2,EP_RX_DIS); SetEPR_TXStatus(ENDP2,EP_TX_VALID); }
至此,整个基于STM32的USB开发过程的介绍大致说了一遍,详细情况请参考源代码,这篇学习笔记到此结束了,谢谢这段时间关心与支持的朋友们,水平有限欢迎朋友们一起探讨学习。 2008-11-25
http://www.hkaco.com
2);
SetBuffDescTable_TXCount(ENDP2, 2); SetEPR_RXStatus(ENDP2,EP_RX_DIS); SetEPR_TXStatus(ENDP2,EP_TX_VALID); }
至此,整个基于STM32的USB开发过程的介绍大致说了一遍,详细情况请参考源代码,这篇学习笔记到此结束了,谢谢这段时间关心与支持的朋友们,水平有限欢迎朋友们一起探讨学习。 2008-11-25
正在阅读:
基于STM32的USB程序开发笔记01-27
2019高考化学二轮复习专题复习回扣基础排查考点五离子共存问题归纳练习09-04
茶艺文化解析02-21
童年的游戏作文小学生二年级06-13
基于社会网络重建的历史街区保护与_省略_以重庆市长寿区三倒拐历史街区为例_赵万民08-16
对外直接投资对母国就业的影响04-30
游宜都九凤谷作文600字07-15
中国炼铁技术研究与应用进展动态05-10
Java操作Hadoop集群文件的步骤12-19
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 程序开发
- 基于
- 笔记
- STM32
- USB
- 父母不是孩子的答案
- EMC Documentum内容管理平台简介
- 浙江省绍兴市2017年中考英语真题试题(含解析)
- 3000万只肉鸭屠宰加工项目
- 2013年05月30日当前幼儿园班级管理存在的问题、成因与对策研究
- 焊 接 施 工 记 录 表
- 蒸压加气混凝土砌块砌体工程施工及质量验收规程DB34
- 资产评估多项选择题
- 图书馆概况
- 住宅小区高层剪力墙结构住宅楼施工组织设计 - 图文
- 高相液相综述
- 公差配合与技术测量基础教案
- 1、ZJ50D、ZJ70D钻机主要设备安装使用技术要求 - 图文
- 疟原虫镜检技术操作规
- 校园文化创意品牌营销大赛策划案
- 电大商法任务6在线考试答案0041
- 甘肃省兰州市2016届中考数学一诊试题(含解析)
- 江苏密卷语文六年级上
- 联合试运转方案10.1
- 比喻 - 让初中化学课堂教学更精彩