接着上篇,详细情况可以查看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); 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 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);
// --------------------------------------------------------------------- // 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 // 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 #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; 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 { 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; } } } } }
INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个需要查看STM32的参考手册以及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事件数据长度固定为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;
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协议中,程序根据请求代码,再去执行对应函数,这样做的目的就是让程序结构明了。其中注释为"// done"的部分表明此部分功能已完成。对于未完成部分,希望大家在交流中完善。
// ***************************************************************************** // Routine Groups: SETUP_Data // ***************************************************************************** RESULT SETUP0_Data(void) { // SetupData.bRequest: request code switch(vsDeviceInfo.SetupData.bRequest) { 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; } }
|