关于stm32的USB学习笔记之usbcore.c

来源:互联网 发布:弹壳k9999知乎 编辑:程序博客网 时间:2024/05/22 10:48
#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设备的状态WORDUSB_DeviceStatus;//用来存储设备的地址BYTEUSB_DeviceAddress;//用来存储USB当前使用的设备的配置BYTEUSB_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发送数据时用的缓冲区*/BYTEEP0Buf[USB_MAX_PACKET0];/*功能:复位USB的一些数据标志 *参数:无 *返回值:无 */voidUSB_ResetCore(void){//默认为总线供电,因为我们的USB现在不都是插在电脑上才能工作的么&^_^USB_DeviceStatus =USB_POWER;//在枚举之初地址当然是0USB_DeviceAddress=0;//配置描述符的标识从1开始,这里也先置为0USB_Configuration=0;//目前使用的是端点0USB_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){DWORDcnt;//先计算引次要发送多少数据if(EP0Data.Count > USB_MAX_PACKET0)cnt= USB_MAX_PACKET0;elsecnt= EP0Data.Count;//这里写端点却主机读,则将Dir位置1   cnt= USB_WriteEP(0x80,EP0Data.pData,cnt);EP0Data.pData+=cnt;EP0Data.Count-=cnt;}/*功能:数据Out阶段 *参数:无 *返回值:无 */void USB_DataOutStage(void){ DWORDcnt; cnt=USB_ReadEP(0x00,EP0Data.pData); EP0Data.pData+=cnt; EP0Data.Count-=cnt;}/*功能:获取USB设备的状态 *参数:无  *返回值:TRUE--->成功 * FALSE--->错误 */__inline BOOLUSB_GetStatus(void){DWORDn,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();}//此接口出现了错误elsereturnFALSE;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();}//说明配置描述符出了问题elsereturnFALSE;break;default:returnFALSE;}  return TRUE;}/*功能:设置/清除USB设备的特征 *参数:sc 0----->清除1----->设置 *返回值:TRUE--->成功 * FALSE--->错误 */__inline BOOL USB_SetClrFeature(DWORDsc){DWORDn,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");//设置请求的端点为stallUSB_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");returnFALSE;}break;//未定义的接收端类型default:printf("未定义的接收端类型\r\n");printf("接收端为:");printhex(SetupPacket.bmRequestType.BM.Recipient);printf("\r\n");returnFALSE;}return TRUE;}/*功能:用于向主机发送描述符 *参数:无  *返回值:TRUE--->成功 * FALSE--->发生了错误 */__inline BOOLUSB_GetDescriptor(){BYTE * pD;DWORDlen,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;}}//如果请求的描述符为空则返回stallif(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0)returnFALSE;//设置要传送的指针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:returnFALSE;}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:returnFALSE; }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;DWORDalt,n,m;printf("设置配置\r\n");//配置值不能为0if(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);}//还没有枚举到端点因此这里只使用的是端点0USB_EndPointMask = 0x00010001;//刚配置,当然没有停止的端点啊,全新的&_^USB_EndPointHalt = 0x00000000;/*在复位时,我们默认是总线供电,现在开始正式运行以前要因为主机会根据我们的配 *置描述符进行调整,一旦配置描述符返回后,配置描述符就生效了,所以这里必须来依据实际 *来重新设置 */if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_SELF_POWERED)USB_DeviceStatus |= USB_GETSTATUS_SELF_POWERED;elseUSB_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 itif(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");returnTRUE;}else{printf("设置配置失败\r\n");returnFALSE;}}/*功能:用于处理端点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");gotostall_i;}break;case USB_REQUEST_CLEAR_FEATURE://如果清除特征失败则停止0x81端点if(!USB_SetClrFeature(0)){printf("清楚特征失败\r\n");goto stall_i;}//成功了握一下手OKUSB_StatusInStage();break;case USB_REQUEST_SET_FEATURE://如果状态失败则停止0x81端点if(!USB_SetClrFeature(1)){printf("设置特征失败\r\n");goto stall_i;}//成功了握一下手OKUSB_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");gotostall_i;}break;//获取设置描述符case USB_REQUEST_GET_DESCRIPTOR:if(!USB_GetDescriptor())gotostall_i;break;//设置配置请求case USB_REQUEST_SET_CONFIGURATION:switch(SetupPacket.bmRequestType.BM.Recipient){//对设备 case REQUEST_TO_DEVICE:if(!USB_SetConfiguration()){//如果失败则对主机返回stallgoto stall_i;}//完成了请求,握个手OKUSB_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;}}