#include <stm32f10x_lib.h> #include "usbreg.h" #include "usbcore.h" #include "usbuser.h" #include "usbcfg.h" #include "usb.h" #include "usb_hw.h" #include "usbdesc.h" #include "hid.h" #include "hiduser.h" #define _DEBUG_ #include "debug.h" #pragma diag_suppress 111,1441 //用来指示USB设备的状态 WORD USB_DeviceStatus; //用来存储设备的地址 BYTE USB_DeviceAddress; //用来存储USB当前使用的设备的配置 BYTE USB_Configuration; //此配置使用端点 DWORD USB_EndPointMask; //用于标志此端点是否已经被停止0~15依次代表15个端点 DWORD USB_EndPointHalt; //此配置使用的接口数 BYTE USB_NumInterfaces; //每个接口可用的当前接口可替换值 BYTE USB_AltSetting[USB_IF_NUM]; /*用于临时存储控制传输时的数据包*/ USB_SETUP_PACKET SetupPacket; /*用于向主机发送数据的EP0的数据结构*/ USB_EP_DATA EP0Data; /*EP0Buf用于向USB发送数据时用的缓冲区*/ BYTE EP0Buf[USB_MAX_PACKET0]; /*功能:复位USB的一些数据标志 *参数:无 *返回值:无 */ void USB_ResetCore(void) { //默认为总线供电,因为我们的USB现在不都是插在电脑上才能工作的么&^_^ USB_DeviceStatus = USB_POWER; //在枚举之初地址当然是0 USB_DeviceAddress = 0; //配置描述符的标识从1开始,这里也先置为0 USB_Configuration = 0; //目前使用的是端点0 USB_EndPointMask = 0x00010001; //没有停止的端点 USB_EndPointHalt = 0x00000000; } /*功能:建立阶段,读取建立数据包 *参数:无 *返回值:无 */ void USB_SetupStage(void) { USB_ReadEP(0x00,(BYTE*)&SetupPacket); } /*功能:建立阶段,In握手包 *参数:无 *返回值:无 */ void USB_StatusInStage(void) { USB_WriteEP(0x80,NULL,0); } /*功能:建立阶段,Out握手包 *参数:无 *返回值:无 */ void USB_StatusOutStage(void) { USB_ReadEP(0x00,EP0Buf); } /*功能:数据In阶段 *参数:无 *返回值:无 */ void USB_DataInStage(void) { DWORD cnt; //先计算引次要发送多少数据 if(EP0Data.Count > USB_MAX_PACKET0) cnt = USB_MAX_PACKET0; else cnt = EP0Data.Count; //这里写端点却主机读,则将Dir位置1 cnt = USB_WriteEP(0x80,EP0Data.pData,cnt); EP0Data.pData += cnt; EP0Data.Count -= cnt; } /*功能:数据Out阶段 *参数:无 *返回值:无 */ void USB_DataOutStage(void) { DWORD cnt; cnt = USB_ReadEP(0x00,EP0Data.pData); EP0Data.pData+=cnt; EP0Data.Count-=cnt; } /*功能:获取USB设备的状态 *参数:无 *返回值:TRUE --->成功 * FALSE --->错误 */ __inline BOOL USB_GetStatus(void) { DWORD n,m; switch(SetupPacket.bmRequestType.BM.Recipient) { //接收端是设备 case REQUEST_TO_DEVICE: //返回设备状态给他 EP0Data.pData = (BYTE *)&USB_DeviceStatus; //将状态信息发送给主机 USB_DataInStage(); break; //接收端是接口 case REQUEST_TO_INTERFACE: /*配置描述符的标识从1开始,并且请求的接口号不能大于接口的数目,因为接口数目从0开始 *因为我们接口描述符中的接口号是一个字节所以这里wIndex中的数据中人低字节有效 */ if((USB_Configuration !=0)&&(SetupPacket.wIndex.WB.L < USB_NumInterfaces)) { //清0两个字节,因为根据USB协议此处必须返回0 *((__packed WORD *)EP0Buf) = 0; EP0Data.pData = EP0Buf; //发送给主机 USB_DataInStage(); } //此接口出现了错误 else return FALSE; break; case REQUEST_TO_ENDPOINT: //端点号高1位0方向,低4位为端点号 n = SetupPacket.wIndex.WB.L & 0x8f; //m的高16位代表in端点的标志,低16位代表out端点的标志 m = (n&0x80)?(1<<16 )<< (n&0x0f) :(1<<n); //如果配置标识不为0,或配置标识为0,但是是端点0时,才算正常 if((USB_Configuration !=0)||((n&0x0f)==0)&&(USB_EndPointMask & m)) { //查看此端点是否已经停用 *((__packed WORD *)EP0Buf) = (USB_EndPointHalt &m )?1:0; EP0Data.pData = EP0Buf; //将数据发送给主机 USB_DataInStage(); } //说明配置描述符出了问题 else return FALSE; break; default: return FALSE; } return TRUE; } /*功能:设置/清除USB设备的特征 *参数:sc 0----->清除 1----->设置 *返回值:TRUE --->成功 * FALSE --->错误 */ __inline BOOL USB_SetClrFeature(DWORD sc) { DWORD n,m; switch(SetupPacket.bmRequestType.BM.Recipient) { //接收端是设备,则清除或设置设备的特性 case REQUEST_TO_DEVICE: if(SetupPacket.wValue.W == USB_FEATURE_REMOTE_WAKEUP) { if(sc) { printf("设置设备远程唤醒特性\r\n"); //设置USB状态为使能远程唤醒 USB_DeviceStatus |= USB_GETSTATUS_REMOTE_WAKEUP; /*stm32硬件本身就支持远程唤醒,这里就不用设置了 *当然,软件支不支持在于对中断屏蔽位的设置 */ } else { printf("清除设备远程唤醒特性\r\n"); USB_DeviceStatus &= ~USB_GETSTATUS_REMOTE_WAKEUP; /*stm32硬件本身就支持远程唤醒,这里就不用设置了 *当然,软件支不支持在于对中断屏蔽位的设置 */ } } //不支持的请求 else { printf("不支持的请求\r\n"); return FALSE; } break; //接收端是接口 case REQUEST_TO_INTERFACE: //接口不支持清除特性 printf("清楚设置接口特征不被USB2.0支持\r\n"); return FALSE; //接收端是端点 case REQUEST_TO_ENDPOINT: //取得端点号 n = SetupPacket.wIndex.WB.L & 0x8F; // printf("请求的端点是:"); // printhex(n); // printf("USB_Configuration:"); // printhex(USB_Configuration); // printf("USB_EndPointMask"); // printhex(USB_EndPointMask); // printf("\r\n"); //将端点转化成与EndPointMask一样的格式 m = (n & 0x80)?((1<<16) << (n&0x0f)) : (1<<n); /*根据USB协议来说, *地址状态下只有端点0才能使用, *配置状态下可用 *不知道他这句是不是bug *原文件引用: *This request is valid when the device is in the Address state; *references to interfaces or to endpoints other than endpoint zero *shall cause the device to respond with a Request Error */ /* 此处认为只用配置状态下的非0端点才能清楚特征 ,至于地址状态下没有处理 * */ if((USB_Configuration !=0) && ((n&0x0f)!=0) && (USB_EndPointMask & m)) { //设置/清楚 某个端点stall状态 if(SetupPacket.wValue.W == USB_FEATURE_ENDPOINT_STALL) { //设置 if(sc) { printf("设置端点远程唤醒特性\r\n"); //设置请求的端点为stall USB_SetStallEP(n); //更新USB的状态位 USB_EndPointHalt |= m; } //清楚 else { printf("清楚端点远程唤醒特性\r\n"); USB_ClrStallEP(n); USB_EndPointHalt &= ~m; } } //未定义的请求 else { printf("不正确的端点请求\r\n"); return FALSE; } } //不正确的请求时机 else { printf("不正确的请求时机\r\n"); return FALSE; } break; //未定义的接收端类型 default: printf("未定义的接收端类型\r\n"); printf("接收端为:"); printhex(SetupPacket.bmRequestType.BM.Recipient); printf("\r\n"); return FALSE; } return TRUE; } /*功能:用于向主机发送描述符 *参数:无 *返回值:TRUE --->成功 * FALSE --->发生了错误 */ __inline BOOL USB_GetDescriptor() { BYTE * pD; DWORD len,n; switch (SetupPacket.bmRequestType.BM.Recipient) { //发给设备的请求 case REQUEST_TO_DEVICE: switch(SetupPacket.wValue.WB.H) { //获取设备描述符 case USB_DEVICE_DESCRIPTOR_TYPE: printf("获取设备描述符\r\n"); EP0Data.pData = (BYTE *)USB_DeviceDescriptor; len = USB_DEVICE_DESC_SIZE; break; //获取配置描述符 case USB_CONFIGURATION_DESCRIPTOR_TYPE: printf("获取USB配置描述符\r\n"); pD = (BYTE *)USB_ConfigDescriptor; /* 引用USB协议:The range of values used for a * descriptor index is from 0 to one less than * the number of descriptors of that type implemented by the device * 配置描述符中的标识是从1开始计的,但是,请求中的值是标识值为配置描述符 */ /*由于可能有几个配置描述符所以可能需要描述一下指针*/ for(n=0;n!= SetupPacket.wValue.WB.L;n++) { if(((USB_CONFIGURATION_DESCRIPTOR *)pD)-> bLength != 0) { pD+=((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; } } //如果请求的描述符为空则返回stall if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0) return FALSE; //设置要传送的指针 EP0Data.pData = pD; //虽然配置描述符中多了一个字节,但是wTotalLength中没有算上,所以这里不用管,直接用就行了 len = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->wTotalLength; break; //字符串描述符 case USB_STRING_DESCRIPTOR_TYPE: printf("获取字符串描述符\r\n"); EP0Data.pData = (BYTE *)USB_StringDescriptor + SetupPacket.wValue.WB.L; len = ((USB_STRING_DESCRIPTOR *)EP0Data.pData)->bLength; break; default: return FALSE; } break; //请求接收端接口 // case REQUEST_TO_INTERFACE: // printf("请求接收端接口\r\n"); // switch(SetupPacket.wValue.WB.H) // { // //Hid 描述符 // case HID_HID_DESCRIPTOR_TYPE: // printf("请求HID描\r\n"); // break; // case HID_REPORT_DESCRIPTOR_TYPE: // printf("请求报告描述符\r\n"); // //这里只有一个报告描述符,报告描述符的索引是从0开始计,跟数组是一样的,如是不是0则返回Stall // if(SetupPacket.wIndex.WB.L != 0) // return FALSE; // EP0Data.pData = (BYTE *)HID_ReportDescriptor; // printf("报告描述符请求的字节:"); // printhex(EP0Data.Count); // printf("报告描述符本来的长度:"); // printhex(HID_ReportDescSize); // printf("\r\n"); // len = HID_ReportDescSize; // break; // default: // printf("未识别的请求!!!###,类型是:"); // printhex(SetupPacket.wValue.WB.H); // printf("\r\n"); // break; // } // break; default: return FALSE; } printf("获取描述符成功\r\n"); //将数据发送给主机 if(EP0Data.Count > len) EP0Data.Count = len; USB_DataInStage(); return TRUE; } /*功能:设置配置请求 *参数:无 *返回值:TRUE --- >成功 * FALSE --- >失败 */ __inline BOOL USB_SetConfiguration(void) { USB_COMMON_DESCRIPTOR * pD; DWORD alt,n,m; printf("设置配置\r\n"); //配置值不能为0 if(SetupPacket.wValue.WB.L) { pD=(USB_COMMON_DESCRIPTOR *)USB_ConfigDescriptor; //循环到配置描述符中的最后一个端点描述符 while(pD->bLength) { switch(pD->bDescriptorType) { //此时是配置描述符 case USB_CONFIGURATION_DESCRIPTOR_TYPE: //如果此配置描述符是主机要设置的配置描述符 if(((USB_CONFIGURATION_DESCRIPTOR * )pD)->bConfigurationValue == SetupPacket.wValue.WB.L) { //保存起此时,设备所用的配置值 USB_Configuration = SetupPacket.wValue.WB.L; //保存起此时,设备所用的配置的接口数目 USB_NumInterfaces = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->bNumInterfaces; //清0每个接口可替换设置值 for(n=0;n<USB_IF_NUM;n++) { USB_AltSetting[n]=0; } //我们将所有的以前可能使用的端点禁用,如果需要,在端点描述符中再使能就行了 //这样如果更改了配置就保证了,不会使用到以前配置使用,而本次配置没有使用的端点 //可能是为了防止干扰,猜的^-^! for(n=1;n<16;n++) { if(USB_EndPointMask & (1<<n)) USB_DisableEP(n); if(USB_EndPointMask & ((1<<16)<<n)) USB_DisableEP(n|0x80); } //还没有枚举到端点因此这里只使用的是端点0 USB_EndPointMask = 0x00010001; //刚配置,当然没有停止的端点啊,全新的&_^ USB_EndPointHalt = 0x00000000; /*在复位时,我们默认是总线供电,现在开始正式运行以前要因为主机会根据我们的配 *置描述符进行调整,一旦配置描述符返回后,配置描述符就生效了,所以这里必须来依据实际 *来重新设置 */ if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_SELF_POWERED) USB_DeviceStatus |= USB_GETSTATUS_SELF_POWERED; else USB_DeviceStatus &= ~USB_GETSTATUS_SELF_POWERED; } //否则略过,下一个配置描述符 else { (BYTE *)pD += ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; continue; } break; //下面是接口描述符 case USB_INTERFACE_DESCRIPTOR_TYPE: alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting; break; case USB_ENDPOINT_DESCRIPTOR_TYPE: //我们仅仅需要处理多个子接口中的第一个就行了,(如果有子接口的话,没有的话刚好也是0) if(alt == 0) { //得到此端点 n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8f; //将端点号转化成bit以存储在USB_EndPointMask中去代表我们使用了此端点 m = (n&0x80)?((1<<16)<<(n&0x0f)):(1<<n); USB_EndPointMask |= m; //配置端点 USB_ConfigEP((USB_ENDPOINT_DESCRIPTOR *)pD); //使能端点 USB_EnableEP(n); //复位端点,使端点处于确定的端点 USB_ResetEP(n); } break; } (BYTE *)pD += pD->bLength; } } //如果请求的配置为0,则我们将所有的配置恢复到地址状态时的配置 else { USB_Configuration = 0; for(n=1;n<16;n++) { //如果上一个配置使用了此端点,则disable it if(USB_EndPointMask & (1<<n)) //OUT端点 USB_DisableEP(n); if(USB_EndPointMask & (1<<16)<<n) //IN端点 USB_DisableEP(n | 0x80); } //清除使用的全部端点,(当然0端点还是除外的) USB_EndPointMask = 0x00010001; USB_EndPointHalt = 0x00000000; } //我们根据USB_Configuration的值来查看我们到底时否找到了所需要的值 if(USB_Configuration == SetupPacket.wValue.WB.L) { printf("设置配置成功\r\n"); return TRUE; } else { printf("设置配置失败\r\n"); return FALSE; } } /*功能:用于处理端点0的输入和输出请求函数 *参数: USB_EVT_SETUP 枚举时上位机发送的数据包 * USB_EVT_OUT 普通传输时,上位机发送的数据包 * USB_EVT_IN 普通传输时,上位机请求发送数据 *返回值:无 */ void USB_EndPoint0 (DWORD event) { // printf("进入端点0处理函数\r\n"); switch (event) { //控制传输 case USB_EVT_SETUP: // printf("进入建立阶段\r\n"); //首先读取主机发来的请求 USB_SetupStage(); //设置EP0的数据结构 EP0Data.Count = SetupPacket.wLength; //识别是何请求 // printf("请求是"); // printhex(SetupPacket.bRequest); // printf(" 被请求对象:"); // printhex(SetupPacket.bmRequestType.B); // printf("\r\n"); //bRequestType.bit5~bit6代表请求类型 switch(SetupPacket.bmRequestType.BM.Type) { //标准请求 case REQUEST_STANDARD: switch(SetupPacket.bRequest) { //获取USB状态 case USB_REQUEST_GET_STATUS: //如果状态失败则停止0x81端点 if(!USB_GetStatus()) { printf("获取状态失败\r\n"); goto stall_i; } break; case USB_REQUEST_CLEAR_FEATURE: //如果清除特征失败则停止0x81端点 if(!USB_SetClrFeature(0)) { printf("清楚特征失败\r\n"); goto stall_i; } //成功了握一下手OK USB_StatusInStage(); break; case USB_REQUEST_SET_FEATURE: //如果状态失败则停止0x81端点 if(!USB_SetClrFeature(1)) { printf("设置特征失败\r\n"); goto stall_i; } //成功了握一下手OK USB_StatusInStage(); break; case USB_REQUEST_SET_ADDRESS: switch(SetupPacket.bmRequestType.BM.Recipient) { //只支持向设备设置地址 /*USB协议指出必须在改地址前完成检测并对此信息包进行响应 *即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行 *当主机接收到状态阶段的0数据包时将会产生接收中断 *所以放到了USB_EVT_IN处*/ case REQUEST_TO_DEVICE: printf("设置地址\r\n"); USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L; USB_StatusInStage(); break; default: printf("未识别的接收端,接收端是"); printhex(SetupPacket.bmRequestType.BM.Recipient); printf("\r\n"); goto stall_i; } break; //获取设置描述符 case USB_REQUEST_GET_DESCRIPTOR: if(!USB_GetDescriptor()) goto stall_i; break; //设置配置请求 case USB_REQUEST_SET_CONFIGURATION: switch(SetupPacket.bmRequestType.BM.Recipient) { //对设备 case REQUEST_TO_DEVICE: if(!USB_SetConfiguration()) { //如果失败则对主机返回stall goto stall_i; } //完成了请求,握个手OK USB_StatusInStage(); break; //不支持对其他类型请求 default: goto stall_i; } break; case USB_REQUEST_GET_INTERFACE: printf("请求接口\r\n"); break; default: printf("未识别的请求,请求代码是"); printhex(SetupPacket.bRequest); printf("\r\n"); break; } break; //类请求 case REQUEST_CLASS: printf("类请求\r\n"); switch (SetupPacket.bmRequestType.BM.Recipient) { //对接口请求 case REQUEST_TO_INTERFACE: if(SetupPacket.wIndex.WB.L == USB_HID_IF_NUM) { switch(SetupPacket.bRequest) { case HID_REQUEST_GET_REPORT: printf("获取报告描述符\r\n"); break; case HID_REQUEST_SET_REPORT: printf("设置报告描述符\r\n"); break; case HID_REQUEST_GET_IDLE: printf("获取空闲率\r\n"); break; case HID_REQUEST_SET_IDLE: printf("设置空闲\r\n"); break; case HID_REQUEST_GET_PROTOCOL: printf("查询报告协议是否在活动\r\n"); break; case HID_REQUEST_SET_PROTOCOL: printf("SetReprot\r\n"); break; default: printf("不支持的HID请求\r\n"); break; } } break; } break; //产商请求 case REQUEST_VENDOR: //未定义请求 default: stall_i: USB_SetStallEP(0x80); break; } break; //接收成功 case USB_EVT_OUT: printf("USB_EVT_OUT\r\n"); if(SetupPacket.bmRequestType.BM.Dir ==0) { printf(" 请求是:"); printhex(SetupPacket.bRequest); printf("\r\n"); } //返回握手 else { USB_StatusOutStage(); } break; //发送成功 case USB_EVT_IN: // printf("USB_EVT_IN\r\n"); if(SetupPacket.bmRequestType.BM.Dir == 1) USB_DataInStage(); /*USB协议指出必须在改地址前完成检测并对此信息包进行响应 *即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行 *当主机接收到状态阶段的0数据包时将会产生接收中断 *所以放到了USB_EVT_IN处*/ //原keil库没有加if判断,怀疑为bug,这里判断是否是由于设置地址的状态阶段成功而触发的中断 else// if(SetupPacket.bRequest == USB_REQUEST_SET_ADDRESS) { if(USB_DeviceAddress & 0x80) { USB_DeviceAddress &= 0x7f; USB_SetAddress(USB_DeviceAddress); } } break; } }