NUC970 裸机USBD驱动(第一章)

官方提供的USBD驱动太随意,根本没法直接使用,折腾好久原因是我的开发板上面使用了一个很老的HUB芯片,是USB1.1的,导致没法使用USB2.0,后续测试都是基于USB1.1的,实际上USB2.0会更简单,因为支持的bluk可以支持512字节,USB1.1只有64字节,但是实际使用中除了速度区别,别的区别不大,因为NUC970的USB接收缓冲区非常方便,比如实际上bluk只有64字节,但是设置缓冲区大小512字节,也可以非常容易的接收512字节及以上的数据包,只要主机OUT,设备会自动接收,但是也有个问题,就是CBW数据包会跟bluk数据包混在一起,这个时候就不好处理了,貌似NUC970是自动流控,就是只要缓冲区有数据,就会自动接收来自主机的OUT数据。

先上USBD.c代码,主要是底层硬件操作,跟USB协议无关部分,将所有中断函数使用回调方式暴露出来,无需着重的去处理某个中断,实际内部已经做好了处理。

/*************************************************************************************************************
 * 文件名:			usbd.c
 * 功能:			NUC970 USB设备相关驱动
 * 作者:			cp1300@139.com
 * 创建时间:		2020-10-12
 * 最后修改时间:	2020-10-12
 * 详细:			
*************************************************************************************************************/
#include "nuc970_system.h"
#include "usbd.h"
#include "typedef.h"
#include "irq_aic.h"
#include "usbd_req.h"

#define USBD_MAX_DMA_LEN    		0x1000								//DMA最大传输数据长度
		
//USBD所需全局变量定义==必须4字节对齐
#pragma pack(4)
typedef struct
{
	const USBD_DESCRIPTOR *pDescData;									//全局的描述符信息
	USBD_IRQHandlerType pUSBD_BusIRQHandlerCallBack;					//总线中断服务程序回调处理
	USBD_IRQHandlerType pUSBD_cEpIRQHandlerCallBack;					//控制端点中断服务程序回调处理
	USBD_CLASS_REQ pClassReqCallBack;									//类别请求回调处理
	USBD_VENDOR_REQ pVendorReqCallBack;									//厂商请求回调处理
	volatile u8	 DeviceAddr;											//USB设备地址
	volatile bool isTestMode;											//是否为测试模式
	volatile u8	TestSelector;											//选择的测试通道
	volatile bool isConfig;												//主机配置设备
	volatile u8	 ConfigValue;											//主机发送的配置参数
	volatile u8	 UsbAltInterface;										//主机指定的接口描述符
	volatile u8 CtrSendDataLen;											//控制待发送命令数据长度,待控制端口IN令牌有效后将会发送数据
	volatile u8 CtrReadDataLen;											//读取的控制端点数据长度
	volatile u8	 CtrSendDataBuff[128];									//控制待发送命令缓冲区
	volatile u8 *pCtrlInPointer;										//指向 CtrDataBuff 缓冲区的指针
	volatile u8	 CtrReadDataBuff[64];									//读取的控制端点数据缓冲区
}USBD_GLOBAL_DTAT;
#pragma pack()

static void USBD_IRQHandler(void);										//中断服务程序
static USBD_EP_CONFIG sg_EpConfig[USBD_MAX_EP];							//全局EP配置信息
static USBD_GLOBAL_DTAT sg_USBD_Data;									//USBD所需全局变量结构体
USBD_SETUP_TYPE g_USBD_Setup;											//全局USB SETUP信息结构体

#define USBD_EP_INIT_ID			0x13542557													//端点是否初始化
#define USBD_SET_MAX_PAYLOAD(ep, size)  	(USBD->EPx[ep].MPS = (size))					//设置端点x的最大数据包长度
#define USBD_WriteCEP_DataByte(data) 		(*((vu8*)(&(USBD->CEPDAT))) = (data))			//写入BYTE数据到控制端点数据发送寄存器
#define USBD_ReadCEP_DataByte() 			(*((vu8*)(&(USBD->CEPDAT))))					//从控制端点数据接收寄存器读取BYTE数据


static void USBD_SetEpConfig(USBD_EP_CONFIG *pConfig);							//设置一个端点(会配置到硬件中)
static void USBD_SetEpBuffAddr(USBD_EPx EPx, u32 EpBuffOffset, u32 EpBuffSzie);	//设置端点缓冲区地址(会配置到硬件中)
static void USBD_StandardRequest(void);											//标准请求处理
static void USBD_SetupHandle(void);												//USB Setup处理
static bool USBD_DMA_TransferData(USBD_EPx EPx, u32 BuffAddr, u16 Len, bool isShortPacket);	//DAM传输数据(不会设置传输方向)



/*************************************************************************************************************************
* 函数			:	void USBD_Init(void)
* 功能			:	初始化USBD
* 参数			:	无
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:
*************************************************************************************************************************/
void USBD_Init(void)
{
	outpw(REG_SYS_GPH_MFPL, (inpw(REG_SYS_GPH_MFPL) & ~0xf) | 0x7);
	SYS_DeviceClockEnable(DEV_USBD, TRUE);					//使能USBD时钟
	USBD_DISABLE_USB();
	USBD_DISABLE_PHY();
	SYS_DeviceReset(DEV_RESET_USBD);						//USBD复位
	SYS_DelayMS(10);
	
    USBD_ENABLE_PHY() ;										//使能USBD PHY
	SYS_DelayMS(1);
	memset(sg_EpConfig, 0, sizeof(sg_EpConfig));			//复位ep配置信息
	memset(&sg_USBD_Data, 0, sizeof(USBD_GLOBAL_DTAT));		//全局数据清除
	while(USBD->EPx[USBD_EPA].MPS != 0x08)
	{
		USBD->EPx[USBD_EPA].MPS = 0x08;						//写入并读取,看看是否一致,等待PHY时钟稳定
	}
	
	USBD_SET_ADDR(0);										//复位地址为0
	USBD_SetEpBuffAddr(USBD_CEP, CEP_BUFF_OFFSET, CEP_BUFF_SIZE);//设置控制端点缓冲区
	USBD_ENABLE_USB_INT(0x1FFF);							//开启所有USB中断
	//控制端口相关中断开启
	USBD_ENABLE_CEP_INT(USBD_CEPINTSTS_SETUPPKIF_Msk|		//控制端点SETUP数据包中断
						//USBD_CEPINTSTS_OUTTKIF_Msk|		//控制端点OUT令牌中断
						//USBD_CEPINTSTS_INTKIF_Msk|		//控制端点IN令牌中断-无需提前开启,在USBD_PrepareCtrlIn中开启,有数据发送才提前开启
						//USBD_CEPINTSTS_TXPKIF_Msk|		//控制端点数据发送中断-无需提前开启,收到IN令牌后有数据要发送就会自动开启
						USBD_CEPINTSTS_RXPKIF_Msk|			//控制端点数据接收中断
						USBD_CEPINTSTS_ERRIF_Msk			//控制端点错误中断
						//USBD_CEPINTSTS_STSDONEIF_Msk		//控制端点状态完成中断	
						);
	//BUS相关中断开启
	USBD_ENABLE_BUS_INT(USBD_BUSINTSTS_RSTIF_Msk|			//端口重置中断
						USBD_BUSINTSTS_RESUMEIF_Msk|		//设备恢复
						USBD_BUSINTSTS_SUSPENDIF_Msk|		//暂停
						USBD_BUSINTSTS_HISPDIF_Msk|			//设备已设置为高速
						USBD_BUSINTSTS_VBUSDETIF_Msk		//VBUS已插入
						);

	USBD_SET_OPER(USBD_OPER_HISPDEN_Msk); 					//设置为高速
	USBD_SET_SE0();
	USBD_SET_ADDR(0);										//复位地址为0
	
	AIC_SetIrqTriggered(AIC_USBD_INT, AIC_HIGHT_LEVEL);		//设置中断高电平触发
	AIC_SetIrqPriority(AIC_USBD_INT, SYS_INT_USBD_PRO);		//设置一个中断优先级
	AIC_RegisterIRQHandler(AIC_USBD_INT, USBD_IRQHandler);	//注册中断服务程序
	AIC_IrqEnable(AIC_USBD_INT, TRUE);						//开启AIC中断
}


/*************************************************************************************************************************
* 函数			:	void USBD_InterfaceConfig(USBD_IRQHandlerType pBusIRQCallBack, USBD_IRQHandlerType pCEpIRQCallBack, USBD_CLASS_REQ pClassReqCallBack, 
						USBD_VENDOR_REQ pVendorReqCallBack,const USBD_DESCRIPTOR *pDescData)
* 功能			:	USBD所需接口配置
* 参数			:	pBusIRQCallBack:BUS中断回调处理;pCEpIRQCallBack:控制端点中断回调处理;pClassReqCallBack:类别请求回调处理
					pVendorReqCallBack:厂商请求回调处理;pDescData:全局描述符数据信息
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:
*************************************************************************************************************************/
void USBD_InterfaceConfig(USBD_IRQHandlerType pBusIRQCallBack, USBD_IRQHandlerType pCEpIRQCallBack, USBD_CLASS_REQ pClassReqCallBack, 
	USBD_VENDOR_REQ pVendorReqCallBack,const USBD_DESCRIPTOR *pDescData)
{
	sg_USBD_Data.pUSBD_BusIRQHandlerCallBack = pBusIRQCallBack;	//总线中断服务程序回调处理		
	sg_USBD_Data.pUSBD_cEpIRQHandlerCallBack = pCEpIRQCallBack;	//控制端点中断服务程序回调处理	
	sg_USBD_Data.pClassReqCallBack = pClassReqCallBack;			//类别请求回调处理
	sg_USBD_Data.pVendorReqCallBack = pVendorReqCallBack;		//厂商请求回调处理
	sg_USBD_Data.pDescData = pDescData;							//描述符数据信息
}


/*************************************************************************************************************************
* 函数			:	void USBD_Start(void)
* 功能			:	USB启动(清除SE0)
* 参数			:	无
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	当初始化USB硬件后,监测到VBUS有效后调用,清除SE0状态
*************************************************************************************************************************/
void USBD_Start(void)
{
    USBD_CLR_SE0();
}

/*************************************************************************************************************************
* 函数			:	u16 USBD_ReadEPxOutData(USBD_EPx EPx, u8 *pData)
* 功能			:	从EPx的OUT缓冲区中读取数据
* 参数			:	EPx:EP端点选择,见USBD_EPx;pData:数据缓冲区(缓冲区长度必须大于一个包长度)
* 返回			:	数据长度
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	在端点收到数据后调用
*************************************************************************************************************************/
u16 USBD_ReadEPxOutData(USBD_EPx EPx, u8 *pData)
{
	u16 len,i;
	
	if(EPx > USBD_EPL) return 0;
	len = USBD->EPx[EPx].DATCNT & 0xffff;	//获取收到的数据长度
	for(i = 0;i < len;i ++)
	{
		pData[i] = USBD_ReadEPx_DataByte(EPx);//读取数据
	}
	
	return len;
}

/*************************************************************************************************************************
* 函数			:	USBD_EP_CONFIG *USBD_GetEpConfigPointer(USBD_EPx EPx)
* 功能			:	获取一个端点的配置指针,可以对端点进行配置(配置到缓存中)
* 参数			:	EPx:EP端点选择,见USBD_EPx
* 返回			:	配置指针,见USBD_EP_CONFIG
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	并未配置到设备
*************************************************************************************************************************/
USBD_EP_CONFIG *USBD_GetEpConfigPointer(USBD_EPx EPx)
{
	if(EPx > USBD_EPL) return NULL;
	
	return &sg_EpConfig[EPx];
}


/*************************************************************************************************************************
* 函数			:	void USBD_SetEpConfigData(USBD_EPx EPx,USBD_EP_TYPE EpType, USBD_EP_DIR EpDir, u32 EnableIntBit, u16 EpBuffOffset, u16 EpBuffSzie, u16 EpMaxPackSzie, 
						USBD_EPxIRQHandlerType pCallBack)
* 功能			:	对一个端点进行配置(配置到缓存中)
* 参数			:	EPx:EP端点选择,见USBD_EPx;EpType:EP类型,见USBD_EP_TYPE;EpDir:EP方向,见USBD_EP_DIR;EnableIntBit:需要使能的中断;
					EpBuffOffset:EP缓冲区偏移;EpBuffSzie:EP缓冲区大小;EpMaxPackSzie:当前EP最大数据包大小;pCallBack:当前EP中断回调函数
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	要在USB启动前进行初始化,并且并未配置到USB硬件中;先调用USBD_GetIdleEp()获取一个空闲端点,之后调用本函数进行初始化
*************************************************************************************************************************/
void USBD_SetEpConfigData(USBD_EPx EPx,USBD_EP_TYPE EpType, USBD_EP_DIR EpDir, u32 EnableIntBit, u16 EpBuffOffset, u16 EpBuffSzie, u16 EpMaxPackSzie, 
	USBD_EPxIRQHandlerType pCallBack)
{
	if(EPx > USBD_EPL) return;

	sg_EpConfig[EPx].EPx = EPx;								//EP端点
	sg_EpConfig[EPx].EnableIntBit = EnableIntBit;			//使能的中断
	sg_EpConfig[EPx].EpNum = EPx + 1;						//EP端点编号
	sg_EpConfig[EPx].EpBuffOffset = EpBuffOffset;			//EP端点缓冲区位置(USBD缓冲区)
	sg_EpConfig[EPx].EpBuffSzie = EpBuffSzie;				//EP端点缓冲区大小(USBD缓冲区)
	sg_EpConfig[EPx].EpUSB20_MaxPackSize = EpMaxPackSzie;	//EP端点最大数据包大小
	sg_EpConfig[EPx].EpType = EpType;						//EP端点类型
	sg_EpConfig[EPx].EpDir = EpDir;							//EP端点方向
	sg_EpConfig[EPx].EpIRQHandlerCallBack = pCallBack;		//EP端点中断回调函数
	
	sg_EpConfig[EPx].InitId = USBD_EP_INIT_ID;				//端点是否初始化了
}


/*************************************************************************************************************************
* 函数			:	bool USBD_GetIdleEp(USBD_EPx *pEPx)
* 功能			:	获取一个空闲的EP通道
* 参数			:	pEPx:返回EP端点选择,见USBD_EPx
* 返回			:	TRUE:空闲;FALSE:没有空闲
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	获取后需要立即调用 USBD_SetEpConfigData() ,将端点进行配置,并占用,否则会一直返回同一个空闲端点
*************************************************************************************************************************/
bool USBD_GetIdleEp(USBD_EPx *pEPx)
{
	u8 i;
	
	for(i = 0;i < USBD_MAX_EP;i ++)
	{
		if(sg_EpConfig[i].InitId != USBD_EP_INIT_ID)		//找到空闲的EP了
		{
			*pEPx = (USBD_EPx)i;
			return TRUE;
		}
	}
	
	return FALSE;
}


/*************************************************************************************************************************
* 函数			:	static void USBD_SetEpConfig(USBD_EP_CONFIG *pConfig)
* 功能			:	设置一个端点(会配置到硬件中)
* 参数			:	pConfig:需要配置的EP配置数据
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	会配置到设备
*************************************************************************************************************************/
static void USBD_SetEpConfig(USBD_EP_CONFIG *pConfig)
{
	if(pConfig->EPx > USBD_EPL) return;
	
	switch(pConfig->EpType)
	{
		case USBD_EP_TYPE_BULK	:	//块传输
		{
			USBD->EPx[pConfig->EPx].RSPCTL = (USB_EP_RSPCTL_FLUSH|USB_EP_RSPCTL_MODE_AUTO);
		}break;
		case USBD_EP_TYPE_INT	:	//中断
		{
			USBD->EPx[pConfig->EPx].RSPCTL = (USB_EP_RSPCTL_FLUSH|USB_EP_RSPCTL_MODE_MANUAL);
		}break;
		case USBD_EP_TYPE_ISO	:	//同步
		{
			USBD->EPx[pConfig->EPx].RSPCTL = (USB_EP_RSPCTL_FLUSH|USB_EP_RSPCTL_MODE_FLY);
		}break;
		default:break;
	}
	if (USBD->OPER & 0x04)  //high speed USB2.0
	{
		pConfig->EpMaxPackSzie = pConfig->EpUSB20_MaxPackSize;										//USB2.0数据包大小,通常是超过64字节的
	}
	else //USB1.1
	{
		pConfig->EpMaxPackSzie = 64;																//USB 1.1 固定为64字节
	}
	USBD_SET_MAX_PAYLOAD(pConfig->EPx, pConfig->EpMaxPackSzie);										//设置最大数据包大小

    USBD->EPx[pConfig->EPx].CFG = ((pConfig->EpNum&0xF) << 4) | (pConfig->EpDir << 3) | (pConfig->EpType<<1) | (1<<0);
	USBD_SetEpBuffAddr(pConfig->EPx, pConfig->EpBuffOffset, pConfig->EpBuffSzie);
	USBD_ENABLE_EP_INT(pConfig->EPx, pConfig->EnableIntBit);		//开启对应中断
	
	sg_EpConfig[pConfig->EPx].InitId = USBD_EP_INIT_ID;				//端点是否初始化了
}


/*************************************************************************************************************************
* 函数			:	void USBD_ResetDMA(void)
* 功能			:	复位DMA
* 参数			:	无
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
void USBD_ResetDMA(void)
{
    USBD->DMACNT = 0;
    USBD->DMACTL = 0x80;
    USBD->DMACTL = 0x00;
}

/*************************************************************************************************************************
* 函数			:	void USBD_SetEpBuffAddr(USBD_EPx EPx, u32 EpBuffOffset, u32 EpBuffSzie)
* 功能			:	设置端点缓冲区地址(会配置到硬件中)
* 参数			:	EPx:端点号,见USBD_EPx;EpBuffOffset:端点缓冲区偏移;EpBuffSzie:端点缓冲区大小
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
static void USBD_SetEpBuffAddr(USBD_EPx EPx, u32 EpBuffOffset, u32 EpBuffSzie)
{
	if(EPx > USBD_CEP) return;
	
    if (EPx == USBD_CEP) 	//控制端点
	{
        USBD->CEPBUFSTART = EpBuffOffset;
        USBD->CEPBUFEND   = EpBuffOffset + EpBuffSzie - 1;
    } 
	else
	{
        USBD->EPx[EPx].BUFSTART = EpBuffOffset;
        USBD->EPx[EPx].BUFEND 	= EpBuffOffset + EpBuffSzie - 1;
		
		sg_EpConfig[EPx].EPx = EPx;								//EP端点
		sg_EpConfig[EPx].EpBuffOffset = EpBuffOffset;			//EP端点缓冲区位置(USBD缓冲区)
		sg_EpConfig[EPx].EpBuffSzie = EpBuffSzie;				//EP端点缓冲区大小(USBD缓冲区)
    }
}

/*************************************************************************************************************************
* 函数			:	u16 USBD_GetEpBuffSize(USBD_EPx EPx)
* 功能			:	获取端点缓冲区大小(从硬件配置中获取)
* 参数			:	EPx:端点号,见USBD_EPx
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-17
* 最后修改时间 	: 	2020-10-17
* 说明			:	
*************************************************************************************************************************/
u16 USBD_GetEpBuffSize(USBD_EPx EPx)
{
	if(EPx > USBD_CEP) return 0;
	
	if (EPx == USBD_CEP) 	//控制端点
	{
		if(USBD->CEPBUFEND == 0) return 0;
        return USBD->CEPBUFEND - USBD->CEPBUFSTART + 1;
    } 
	else
	{
		if(USBD->EPx[EPx].BUFEND == 0) return 0;
        return USBD->EPx[EPx].BUFEND - USBD->EPx[EPx].BUFSTART + 1;
    }
}


/*************************************************************************************************************************
* 函数			:	void USBD_SetEpStall(USBD_EPx EPx)
* 功能			:	将USB端点停止状态设置为指定的端点ID。 端点将自动响应STALL令牌
* 参数			:	EPx:端点号,见USBD_EPx;
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
void USBD_SetEpStall(USBD_EPx EPx)
{
    if (EPx == USBD_CEP) 	//控制端点
	{
        USBD_SET_CEP_STATE(USB_CEPCTL_STALL);
	}
    else 
	{
        USBD->EPx[EPx].RSPCTL = (USBD->EPx[EPx].RSPCTL & 0xf7) | USB_EP_RSPCTL_HALT;
    }
}

/*************************************************************************************************************************
* 函数			:	static void USBD_SetStall(u8 EpNum)
* 功能			:	设置指定编号端点为停止状态, 端点将自动响应STALL令牌
* 参数			:	EpNum:端点编号
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
static void USBD_SetStall(u8 EpNum)
{
    int i;

    if (EpNum == 0)
	{
		USBD_SET_CEP_STATE(USB_CEPCTL_STALL);
	}   
    else 
	{
        for (i=0; i<USBD_MAX_EP; i++) 
		{
            if (((USBD->EPx[i].CFG & 0xf0) >> 4) == EpNum) 
			{
                USBD->EPx[i].RSPCTL = (USBD->EPx[i].RSPCTL & 0xf7) | USB_EP_RSPCTL_HALT;
            }
        }
    }
}


/*************************************************************************************************************************
* 函数			:	void  USBD_ClearEpStall(USBD_EPx EPx)
* 功能			:	清除USB端点停止状态
* 参数			:	EPx:端点号,见USBD_EPx;
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
void  USBD_ClearEpStall(USBD_EPx EPx)
{
	if(EPx > USBD_EPL) return;
    USBD->EPx[EPx].RSPCTL = USB_EP_RSPCTL_TOGGLE;
}


/*************************************************************************************************************************
* 函数			:	void USBD_ClearStall(u8 EpNum)
* 功能			:	清除指定编号端点的停止状态,端点将返回ACK / NAK令牌
* 参数			:	EpNum:端点编号
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
void USBD_ClearStall(u8 EpNum)
{
    int i;

    for (i = 0; i < USBD_MAX_EP; i ++) 
	{
        if (((USBD->EPx[i].CFG & 0xf0) >> 4) == EpNum) 
		{
            USBD->EPx[i].RSPCTL = USB_EP_RSPCTL_TOGGLE;
			break;
        }
    }
}

/*************************************************************************************************************************
* 函数			:	void USBD_PrepareCtrlIn(u8 *pDataBuff, u8 DataLen)
* 功能			:	控制端点准备响应控制命令(将数据写入缓冲区,准备等待控制端口IN令牌到来后发送到主机)
* 参数			:	pDataBuff:控制命令缓冲区;DataLen:控制命令长度
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	数据别缓存到全局缓冲区sg_USBD_Data.CtrDataBuff sg_USBD_Data.CtrDataLen
*************************************************************************************************************************/
void USBD_PrepareCtrlIn(u8 *pDataBuff, u8 DataLen)
{
	if(DataLen > 0)
	{
		if(sg_USBD_Data.CtrSendDataLen == 0)								//数据发送完了
		{
			USBD->CEPCTL |= USB_CEPCTL_FLUSH;								//清除控制端口待发送数据区-该位是自清除
		}
		USBD_MemCopy((u8*)sg_USBD_Data.CtrSendDataBuff, pDataBuff, DataLen);//拷贝命令到缓冲区中
		sg_USBD_Data.CtrSendDataLen = DataLen;
		sg_USBD_Data.pCtrlInPointer = sg_USBD_Data.CtrSendDataBuff;			//指针复位
		
		USBD_SET_CEP_INT_BIT(USBD_CEPINTSTS_INTKIF_Msk);					//控制端点IN令牌中断使能,等待发送数据
	}	
}

/*************************************************************************************************************************
* 函数			:	bool USBD_CtrlIn(void)
* 功能			:	控制端点发送数据给主机(必须在控制端点IN令牌有效时调用)
* 参数			:	无
* 返回			:	TRUE:有数据要发送;FALSE:无数据要发送
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	数据别缓存到全局缓冲区sg_USBD_Data.CtrDataBuff sg_USBD_Data.CtrDataLen
					当要发送的数据大于64字节,会分多次发送,需要等待多个IN令牌
*************************************************************************************************************************/
bool USBD_CtrlIn(void)
{
    int volatile i;
    uint32_t volatile count;

	if(sg_USBD_Data.CtrSendDataLen == 0) return FALSE;		//没有数据要发送
    if(sg_USBD_Data.CtrSendDataLen >= CEP_BUFF_SIZE)		//数据大小超过了控制端点的缓冲区大小
	{
        for (i=0; i<(CEP_BUFF_SIZE >> 2); i++)
		{
			USBD->CEPDAT = *(uint32_t *)sg_USBD_Data.pCtrlInPointer;
			sg_USBD_Data.pCtrlInPointer += 4;
		}
        USBD_START_CEP_IN(CEP_BUFF_SIZE);
        sg_USBD_Data.CtrSendDataLen -= CEP_BUFF_SIZE;
    } 
	else  //不足64字节的数据
	{
        for (i=0; i<(sg_USBD_Data.CtrSendDataLen >> 2); i++)//4字节整数倍
		{
			USBD->CEPDAT = *(uint32_t *)sg_USBD_Data.pCtrlInPointer;
			//uart_printf("%02X ", *(uint32_t *)sg_USBD_Data.pCtrlInPointer);
			sg_USBD_Data.pCtrlInPointer += 4;
		}
        count = sg_USBD_Data.CtrSendDataLen % 4;
        for (i=0; i<count; i++)
		{
			USBD_WriteCEP_DataByte(sg_USBD_Data.pCtrlInPointer[i]);
		}

        USBD_START_CEP_IN(sg_USBD_Data.CtrSendDataLen);
        sg_USBD_Data.pCtrlInPointer = sg_USBD_Data.CtrSendDataBuff;	//发送数据指针复位
        sg_USBD_Data.CtrSendDataLen = 0;							//待发送数据长度清零
    }
	
	return TRUE;
}

/*************************************************************************************************************************
* 函数			:	static bool USBD_DMA_TransferData(USBD_EPx EPx, u32 BuffAddr, u16 Len, bool isShortPacket)
* 功能			:	DAM传输数据(不会设置传输方向)
* 参数			:	EPx:端点号,见USBD_EPx;BuffAddr:数据缓冲区地址;Len:要传输的数据长度;isShortPacket:是否为短数据包
* 返回			:	TRUE:完成;FALSE:失败
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-16
* 最后修改时间 	: 	2020-10-16
* 说明			:	需要提前设置DMA传输的端口,不要开启DMA传输完成中断,并且调用之前需要清除DMA完成中断状态
*************************************************************************************************************************/
static bool USBD_DMA_TransferData(USBD_EPx EPx, u32 BuffAddr, u16 Len, bool isShortPacket)
{
	u32 TimeOut = 0;
	
    USBD->BUSINTSTS |= USBD_BUSINTSTS_DMADONEIF_Msk;	//清除DMA完成中断
	
	USBD_SET_DMA_ADDR(BuffAddr);						//设置传输地址
    USBD_SET_DMA_LEN(Len);								//设置传输长度
    USBD_ENABLE_DMA();									//启动DMA
	while(1)											//等待传输完成或超时
	{
		if(USBD->BUSINTSTS & (USBD_BUSINTSTS_DMADONEIF_Msk | USBD_BUSINTSTS_RSTIF_Msk)) break;	//传输完成了,或端口重置了
		if (!USBD_IS_ATTACHED()) break;								//总线未就绪
		TimeOut ++;
	}	
	if(USBD->BUSINTSTS & USBD_BUSINTSTS_DMADONEIF_Msk) 
	{
		if(isShortPacket)	//短数据包,需要发送结束
		{
			//短数据包,手动发送结束
			USBD->EPx[EPx].RSPCTL = (USBD->EPx[EPx].RSPCTL & 0x10) | USB_EP_RSPCTL_SHORTTXEN;    // packet end
		}
		return TRUE;
	}
	else return FALSE;
}

/*************************************************************************************************************************
* 函数			:	bool USBD_ReadBulkOutPackData(USBD_EPx EPx, u8 *pData, u16 EpBuffSize)
* 功能			:	读取一整包来自OUT的数据(需要等待缓冲区满)
* 参数			:	EPx:EP端点选择,见USBD_EPx;pData:数据缓冲区;EpBuffSize:端点缓冲区大小
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-17
* 最后修改时间 	: 	2020-10-17
* 说明			:	注意:不会判断端点缓冲区大小,等待数据包缓冲区满的过程可能出现假死,后面需要根据错误状态进行优化
					如果数据不够一整包也会假死,所以使用前一定要注意
*************************************************************************************************************************/
bool USBD_ReadBulkOutPackData(USBD_EPx EPx, u8 *pData, u16 EpBuffSize)
{
	if(EPx > USBD_EPL) return FALSE;
	while((USBD->EPx[EPx].INTSTS & USBD_EPINTSTS_BUFFULLIF_Msk) == 0)	//等待端点数据包缓冲区满
	{
		if (!USBD_IS_ATTACHED()) return FALSE;							//总线未就绪
		if (USBD->EPx[EPx].INTSTS & USBD_EPINTSTS_STALLIF_Msk) return FALSE;	//端点失速了
	}
	USBD->EPx[EPx].INTSTS |= USBD_EPINTSTS_BUFFULLIF_Msk;				//清除状态
	return USBD_BulkOut(EPx, pData, EpBuffSize);						//接收来自Bulk OUT的数据
}

/*************************************************************************************************************************
* 函数			:	static bool USBD_BulkOut(USBD_EPx EPx, u8 *pDataBuff, u32 Len)
* 功能			:	接收来自Bulk OUT的数据
* 参数			:	EPx:端点号,见USBD_EPx;BuffAddr:数据缓冲区地址;Len:要传输的数据长度;
* 返回			:	TRUE:完成;FALSE:失败
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-16
* 最后修改时间 	: 	2020-10-16
* 说明			:	使用的DMA阻塞传输,地址必须4字节对齐,需要提前等待端口缓冲区有数据,再调用此函数
*************************************************************************************************************************/
bool USBD_BulkOut(USBD_EPx EPx, u8 *pDataBuff, u32 Len)
{
	u32 Loop;
    u32 i;
	u32 Addr;

	if(EPx > USBD_EPL) return FALSE;

	Addr = (u32)pDataBuff;			//获取地址
	if(Addr % 4)
	{
		DEBUG("缓冲区地址没有4字节对齐\r\n");
		SYS_DelayMS(5000);
		return FALSE;
	}
	 Loop = Len / USBD_MAX_DMA_LEN;
	
	USBD_SET_DMA_WRITE(EPx+1);		//端点地址使用端点号+1
    for (i=0; i < Loop; i++) 
	{
        if(USBD_DMA_TransferData(EPx, Addr, USBD_MAX_DMA_LEN, FALSE) == FALSE) return FALSE;
		Addr += USBD_MAX_DMA_LEN;	//地址增加
    }

    Loop = Len % USBD_MAX_DMA_LEN;	//余下的
    if (Loop) 
	{
        if(USBD_DMA_TransferData(EPx, Addr, Loop, FALSE) == FALSE) return FALSE;
    }
	
	return TRUE;
}

/*************************************************************************************************************************
* 函数			:	static bool USBD_BulkIn(USBD_EPx EPx, u8 *pDataBuff, u32 Len)
* 功能			:	写入数据到Bulk IN
* 参数			:	EPx:端点号,见USBD_EPx;BuffAddr:数据缓冲区地址;Len:要传输的数据长度;
* 返回			:	TRUE:完成;FALSE:失败
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-16
* 最后修改时间 	: 	2020-10-16
* 说明			:	使用的DMA阻塞传输,地址必须4字节对齐
*************************************************************************************************************************/
bool USBD_BulkIn(USBD_EPx EPx, u8 *pDataBuff, u32 Len)
{
	u32 Loop;
    u32 i;
	u32 Addr;
	u32 EpMaxPackSzie = USBD_GetEpMaxPacketSize(EPx);

	if(EPx > USBD_EPL) return FALSE;

	Addr = (u32)pDataBuff;			//获取地址
	if(Addr % 4)
	{
		DEBUG("缓冲区地址没有4字节对齐\r\n");
		SYS_DelayMS(5000);
		return FALSE;
	}

	Loop = Len / EpMaxPackSzie;
	USBD_SET_DMA_READ(EPx+1);											//端点地址使用端点号+1
    for (i=0; i < Loop; i++) 
	{
		while(1)
		{
			if(USBD->EPx[EPx].INTSTS & USBD_EPINTSTS_BUFEMPTYIF_Msk)	//端点缓冲区为空,可以写入数据了
			{
				if(USBD_DMA_TransferData(EPx, Addr, EpMaxPackSzie, FALSE) == FALSE) return FALSE;
				else 
				{
					break;
				}
			}
		}
        Addr += EpMaxPackSzie;											//地址增加
    }

    Loop = Len % EpMaxPackSzie;											//余下的
    if (Loop) 
	{
		while(1)
		{
			if(USBD->EPx[EPx].INTSTS & USBD_EPINTSTS_BUFEMPTYIF_Msk)	//端点缓冲区为空,可以写入数据了
			{
				if(USBD_DMA_TransferData(EPx, Addr, Loop, TRUE) == FALSE) 
				{
					return FALSE;
				}
				else break;
			}
		}
		
    }
	
	return TRUE;
}

/*************************************************************************************************************************
* 函数			:	u32 USBD_GetEpStall(USBD_EPx EPx)
* 功能			:	获取指定端点ID的USB端点停止状态
* 参数			:	EPx:端点号,见USBD_EPx;
* 返回			:	停止状态
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
u32 USBD_GetEpStall(USBD_EPx EPx)
{
	if(EPx > USBD_EPL) return 0;
	
    return (USBD->EPx[EPx].RSPCTL & USB_EP_RSPCTL_HALT);
}

/*************************************************************************************************************************
* 函数			:	u32 USBD_GetStall(u8 EpNum)
* 功能			:	获取指定端点号的停止状态
* 参数			:	EpNum:端点编号
* 返回			:	停止状态
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
u32 USBD_GetStall(u8 EpNum)
{
    int i;

    for (i=0; i<USBD_MAX_EP; i++) 
	{
        if (((USBD->EPx[i].CFG & 0xf0) >> 4) == EpNum) 
		{
            return (USBD->EPx[i].RSPCTL & USB_EP_RSPCTL_HALT);
        }
    }
    return 0;
}

/*************************************************************************************************************************
* 函数			:	static void USBD_SwReset(void)
* 功能			:	USBD软复位
* 参数			:	无
* 返回			:	无
* 依赖			:	无
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	主要用于清除一些状态以及USB地址
*************************************************************************************************************************/
static void USBD_SwReset(void)
{
	sg_USBD_Data.ConfigValue = 0;
	sg_USBD_Data.DeviceAddr = 0;
	sg_USBD_Data.CtrSendDataLen = 0;
	USBD_SET_ADDR(0);
}

/*************************************************************************************************************************
* 函数			:	void USBD_StandardRequest(void)
* 功能			:	标准请求处理
* 参数			:	无
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
static void USBD_StandardRequest(void)
{
    if (g_USBD_Setup.RequestType & 0x80) 									//USBD发送数据到主机
	{
        // Device to host
        switch (g_USBD_Setup.Request) 
		{
			case USBD_GET_CONFIGURATION: //获取设备配置描述符-主机枚举设备
			{
				uart_printf("GET_CONFIGURATION\r\n");
				// Return current configuration setting
				USBD_PrepareCtrlIn((u8 *)&sg_USBD_Data.ConfigValue, 1);
			}break;
			case USBD_GET_DESCRIPTOR:	 //用于主机获取设备的特定描述符 
			{
				uart_printf("GET_DESCRIPTOR\r\n");
				USBD_GetDescriptor(sg_USBD_Data.pDescData);//获取设备描述符处理
			}break;
			case USBD_GET_INTERFACE: 	//用于获取当前某个接口描述符编号
			{
				uart_printf("GET_INTERFACE\r\n");
				USBD_PrepareCtrlIn((u8 *)&sg_USBD_Data.UsbAltInterface, 1);				
			}break;
			case USBD_GET_STATUS: 		//用来返回特定接收者的状态
			{
				u8 usbd_buff[2] = {0,0};
				
				uart_printf("GET_STATUS\r\n");
				// Device
				if (g_USBD_Setup.RequestType == 0x80) 
				{
					//usbd_buff[0] = 1; // 自供电
					usbd_buff[0] = 0; // 总线供电
				}
				else if (g_USBD_Setup.RequestType == 0x81)// Interface
				{
					usbd_buff[0] = 0;
				}
				
				else if (g_USBD_Setup.RequestType == 0x82) // Endpoint
				{
					usbd_buff[0] = USBD_GetStall((g_USBD_Setup.Index & 0xF))? 1 : 0;
				}
				usbd_buff[1] = 0;
				USBD_PrepareCtrlIn(usbd_buff, 2);
			}break;
			default: //未知请求
			{
				uart_printf("未知01\r\n");
				/* Setup error, stall the device */
				USBD_SET_CEP_STATE(USBD_CEPCTL_STALLEN_Msk);
			}break;
        }
    } else //来自主机的请求
	{
        // Host to device
        switch (g_USBD_Setup.Request) 
		{
			case USBD_CLEAR_FEATURE: //用来清除或禁止接收者的某些特性
			{
				uart_printf("CLEAR_FEATURE\r\n");
				/* Status stage */
			}break;
			case USBD_SET_ADDRESS: //用来给设备分配地址
			{
				uart_printf("SET_ADDRESS\r\n");
				sg_USBD_Data.DeviceAddr = (u8)g_USBD_Setup.Value;

				// DATA IN for end of setup
				/* Status Stage */
			}break;
			case USBD_SET_CONFIGURATION: //用于主机指示设备采用的要求的配置
			{
				uart_printf("SET_CONFIGURATION\r\n");
				sg_USBD_Data.ConfigValue = (u8)g_USBD_Setup.Value;
				// DATA IN for end of setup
				/* Status stage */

			}break;
			case USBD_SET_FEATURE: 
			{
				uart_printf("SET_FEATURE\r\n");
				if ((g_USBD_Setup.Value & 0x3) == USBD_FEATURE_TEST_MODE) 
				{  /* TEST_MODE*/
					sg_USBD_Data.TestSelector = g_USBD_Setup.Index >> 8;
					sg_USBD_Data.isTestMode = TRUE;
				}
				/* Status stage */
			}break;
			case USBD_SET_INTERFACE: //用于主机要求设备用某个描述符来描述接口
			{
				uart_printf("SET_INTERFACE\r\n");
				sg_USBD_Data.UsbAltInterface = (u8)g_USBD_Setup.Value;
				/* Status stage */			
			}break;
			default: //其它命令,不支持
			{
				uart_printf("未知02\r\n");
				/* Setup error, stall the device */
				USBD_SET_CEP_STATE(USBD_CEPCTL_STALLEN_Msk);
			}break;
        }
    }
}

/*************************************************************************************************************************
* 函数			:	void USBD_SetupHandle(void)
* 功能			:	USB Setup处理
* 参数			:	无
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-14
* 最后修改时间 	: 	2020-10-14
* 说明			:	
*************************************************************************************************************************/
static void USBD_SetupHandle(void)
{
	//首先获取全局SETUP数据,存储到g_USBD_Setup中
	g_USBD_Setup.RequestType = USBD->SETUP1_0 & 0xFF;			//请求类型
	g_USBD_Setup.Request = USBD->SETUP1_0>>8;					//本描述符的请求类型
	g_USBD_Setup.Value = USBD->SETUP3_2;						//参数
	g_USBD_Setup.Index = USBD->SETUP5_4;						//标示
	g_USBD_Setup.Length = USBD->SETUP7_6;						//下一阶段发送数据的长度

	uart_printf("RequestType:0x%X\t", g_USBD_Setup.RequestType);
	uart_printf("Request:0x%X\t", g_USBD_Setup.Request);
	uart_printf("Value:0x%X\t", g_USBD_Setup.Value);
	uart_printf("Index:0x%X\t", g_USBD_Setup.Index);
	uart_printf("Length:0x%X\r\n", g_USBD_Setup.Length);
	
    //处理SETUP请求
    switch (g_USBD_Setup.RequestType & 0x60) 					//D6-D5位是请求主分类型
	{
		case USBD_REQ_STANDARD: //标准请求
		{ 
			uart_printf("USBD_REQ_STANDARD\r\n");
			USBD_StandardRequest();
        }break;
		case USBD_REQ_CLASS: 	//类别请求
		{ 
			uart_printf("USBD_REQ_CLASS\r\n");
			if (sg_USBD_Data.pClassReqCallBack != NULL) sg_USBD_Data.pClassReqCallBack();
		}break;
		case USBD_REQ_VENDOR: 	//厂商请求
		{
			uart_printf("USBD_REQ_VENDOR\r\n");
			if (sg_USBD_Data.pVendorReqCallBack != NULL) sg_USBD_Data.pVendorReqCallBack();
		}break;
		default: 				//保留的其它请求
		{
			/* Setup error, stall the device */
			USBD_SET_CEP_STATE(USBD_CEPCTL_STALLEN_Msk);
		}break;
    }
}

/*************************************************************************************************************************
* 函数			:	void USBD_SetupDoneHandle(void)
* 功能			:	USB Setup安装完成处理
* 参数			:	无
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	
*************************************************************************************************************************/
void USBD_SetupDoneHandle(void)
{
#define USBD_TEST_J                  0x01    //TEST J
#define USBD_TEST_K                  0x02    //TEST K
#define USBD_TEST_SE0_NAK            0x03    //TEST SE0
#define USBD_TEST_PACKET             0x04    //TEST Packet
#define USBD_TEST_FORCE_ENABLE       0x05    //TEST Force enable
	
    switch (g_USBD_Setup.Request) 
	{
		case USBD_SET_ADDRESS: 		//用来给设备分配地址
		{
			uart_printf("USBD_SET_ADDRESS %d\r\n",sg_USBD_Data.DeviceAddr);
			USBD_SET_ADDR(sg_USBD_Data.DeviceAddr);
		}break;
		case USBD_SET_CONFIGURATION: //用于主机指示设备采用的要求的配置
		{
			uart_printf("\r\n");
			if (sg_USBD_Data.ConfigValue == 0) 
			{
				int volatile i;
				/* Reset PID DATA0 */
				for (i=0; i<USBD_MAX_EP; i++) 
				{
					if (USBD->EPx[i].CFG & 0x1) 
					{
						USBD->EPx[i].RSPCTL = USB_EP_RSPCTL_TOGGLE;
					}
				}
			}
		}break;
		case USBD_SET_FEATURE: 		//用来启用或激活命令接收者的某些特性
		{
			uart_printf("USBD_SET_FEATURE\r\n");
			if(g_USBD_Setup.Value == USBD_FEATURE_ENDPOINT_HALT)
			{
				USBD_SetStall(g_USBD_Setup.Index & 0xF);
			}
			else if (sg_USBD_Data.isTestMode == TRUE) 
			{
				sg_USBD_Data.isTestMode = FALSE;
				if (sg_USBD_Data.TestSelector == USBD_TEST_J)
					USBD->TEST = USBD_TEST_J;
				else if (sg_USBD_Data.TestSelector == USBD_TEST_K)
					USBD->TEST = USBD_TEST_K;
				else if (sg_USBD_Data.TestSelector == USBD_TEST_SE0_NAK)
					USBD->TEST = USBD_TEST_SE0_NAK;
				else if (sg_USBD_Data.TestSelector == USBD_TEST_PACKET)
					USBD->TEST = USBD_TEST_PACKET;
				else if (sg_USBD_Data.TestSelector == USBD_TEST_FORCE_ENABLE)
					USBD->TEST = USBD_TEST_FORCE_ENABLE;
			}
		}break;
		case USBD_CLEAR_FEATURE:	//用来清除或禁止接收者的某些特性 
		{
			uart_printf("USBD_CLEAR_FEATURE\r\n");
			if(g_USBD_Setup.Value == USBD_FEATURE_ENDPOINT_HALT)
			{
				USBD_ClearStall(g_USBD_Setup.Index & 0xF);
			}				
		}break;
		default: break;
    }
}

/*************************************************************************************************************************
* 函数			:	static __inline void USBD_BUS_IRQHandler(void)
* 功能			:	USBD 总线中断处理
* 参数			:	无
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	
*************************************************************************************************************************/
static __inline void USBD_BUS_IRQHandler(void)
{
	static vu32 BusIrq;
	static vu8 i;
	
	BusIrq = USBD->BUSINTSTS & USBD->BUSINTEN;					//获取总线中断
		
	if (BusIrq & USBD_BUSINTSTS_RSTIF_Msk) 						//USB端口重置
	{
		
		uart_printf("===USB端口重置\r\n");
		USBD_SwReset();
		USBD_ResetDMA();										//复位DMA
	
		for(i = 0;i < USBD_MAX_EP;i ++)
		{
			if(sg_EpConfig[i].InitId == USBD_EP_INIT_ID)		//对应端点使能了,则清除数据
			{
				USBD->EPx[i].RSPCTL = USBD_EPRSPCTL_FLUSH_Msk;
				USBD_SetEpConfig(&sg_EpConfig[i]);				//USB连接成功了,需要重置端口,对所有的需要使用的EP进行重新配置
			}
		}
	}  
	else if(BusIrq & USBD_BUSINTSTS_RESUMEIF_Msk)				//设备从挂起状态恢复
	{
		
	}
	else if(BusIrq & USBD_BUSINTSTS_SUSPENDIF_Msk)				//总线被挂起暂停了
	{
		//USBD->OPER |= USBD_OPER_RESUMEEN_Msk;					//唤醒设备,不要挂起
	}
	
	//其它总线中断进行处理
	if(BusIrq && sg_USBD_Data.pUSBD_BusIRQHandlerCallBack != NULL)
	{
		sg_USBD_Data.pUSBD_BusIRQHandlerCallBack(BusIrq);		//USB总线中断处理-由于端口重置已经处理过,此处不再处理端口重置中断
	}
	USBD->BUSINTSTS = BusIrq;									//清除总线中断-总中断貌似不需要写1清除
}

/*************************************************************************************************************************
* 函数			:	static __inline void USBD_CEP_IRQHandler(void)
* 功能			:	USBD 控制端点中断处理
* 参数			:	无
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	控制端点优先处理 SETUP中断进行USB安装并开启完成中断,然后处理IN令牌中断,在IN令牌中断中发送数据,并开启发送完成中断
					发送完成中断中检查是否有数据要发送,没有就关闭IN中断,在完成中断中处理完成事件,并关闭完成中断
*************************************************************************************************************************/
static __inline void USBD_CEP_IRQHandler(void)
{
	static vu32 CeIrq;
	static vu16 i;
	
	CeIrq = USBD->CEPINTSTS & USBD->CEPINTEN;		
	if(CeIrq & USBD_CEPINTSTS_SETUPPKIF_Msk)					//SETUP处理
	{
		USBD_SetupHandle();										//SETUP相关请求处理=主机初始化设备,获取设备相关信息
		//uart_printf("->USBD->CEPCTL:0x%X\r\n", USBD->CEPCTL);
		if(sg_USBD_Data.CtrSendDataLen == 0)					//没有数据要发送,则清除NAK状态
		{
			USBD_SET_CEP_STATE(USB_CEPCTL_NAKCLR);
		}
		USBD_SET_CEP_INT_BIT(USBD_CEPINTSTS_STSDONEIF_Msk);		//开启端点安装状态完成中断
	}
	//控制端点IN令牌可能会与发送完成中断一起来,这个时候要求没有发送完成中断的时候才响应IN令牌
	else if((CeIrq & USBD_CEPINTSTS_INTKIF_Msk)&& ((CeIrq & USBD_CEPINTSTS_TXPKIF_Pos)==0)
		&& (USBD_CtrlIn() == TRUE))								//控制端点IN令牌中断,可以响应数据给主机了
	{
		USBD_SET_CEP_INT_BIT(USBD_CEPINTSTS_TXPKIF_Msk);		//有数据要发送,开启控制端点数据发送完成中断
	}
	else if(CeIrq & USBD_CEPINTSTS_TXPKIF_Msk)					//控制端点数据包发送完成中断-一次数据包发送完成
	{
		if(sg_USBD_Data.CtrSendDataLen == 0)					//后续没有数据要发送了,关闭IN令牌中断
		{
			USBD_CLEAR_CEP_INT_BIT(USBD_CEPINTSTS_INTKIF_Msk);	//关闭IN令牌中断
		}
		USBD_SET_CEP_STATE(USB_CEPCTL_NAKCLR);					//数据发送完成了,清除NAK状态
		USBD_CLEAR_CEP_INT_BIT(USBD_CEPINTSTS_TXPKIF_Msk);		//关闭端点数据发送完成中断
	}
	else if(CeIrq & USBD_CEPINTSTS_STSDONEIF_Msk) 				//数据端口完成中断,更新状态
	{
		USBD_SetupDoneHandle();
		USBD_CLEAR_CEP_INT_BIT(USBD_CEPINTSTS_STSDONEIF_Msk);	//清除端点安装状态完成中断
	}

	if(sg_USBD_Data.pUSBD_cEpIRQHandlerCallBack) sg_USBD_Data.pUSBD_cEpIRQHandlerCallBack(CeIrq);		//控制端点中断回调
	USBD->CEPINTSTS = CeIrq;									//清除中断
}



/*************************************************************************************************************************
* 函数			:	static __inline void USBD_EPx_IRQHandler(USBD_EPx EPx)
* 功能			:	USBD 端点中断处理
* 参数			:	EPx:端点号,见USBD_EPx
* 返回			:	无
* 依赖			:	底层
* 作者			:	cp1300@139.com
* 时间			:	2020-10-15
* 最后修改时间 	: 	2020-10-15
* 说明			:	
*************************************************************************************************************************/
static __inline void USBD_EPx_IRQHandler(USBD_EPx EPx)
{
	static vu32 EpIrq;
	
	EpIrq = USBD->EPx[EPx].INTSTS & USBD->EPx[EPx].INTEN;
	
	if(sg_EpConfig[EPx].EpIRQHandlerCallBack) 
	{
		sg_EpConfig[EPx].EpIRQHandlerCallBack(EPx, EpIrq);		//数据端点中断处理
	}
	USBD->EPx[EPx].INTSTS = EpIrq;								//清除中断
}



//中断服务程序
static void USBD_IRQHandler(void)
{
	static vu32 Irq;
	static vu32 i;
	static vu32 temp;
	
	Irq = USBD->GINTSTS & USBD->GINTEN;							//获取总的中断状态
	if (Irq == 0)    return;

	//uart_printf("INT:0x%X\t BUS:0x%X\t CEP:0x%X\r\n", USBD->GINTSTS & USBD->GINTEN, USBD->BUSINTSTS & USBD->BUSINTEN, USBD->CEPINTSTS & USBD->CEPINTEN);
	
	
	//总线中断======================================================================================================================================
	if(Irq & USBD_GINTSTS_USBIF_Msk)							//USB中断
	{
		USBD_BUS_IRQHandler();									//BUS中断处理
	}
	//控制端点中断======================================================================================================================================
	if(Irq & USBD_GINTSTS_CEPIF_Msk)							//控制端点中断
	{
		USBD_CEP_IRQHandler();									//控制端点中断处理
	}
	//其它数据端点中断处理
	temp = Irq >> 2;
	for(i = 0;i < USBD_MAX_EP;i ++)
	{
		if(temp && (temp&0x01))									//指定的端点有中断
		{
			USBD_EPx_IRQHandler((USBD_EPx)i);					//端点中断处理
		}
		temp >>= 1;
	}
	USBD->GINTSTS = Irq;										//清除总中断
}

//USBD.h

/*************************************************************************************************************
 * 文件名:			usbd.h
 * 功能:			NUC970 USB设备相关驱动
 * 作者:			cp1300@139.com
 * 创建时间:		2020-10-12
 * 最后修改时间:	2020-10-12
 * 详细:			
*************************************************************************************************************/
#ifndef _USB_D_
#define _USB_D_
#include "nuc970_system.h"
#include "typedef.h"

#define USBD_MAX_EP   12		//端点数量


//端点通道定义
typedef enum
{
	USBD_EPA	=	0,		//USB端点A
	USBD_EPB	=	1,
	USBD_EPC	=	2,
	USBD_EPD	=	3,
	USBD_EPE	=	4,
	USBD_EPF	=	5,
	USBD_EPG	=	6,
	USBD_EPH	=	7,
	USBD_EPI	=	8,
	USBD_EPJ	=	9,
	USBD_EPK	=	10,
	USBD_EPL	=	11,
	USBD_CEP	=	12,		//保留的控制端点
	USBD_INVALID_EP = 0xff,	//无效的EP
}USBD_EPx;




//USBD所需全局变量定义==必须4字节对齐
//#pragma pack(4)
EPx读取数据相关信息
//typedef struct
//{
//	volatile u16 ReadDataCount;											//已经读取的数据数量
//	volatile u16 DataBuffSize;											//缓冲区大小
//	volatile u8 *pReadDataBuff;											//读取的数据缓冲区
//	volatile bool isNewData;											//新数据标志
//}USBD_EP_READ_DATA;
//#pragma pack()

typedef void (*USBD_IRQHandlerType)(u32 Status);						//中断服务程序定义
typedef void (*USBD_EPxIRQHandlerType)(USBD_EPx EPx, u32 Status);		//数据端口中断服务程序定义
typedef void (*USBD_CLASS_REQ)(void);									//类别请求回调处理函数定义
typedef void (*USBD_VENDOR_REQ)(void); 									//厂商请求回调处理函数定义

//端点类型定义
typedef enum
{
	USBD_EP_TYPE_BULK	=	1,	//块传输
	USBD_EP_TYPE_INT	=	2,	//中断
	USBD_EP_TYPE_ISO	=	3,	//同步
}USBD_EP_TYPE;

//端点方向设置
typedef enum
{
	USBD_EP_OUTPUT		=	0,	//主机输出
	USBD_EP_INPUT		=	1,	//主机输入
}USBD_EP_DIR;


//USB请求定义 USBD_SETUP_TYPE.Request 的值
typedef enum
{
	USBD_GET_STATUS          =	0x00,	//用来返回特定接收者的状态
	USBD_CLEAR_FEATURE       =	0x01,	//用来清除或禁止接收者的某些特性
	USBD_SET_FEATURE         =	0x03,	//用来启用或激活命令接收者的某些特性
	USBD_SET_ADDRESS         =	0x05,	//用来给设备分配地址
	USBD_GET_DESCRIPTOR      =	0x06,	//用于主机获取设备的特定描述符
	USBD_SET_DESCRIPTOR      =	0x07,	//修改设备中有关的描述符,或者增加新的描述符
	USBD_GET_CONFIGURATION   =	0x08,	//用于主机获取设备当前设备的配置值(注同上面的不同)
	USBD_SET_CONFIGURATION   =	0x09,	//用于主机指示设备采用的要求的配置
	USBD_GET_INTERFACE       =	0x0A,	//用于获取当前某个接口描述符编号
	USBD_SET_INTERFACE       =	0x0B,	//用于主机要求设备用某个描述符来描述接口
	USBD_SYNC_FRAME          =	0x0C,	//用于设备设置和报告一个端点的同步帧
}USBD_REQUEST_TYPE;


//USB SETUP请求数据结构体
typedef struct
{
	//最高位BIT8:0:数据由主控发送到设备;1:数据由设备发送到主控
	//D6-D5位是请求主分类型
	//D4-D0位是表求接收这个包的接口
	u8 RequestType;			//请求类型,
	u8 Request;				//本描述符的请求类型
	u16 Value;				//参数
	u16 Index;				//标示
	u16 Length;				//下一阶段发送数据的长度	
}USBD_SETUP_TYPE;
extern USBD_SETUP_TYPE g_USBD_Setup;

//USB设备描述符集合
typedef struct
{
	const u8 *DeveceDescripArray;           //设备描述符
    const u8 *ConfigDescripArray;         	//配置描述符
    const u8 **StringDescripPoint;        	//USB字符串描述符指针的指针
    const u8 *QualifierDescripArray;        //限定描述符
    const u8 *OtherConfigDescripArray;    	//其他速度配置描述符
    const u8 **HidReportDescripPoint;     	//HID报告描述符的指针
    const u32 *HidReportSize;    			//HID报告描述符大小的指针
}USBD_DESCRIPTOR;

//控制端点buff
#define CEP_BUFF_OFFSET    		0		//控制端点buff缓冲区偏移
#define CEP_BUFF_SIZE			64		//控制端点缓冲区大小

//USB描述符长度
#define USBD_LEN_DEVICE          18
#define USBD_LEN_QUALIFIER       10
#define USBD_LEN_CONFIG          9
#define USBD_LEN_INTERFACE       9
#define USBD_LEN_ENDPOINT        7
#define USBD_LEN_OTG             5
#define USBD_LEN_HID             9

//USB描述符类型
#define USBD_DESC_DEVICE         0x01		//设备描述符(Device Descriptor)
#define USBD_DESC_CONFIG         0x02		//配置描述符(Configuration Descriptor)
#define USBD_DESC_STRING         0x03		//字符串描述符(String Descriptor)
#define USBD_DESC_INTERFACE      0x04		//接口描述符(Interface Descriptor)
#define USBD_DESC_ENDPOINT       0x05		//端点描述符(EndPont Descriptor)
#define USBD_DESC_QUALIFIER      0x06
#define USBD_DESC_OTHERSPEED     0x07
#define USBD_DESC_IFPOWER        0x08
#define USBD_DESC_OTG            0x09

//USB HID描述符类型
#define USBD_DESC_HID            0x21		//获取HID描述符
#define USBD_DESC_HID_RPT        0x22		//获取HID报告描述符

//USB SETUP主请求类型
#define USBD_REQ_STANDARD        0x00	//标准请求
#define USBD_REQ_CLASS           0x20	//类别请求
#define USBD_REQ_VENDOR          0x40	//厂商请求

//定义的数据存储器类型的请求
#define USBD_BULK_ONLY_MASS_STORAGE_RESET    0xFF
#define USBD_GET_MAX_LUN                     0xFE	//获取存储器逻辑磁盘数量

//USB功能选择器
#define USBD_FEATURE_ENDPOINT_HALT           0x00
#define USBD_FEATURE_DEVICE_REMOTE_WAKEUP    0x01
#define USBD_FEATURE_TEST_MODE               0x02



/********************* Bit definition of CEPCTL register **********************/
#define USB_CEPCTL_NAKCLR               ((uint32_t)0x00000000)      //NAK clear
#define USB_CEPCTL_STALL                ((uint32_t)0x00000002)      //Stall
#define USB_CEPCTL_ZEROLEN              ((uint32_t)0x00000004)      //Zero length packet
#define USB_CEPCTL_FLUSH                ((uint32_t)0x00000008)      //CEP flush

/********************* Bit definition of EPxRSPCTL register **********************/
#define USB_EP_RSPCTL_FLUSH             ((uint32_t)0x00000001)      //Buffer Flush
#define USB_EP_RSPCTL_MODE_AUTO         ((uint32_t)0x00000000)      //Auto-Validate Mode
#define USB_EP_RSPCTL_MODE_MANUAL       ((uint32_t)0x00000002)      //Manual-Validate Mode
#define USB_EP_RSPCTL_MODE_FLY          ((uint32_t)0x00000004)      //Fly Mode
#define USB_EP_RSPCTL_MODE_MASK         ((uint32_t)0x00000006)      //Mode Mask
#define USB_EP_RSPCTL_TOGGLE            ((uint32_t)0x00000008)      //Clear Toggle bit
#define USB_EP_RSPCTL_HALT              ((uint32_t)0x00000010)      //Endpoint halt
#define USB_EP_RSPCTL_ZEROLEN           ((uint32_t)0x00000020)      //Zero length packet IN
#define USB_EP_RSPCTL_SHORTTXEN         ((uint32_t)0x00000040)      //Packet end
#define USB_EP_RSPCTL_DISBUF            ((uint32_t)0x00000080)      //Disable buffer


//USBD GINTSTS GINTEN 总的USB中断状态
#define USBD_GINTSTS_USBIF_Pos           (0)                                               
#define USBD_GINTSTS_USBIF_Msk           (0x1ul << USBD_GINTSTS_USBIF_Pos)                 //USB中断状态
#define USBD_GINTSTS_CEPIF_Pos           (1)                                               
#define USBD_GINTSTS_CEPIF_Msk           (0x1ul << USBD_GINTSTS_CEPIF_Pos)                 //控制端点中断
#define USBD_GINTSTS_EPAIF_Pos           (2)                                               
#define USBD_GINTSTS_EPAIF_Msk           (0x1ul << USBD_GINTSTS_EPAIF_Pos)                 //端点A中断
#define USBD_GINTSTS_EPBIF_Pos           (3)                                               
#define USBD_GINTSTS_EPBIF_Msk           (0x1ul << USBD_GINTSTS_EPBIF_Pos)                 //端点B中断
#define USBD_GINTSTS_EPCIF_Pos           (4)                                               
#define USBD_GINTSTS_EPCIF_Msk           (0x1ul << USBD_GINTSTS_EPCIF_Pos)                 //端点C中断
#define USBD_GINTSTS_EPDIF_Pos           (5)                                               
#define USBD_GINTSTS_EPDIF_Msk           (0x1ul << USBD_GINTSTS_EPDIF_Pos)                 //端点D中断
#define USBD_GINTSTS_EPEIF_Pos           (6)                                              
#define USBD_GINTSTS_EPEIF_Msk           (0x1ul << USBD_GINTSTS_EPEIF_Pos)                 //端点E中断
#define USBD_GINTSTS_EPFIF_Pos           (7)                                               
#define USBD_GINTSTS_EPFIF_Msk           (0x1ul << USBD_GINTSTS_EPFIF_Pos)                 //端点F中断
#define USBD_GINTSTS_EPGIF_Pos           (8)                                               
#define USBD_GINTSTS_EPGIF_Msk           (0x1ul << USBD_GINTSTS_EPGIF_Pos)                 //端点G中断
#define USBD_GINTSTS_EPHIF_Pos           (9)                                               
#define USBD_GINTSTS_EPHIF_Msk           (0x1ul << USBD_GINTSTS_EPHIF_Pos)                 //端点H中断
#define USBD_GINTSTS_EPIIF_Pos           (10)                                              
#define USBD_GINTSTS_EPIIF_Msk           (0x1ul << USBD_GINTSTS_EPIIF_Pos)                 //端点I中断
#define USBD_GINTSTS_EPJIF_Pos           (11)                                              
#define USBD_GINTSTS_EPJIF_Msk           (0x1ul << USBD_GINTSTS_EPJIF_Pos)                 //端点J中断
#define USBD_GINTSTS_EPKIF_Pos           (12)                                              
#define USBD_GINTSTS_EPKIF_Msk           (0x1ul << USBD_GINTSTS_EPKIF_Pos)                 //端点K中断
#define USBD_GINTSTS_EPLIF_Pos           (13)                                              
#define USBD_GINTSTS_EPLIF_Msk           (0x1ul << USBD_GINTSTS_EPLIF_Pos)                 //端点L中断


//USBD BUSINTSTS BUSINTEN 总线中断状态
#define USBD_BUSINTSTS_SOFIF_Pos         (0)                                               
#define USBD_BUSINTSTS_SOFIF_Msk         (0x1ul << USBD_BUSINTSTS_SOFIF_Pos)               //接收到SOF中断
#define USBD_BUSINTSTS_RSTIF_Pos         (1)                                               
#define USBD_BUSINTSTS_RSTIF_Msk         (0x1ul << USBD_BUSINTSTS_RSTIF_Pos)               //USB端口重置
#define USBD_BUSINTSTS_RESUMEIF_Pos      (2)                                               
#define USBD_BUSINTSTS_RESUMEIF_Msk      (0x1ul << USBD_BUSINTSTS_RESUMEIF_Pos)            //发生设备恢复
#define USBD_BUSINTSTS_SUSPENDIF_Pos     (3)                                               
#define USBD_BUSINTSTS_SUSPENDIF_Msk     (0x1ul << USBD_BUSINTSTS_SUSPENDIF_Pos)           //暂停请求
#define USBD_BUSINTSTS_HISPDIF_Pos       (4)                                               
#define USBD_BUSINTSTS_HISPDIF_Msk       (0x1ul << USBD_BUSINTSTS_HISPDIF_Pos)             //设备已设置为高速
#define USBD_BUSINTSTS_DMADONEIF_Pos     (5)                                               
#define USBD_BUSINTSTS_DMADONEIF_Msk     (0x1ul << USBD_BUSINTSTS_DMADONEIF_Pos)           //DMA完成中断
#define USBD_BUSINTSTS_PHYCLKVLDIF_Pos   (6)                                               
#define USBD_BUSINTSTS_PHYCLKVLDIF_Msk   (0x1ul << USBD_BUSINTSTS_PHYCLKVLDIF_Pos)         //可从收发器获得可用时钟
#define USBD_BUSINTSTS_VBUSDETIF_Pos     (8)                                               
#define USBD_BUSINTSTS_VBUSDETIF_Msk     (0x1ul << USBD_BUSINTSTS_VBUSDETIF_Pos)           //VBUS已插入


//USBD_CEPINTEN CEPINTSTS USBD控制端点中断
#define USBD_CEPINTSTS_SETUPTKIF_Pos     (0)                                               
#define USBD_CEPINTSTS_SETUPTKIF_Msk     (0x1ul << USBD_CEPINTSTS_SETUPTKIF_Pos)           //控制端点SETUP令牌中断
#define USBD_CEPINTSTS_SETUPPKIF_Pos     (1)                                               
#define USBD_CEPINTSTS_SETUPPKIF_Msk     (0x1ul << USBD_CEPINTSTS_SETUPPKIF_Pos)           //控制端点SETUP数据包中断
#define USBD_CEPINTSTS_OUTTKIF_Pos       (2)                                               
#define USBD_CEPINTSTS_OUTTKIF_Msk       (0x1ul << USBD_CEPINTSTS_OUTTKIF_Pos)             //控制端点OUT令牌中断
#define USBD_CEPINTSTS_INTKIF_Pos        (3)                                              
#define USBD_CEPINTSTS_INTKIF_Msk        (0x1ul << USBD_CEPINTSTS_INTKIF_Pos)              //控制端点IN令牌中断
#define USBD_CEPINTSTS_PINGIF_Pos        (4)                                              
#define USBD_CEPINTSTS_PINGIF_Msk        (0x1ul << USBD_CEPINTSTS_PINGIF_Pos)              //控制端点ping令牌中断
#define USBD_CEPINTSTS_TXPKIF_Pos        (5)                                         
#define USBD_CEPINTSTS_TXPKIF_Msk        (0x1ul << USBD_CEPINTSTS_TXPKIF_Pos)              //控制端点数据发送中断
#define USBD_CEPINTSTS_RXPKIF_Pos        (6)                                             
#define USBD_CEPINTSTS_RXPKIF_Msk        (0x1ul << USBD_CEPINTSTS_RXPKIF_Pos)              //控制端点数据接收中断
#define USBD_CEPINTSTS_NAKIF_Pos         (7)                                              
#define USBD_CEPINTSTS_NAKIF_Msk         (0x1ul << USBD_CEPINTSTS_NAKIF_Pos)               //控制端点NAK发送中断
#define USBD_CEPINTSTS_STALLIF_Pos       (8)                                               
#define USBD_CEPINTSTS_STALLIF_Msk       (0x1ul << USBD_CEPINTSTS_STALLIF_Pos)             //控制端点STALL发送中断
#define USBD_CEPINTSTS_ERRIF_Pos         (9)                                           
#define USBD_CEPINTSTS_ERRIF_Msk         (0x1ul << USBD_CEPINTSTS_ERRIF_Pos)               //控制端点USB错误中断
#define USBD_CEPINTSTS_STSDONEIF_Pos     (10)                                             
#define USBD_CEPINTSTS_STSDONEIF_Msk     (0x1ul << USBD_CEPINTSTS_STSDONEIF_Pos)           //控制端点状态完成中断
#define USBD_CEPINTSTS_BUFFULLIF_Pos     (11)                                           
#define USBD_CEPINTSTS_BUFFULLIF_Msk     (0x1ul << USBD_CEPINTSTS_BUFFULLIF_Pos)           //控制端点缓冲区满中断
#define USBD_CEPINTSTS_BUFEMPTYIF_Pos    (12)                                             
#define USBD_CEPINTSTS_BUFEMPTYIF_Msk    (0x1ul << USBD_CEPINTSTS_BUFEMPTYIF_Pos)          //控制端点缓冲区空中断



 
//USBD_OPER USBD操作命令
#define USBD_OPER_RESUMEEN_Pos           (0)                                               
#define USBD_OPER_RESUMEEN_Msk           (0x1ul << USBD_OPER_RESUMEEN_Pos)                 //如果启用了设备远程唤醒,将向主机启动一个恢复序列(发送后自动清除)。
#define USBD_OPER_HISPDEN_Pos            (1)                                               
#define USBD_OPER_HISPDEN_Msk            (0x1ul << USBD_OPER_HISPDEN_Pos)                  //USB设备控制器在复位协议期间启动线性调频序列
#define USBD_OPER_CURSPD_Pos             (2)                                               
#define USBD_OPER_CURSPD_Msk             (0x1ul << USBD_OPER_CURSPD_Pos)                   //USB设备控制器设置为“高速”


//USBD_CEPCTL USBD控制端点控制
#define USBD_CEPCTL_NAKCLR_Pos           (0)                                               
#define USBD_CEPCTL_NAKCLR_Msk           (0x1ul << USBD_CEPCTL_NAKCLR_Pos)                 //置1后,每当设置令牌被设置时,本地CPU可以花费自己的时间来完成其它工作,然后清除此位
#define USBD_CEPCTL_STALLEN_Pos          (1)                                               
#define USBD_CEPCTL_STALLEN_Msk          (0x1ul << USBD_CEPCTL_STALLEN_Pos)                //发送停止握手之后,控制端点响应任何输入或输出令牌
#define USBD_CEPCTL_ZEROLEN_Pos          (2)                                               
#define USBD_CEPCTL_ZEROLEN_Msk          (0x1ul << USBD_CEPCTL_ZEROLEN_Pos)                //USB设备控制器可以在数据阶段将零长度的数据包发送到主机到IN令牌
#define USBD_CEPCTL_FLUSH_Pos            (3)                                               
#define USBD_CEPCTL_FLUSH_Msk            (0x1ul << USBD_CEPCTL_FLUSH_Pos)                  //清除数据包缓冲区(此位自动复位)

//USBD_DMACTL  DMA控制
#define USBD_DMACTL_EPNUM_Pos            (0)                                               
#define USBD_DMACTL_EPNUM_Msk            (0xful << USBD_DMACTL_EPNUM_Pos)                  //DMA端点地址位
#define USBD_DMACTL_DMARD_Pos            (4)                                               
#define USBD_DMACTL_DMARD_Msk            (0x1ul << USBD_DMACTL_DMARD_Pos)                  //DMA读写操作
#define USBD_DMACTL_DMAEN_Pos            (5)                                               
#define USBD_DMACTL_DMAEN_Msk            (0x1ul << USBD_DMACTL_DMAEN_Pos)                  //启用DMA
#define USBD_DMACTL_SGEN_Pos             (6)                                               
#define USBD_DMACTL_SGEN_Msk             (0x1ul << USBD_DMACTL_SGEN_Pos)                   //分散收集功能
#define USBD_DMACTL_DMARST_Pos           (7)                                               
#define USBD_DMACTL_DMARST_Msk           (0x1ul << USBD_DMACTL_DMARST_Pos)                 //重置DMA状态机


//USBD_EPxINTSTS EPxINTEN 端点中断状态
#define USBD_EPINTSTS_BUFFULLIF_Pos      (0)                                               
#define USBD_EPINTSTS_BUFFULLIF_Msk      (0x1ul << USBD_EPINTSTS_BUFFULLIF_Pos)            //端点数据包缓冲区已满
#define USBD_EPINTSTS_BUFEMPTYIF_Pos     (1)                                               
#define USBD_EPINTSTS_BUFEMPTYIF_Msk     (0x1ul << USBD_EPINTSTS_BUFEMPTYIF_Pos)           //端点缓冲区为空
#define USBD_EPINTSTS_SHORTTXIF_Pos      (2)                                               
#define USBD_EPINTSTS_SHORTTXIF_Msk      (0x1ul << USBD_EPINTSTS_SHORTTXIF_Pos)            //端点最后一个数据包的长度小于最大数据包大小(EPMPS)
#define USBD_EPINTSTS_TXPKIF_Pos         (3)                                               
#define USBD_EPINTSTS_TXPKIF_Msk         (0x1ul << USBD_EPINTSTS_TXPKIF_Pos)               //端点数据发送完成中断
#define USBD_EPINTSTS_RXPKIF_Pos         (4)                                               
#define USBD_EPINTSTS_RXPKIF_Msk         (0x1ul << USBD_EPINTSTS_RXPKIF_Pos)               //端点从主机接收到数据包
#define USBD_EPINTSTS_OUTTKIF_Pos        (5)                                               
#define USBD_EPINTSTS_OUTTKIF_Msk        (0x1ul << USBD_EPINTSTS_OUTTKIF_Pos)              //端点已从主机收到数据输出令牌。 此位也由PING令牌(仅在高速中)设置
#define USBD_EPINTSTS_INTKIF_Pos         (6)                                               
#define USBD_EPINTSTS_INTKIF_Msk         (0x1ul << USBD_EPINTSTS_INTKIF_Pos)               //端点已从主机收到数据输入令牌
#define USBD_EPINTSTS_PINGIF_Pos         (7)                                               
#define USBD_EPINTSTS_PINGIF_Msk         (0x1ul << USBD_EPINTSTS_PINGIF_Pos)               //端点已从主机收到数据PING令牌
#define USBD_EPINTSTS_NAKIF_Pos          (8)                                               
#define USBD_EPINTSTS_NAKIF_Msk          (0x1ul << USBD_EPINTSTS_NAKIF_Pos)                //端点无法提供最后一个USB IN数据包,并且通过一个NAK确认
#define USBD_EPINTSTS_STALLIF_Pos        (9)                                               
#define USBD_EPINTSTS_STALLIF_Msk        (0x1ul << USBD_EPINTSTS_STALLIF_Pos)              //端点无法接受或提供最后一个USB数据包,因为端点停滞了,并被告知失速
#define USBD_EPINTSTS_NYETIF_Pos         (10)                                              
#define USBD_EPINTSTS_NYETIF_Msk         (0x1ul << USBD_EPINTSTS_NYETIF_Pos)               //端点RAM中的可用空间不足以容纳下一个即将到来的数据包
#define USBD_EPINTSTS_ERRIF_Pos          (11)                                              
#define USBD_EPINTSTS_ERRIF_Msk          (0x1ul << USBD_EPINTSTS_ERRIF_Pos)                //端点发生了错误
#define USBD_EPINTSTS_SHORTRXIF_Pos      (12)                                              
#define USBD_EPINTSTS_SHORTRXIF_Msk      (0x1ul << USBD_EPINTSTS_SHORTRXIF_Pos)            //端点收到的批量输出短数据包(包括零长度数据包)


//USBD_EPxRSPCTL 端点控制
#define USBD_EPRSPCTL_FLUSH_Pos          (0)                                               
#define USBD_EPRSPCTL_FLUSH_Msk          (0x1ul << USBD_EPRSPCTL_FLUSH_Pos)                //端点清除数据缓冲区
#define USBD_EPRSPCTL_MODE_Pos           (1)                                               
#define USBD_EPRSPCTL_MODE_Msk           (0x3ul << USBD_EPRSPCTL_MODE_Pos)                 //端点模式控制
#define USBD_EPRSPCTL_TOGGLE_Pos         (3)                                               
#define USBD_EPRSPCTL_TOGGLE_Msk         (0x1ul << USBD_EPRSPCTL_TOGGLE_Pos)               //端点切换,清除端点数据触发位
#define USBD_EPRSPCTL_HALT_Pos           (4)                                               
#define USBD_EPRSPCTL_HALT_Msk           (0x1ul << USBD_EPRSPCTL_HALT_Pos)                 //端点停止,发送一个STALL握手作为对主机令牌的响应
#define USBD_EPRSPCTL_ZEROLEN_Pos        (5)                                               
#define USBD_EPRSPCTL_ZEROLEN_Msk        (0x1ul << USBD_EPRSPCTL_ZEROLEN_Pos)              //端点收到IN令牌后,会将零数据包发送到主机
#define USBD_EPRSPCTL_SHORTTXEN_Pos      (6)                                               
#define USBD_EPRSPCTL_SHORTTXEN_Msk      (0x1ul << USBD_EPRSPCTL_SHORTTXEN_Pos)            //端点短数据包传输使能,验证缓冲区中的任何剩余数据
#define USBD_EPRSPCTL_DISBUF_Pos         (7)                                               
#define USBD_EPRSPCTL_DISBUF_Msk         (0x1ul << USBD_EPRSPCTL_DISBUF_Pos)               //端点缓冲区禁用控制,当接收到Bulk-OUT短数据包时,禁用缓冲器

//USBD_EPxCFG 端点配置
#define USBD_EPCFG_EPEN_Pos              (0)                                               
#define USBD_EPCFG_EPEN_Msk              (0x1ul << USBD_EPCFG_EPEN_Pos)                    //端点已经启用
#define USBD_EPCFG_EPTYPE_Pos            (1)                                               
#define USBD_EPCFG_EPTYPE_Msk            (0x3ul << USBD_EPCFG_EPTYPE_Pos)                  //端点类型
#define USBD_EPCFG_EPDIR_Pos             (3)                                               
#define USBD_EPCFG_EPDIR_Msk             (0x1ul << USBD_EPCFG_EPDIR_Pos)                   //端点方向
#define USBD_EPCFG_EPNUM_Pos             (4)                                               
#define USBD_EPCFG_EPNUM_Msk             (0xful << USBD_EPCFG_EPNUM_Pos)                   //端点编号,不支持两个端点具有相同的端点号

//USBD_PHYCTL USB PHY控制
#define USBD_PHYCTL_DPPUEN_Pos           (8)                                               
#define USBD_PHYCTL_DPPUEN_Msk           (0x1ul << USBD_PHYCTL_DPPUEN_Pos)                 //使能D +上拉电阻
#define USBD_PHYCTL_PHYEN_Pos            (9)                                               
#define USBD_PHYCTL_PHYEN_Msk            (0x1ul << USBD_PHYCTL_PHYEN_Pos)                  //USB PHY未挂起
#define USBD_PHYCTL_WKEN_Pos             (24)                                              
#define USBD_PHYCTL_WKEN_Msk             (0x1ul << USBD_PHYCTL_WKEN_Pos)                   //唤醒功能已启用
#define USBD_PHYCTL_VBUSDET_Pos          (31)                                              
#define USBD_PHYCTL_VBUSDET_Msk          (0x1ul << USBD_PHYCTL_VBUSDET_Pos)                //检测到VBUS

//相关操作
#define USBD_ENABLE_USB()               ((u32)(USBD->PHYCTL |= (USBD_PHYCTL_PHYEN_Msk|USBD_PHYCTL_DPPUEN_Msk))) 	//使能 USB
#define USBD_DISABLE_USB()              ((u32)(USBD->PHYCTL &= ~USBD_PHYCTL_DPPUEN_Msk)) 							//禁用 USB
#define USBD_ENABLE_PHY()               ((u32)(USBD->PHYCTL |= USBD_PHYCTL_PHYEN_Msk)) 								//使能硬件 PHY
#define USBD_DISABLE_PHY()              ((u32)(USBD->PHYCTL &= ~USBD_PHYCTL_PHYEN_Msk)) 							//禁用硬件 PHY
#define USBD_SET_OPER(x)				(USBD->OPER = (x))															//设置OPER操作命令
#define USBD_SET_SE0()                  ((u32)(USBD->PHYCTL &= ~USBD_PHYCTL_DPPUEN_Msk)) 							//Enable SE0, 强制USB PHY收发器驱动SE0
#define USBD_CLR_SE0()                  ((u32)(USBD->PHYCTL |= USBD_PHYCTL_DPPUEN_Msk)) 							//Disable SE0
#define USBD_SET_ADDR(addr)             (USBD->FADDR = (addr)) 														//设置USB功能地址
#define USBD_GET_ADDR()                 ((u32)(USBD->FADDR)) 														//获取USB功能地址
#define USBD_ENABLE_USB_INT(intr)       (USBD->GINTEN = (intr))														//设置USB相关中断
#define USBD_ENABLE_BUS_INT(intr)       (USBD->BUSINTEN = (intr))													//设置USB总线中断
#define USBD_GET_BUS_INT_FLAG()         (USBD->BUSINTSTS) 															//获取总线中断状态
#define USBD_CLR_BUS_INT_FLAG(flag)     (USBD->BUSINTSTS = flag)													//清除总线中断状态
#define USBD_ENABLE_CEP_INT(intr)       (USBD->CEPINTEN = (intr))													//设置控制端点中断
#define USBD_SET_CEP_INT_BIT(intr)		(USBD->CEPINTEN |= (intr))													//设置控制端点中断-只增加
#define USBD_CLEAR_CEP_INT_BIT(intr)    (USBD->CEPINTEN &= ~(intr))													//清除控制端点中断-只减少

#define USBD_CLR_CEP_INT_FLAG(flag)     (USBD->CEPINTSTS = flag) 													//清除控制端点中断状态
#define USBD_SET_CEP_STATE(flag)        (USBD->CEPCTL = flag)														//设置控制端点控制寄存器
#define USBD_START_CEP_IN(size)         (USBD->CEPTXCNT = size)														//设置控制端点传输数据长度
#define USBD_ENABLE_EP_INT(ep, intr)    (USBD->EPx[ep].INTEN = (intr))												//设置端点x的中断
#define USBD_SET_EP_INT_BIT(ep, intr)    (USBD->EPx[ep].INTEN |= (intr))											//设置端点x的中断-只增加
#define USBD_CLEAR_EP_INT_BIT(ep, intr)  (USBD->EPx[ep].INTEN  &= ~(intr))											//清除端点x的中断-只减少

#define USBD_GET_EP_INT_FLAG(ep)        (USBD->EPx[ep].INTSTS)														//获取端点x的中断状态
#define USBD_CLR_EP_INT_FLAG(ep, flag)  (USBD->EPx[ep].INTSTS = (flag))												//清除端点x的中断状态
#define USBD_SET_DMA_LEN(len)           (USBD->DMACNT = len)														//设置DMA传输长度
#define USBD_SET_DMA_ADDR(addr)         (USBD->DMAADDR = addr)														//设置DMA传输的地址
#define USBD_SET_DMA_READ(epnum)        (USBD->DMACTL = (USBD->DMACTL & ~USBD_DMACTL_EPNUM_Msk) | USBD_DMACTL_DMARD_Msk | epnum) 	//设置DMA为读取方向
#define USBD_SET_DMA_WRITE(epnum)       (USBD->DMACTL = (USBD->DMACTL & ~(USBD_DMACTL_EPNUM_Msk | USBD_DMACTL_DMARD_Msk)) | epnum) 	//设置DMA为写入方向
#define USBD_ENABLE_DMA()               (USBD->DMACTL |= USBD_DMACTL_DMAEN_Msk) 													//使能DMA传输
#define USBD_IS_ATTACHED()              ((u32)(USBD->PHYCTL & USBD_PHYCTL_VBUSDET_Msk)) 							//检查USB连接状态
#define USBD_WriteEPx_DataByte(ep, data) 	(*((vu8*)(&(USBD->EPx[ep].DAT))) = (data))								//写入BYTE数据到端点x数据发送寄存器
#define USBD_ReadEPx_DataByte(ep) 			(*((vu8*)(&(USBD->EPx[ep].DAT))))										//从端点x数据接收寄存器读取BYTE数据

#define Maximum(a,b)    (a)>(b) ? (a) : (b)
#define Minimum(a,b)    (a)<(b) ? (a) : (b)

//USBD需要使用到的内存拷贝
__inline void USBD_MemCopy(uint8_t *u8Dst, uint8_t *u8Src, int32_t i32Size)
{
    while (i32Size--) *u8Dst++ = *u8Src++;
}

//获取指定EP的最大数据包大小
__inline u16 USBD_GetEpMaxPacketSize(USBD_EPx EPx)
{
	if(EPx > USBD_EPL) return 0;	
    return USBD->EPx[EPx].MPS & 0x7FF;
}


//获取指定EP的缓冲区数据大小
__inline u16 USBD_GetEpDataCount(USBD_EPx EPx)
{
	if(EPx > USBD_EPL) return 0;	
    return USBD->EPx[EPx].DATCNT & 0xffff;
}


//EP配置
typedef struct
{
	u32 InitId;									//端点是否初始化了-不要在外部修改
	USBD_EPx EPx;								//EP端点-不要在外部修改
	u32 EnableIntBit;							//使能的中断
	u8 EpNum;									//EP端点编号-不要在外部修改
	u16 EpBuffOffset;							//EP端点缓冲区位置(USBD缓冲区)
	u16 EpBuffSzie;								//EP端点缓冲区大小(USBD缓冲区)
	u16 EpUSB20_MaxPackSize;					//USB2.0对应的最大数据包大小
	u16 EpMaxPackSzie;							//EP端点最大数据包大小-实际数据包大小,如果是USB1.1则固定为64字节,否则等于EpUSB20_MaxPackSize
	USBD_EP_TYPE EpType;						//EP端点类型
	USBD_EP_DIR EpDir;							//EP端点方向
	USBD_EPxIRQHandlerType EpIRQHandlerCallBack;//EP端点中断回调函数
}USBD_EP_CONFIG;





//初始化USBD
void USBD_Init(void);
void USBD_InterfaceConfig(USBD_IRQHandlerType pBusIRQCallBack, USBD_IRQHandlerType pCEpIRQCallBack, USBD_CLASS_REQ pClassReqCallBack, 
	USBD_VENDOR_REQ pVendorReqCallBack, const USBD_DESCRIPTOR *pDescData);//USBD所需接口配置
void USBD_Start(void);											//USBD启动
bool USBD_GetIdleEp(USBD_EPx *pEPx);							//获取一个空闲的EP通道
void USBD_ResetDMA(void);										//复位DMA
USBD_EP_CONFIG *USBD_GetEpConfigPointer(USBD_EPx EPx);			//获取一个端点的配置指针,可以对端点进行配置
void USBD_SetEpConfigData(USBD_EPx EPx,USBD_EP_TYPE EpType, USBD_EP_DIR EpDir, u32 EnableIntBit, u16 EpBuffOffset, u16 EpBuffSzie, u16 EpMaxPackSzie, 
	USBD_EPxIRQHandlerType pCallBack);//设置一数据个端点的配置数据
void USBD_PrepareCtrlIn(u8 *pDataBuff, u8 DataLen);				//控制端点准备响应控制命令(将数据写入缓冲区,准备等待控制端口IN令牌到来后发送到主机)
bool USBD_CtrlIn(void);											//控制端点发送数据给主机(必须在控制端点IN令牌有效时调用)
bool USBD_BulkOut(USBD_EPx EPx, u8 *pDataBuff, u32 Len);		//接收来自Bulk OUT的数据
bool USBD_BulkIn(USBD_EPx EPx, u8 *pDataBuff, u32 Len);			//写入数据到Bulk IN
bool USBD_ReadBulkOutPackData(USBD_EPx EPx, u8 *pData, u16 EpBuffSize);//读取一整包来自OUT的数据(需要等待缓冲区满)
u16 USBD_GetEpBuffSize(USBD_EPx EPx);							//获取端点缓冲区大小(从硬件配置中获取)


void USBD_SetEpBuffAddr(USBD_EPx EPx, u32 u32Base, u32 u32Len);	//设置端点缓冲区地址
void USBD_EpConfig(USBD_EPx EPx, u8 EpNum, USBD_EP_TYPE EpType, USBD_EP_DIR EpDir);			//端点配置
void USBD_SetEpStall(USBD_EPx EPx);								//将USB端点停止状态设置为指定的端点ID。 端点将自动响应STALL令牌
void USBD_ClearEpStall(USBD_EPx EPx);							//清除USB端点停止状态
u32 USBD_GetEpStall(USBD_EPx EPx);								//获取指定端点ID的USB端点停顿状态
void USBD_SwReset(void);										//软复位
u16 USBD_ReadEPxOutData(USBD_EPx EPx, u8 *pData);				//从EPx的OUT缓冲区中读取数据


void USBD_ClearStall(u8 EpNum);									//清除指定编号端点的停止状态,端点将返回ACK / NAK令牌
u32 USBD_GetStall(u8 EpNum);									//获取指定端点号的停止状态


#endif //_USB_D_

测试代码,先只测试USBD底层初始化,已经EP端点初始化,后面将会实现虚拟U盘功能。

USBD_EPx g_USBD_MassStorageIN_EP = USBD_INVALID_EP;				//host in ep 需要发送给主机的数据端点
USBD_EPx g_USBD_MassStorageOUT_EP = USBD_INVALID_EP;			//host out ep 来自主机的数据



#define USBD_MASS_STORAGE_PACK_SIZE			64					//最大数据包定义bluk大小定义
#define USBD_MASS_STORAGE_EP_BUFF_SIZE		512					//端点缓冲区大小定义


//USB从机线程
void TaskUSBD(void *pdata)	
{
	OS_ERR err;
	CPU_TS USBD_TaskTs;
	
	USBD_Init();	//初始化USBD
	USBD_InterfaceConfig(NULL, NULL, USBD_ClassRequest, NULL, &cg_USBD_Descriptor);//USBD所需接口配置
	
	USBD_GetIdleEp(&g_USBD_MassStorageIN_EP);							//获取一个空闲的EP通道 IN
	USBD_SetEpConfigData(	g_USBD_MassStorageIN_EP, 					//IN
							USBD_EP_TYPE_BULK, 							//BULK
							USBD_EP_INPUT, 
							0, 
							CEP_BUFF_OFFSET+CEP_BUFF_SIZE, USBD_MASS_STORAGE_EP_BUFF_SIZE, USBD_MASS_STORAGE_PACK_SIZE, NULL);								//设置一数据个端点的配置数据
	
	USBD_GetIdleEp(&g_USBD_MassStorageOUT_EP);							//获取一个空闲的EP通道 OUT
	USBD_SetEpConfigData(	g_USBD_MassStorageOUT_EP, 					//OUT
							USBD_EP_TYPE_BULK, 							//BULK
							USBD_EP_OUTPUT, 
							USBD_EPINTSTS_RXPKIF_Msk, 
							CEP_BUFF_OFFSET+CEP_BUFF_SIZE+USBD_MASS_STORAGE_EP_BUFF_SIZE, USBD_MASS_STORAGE_EP_BUFF_SIZE, USBD_MASS_STORAGE_PACK_SIZE, 
							mass_out_ep_irq);							//设置一数据个端点的配置数据
	USBD_MassStorageInit(&sg_USBD_MassStorage_Handle, g_USBD_MassStorageIN_EP, g_USBD_MassStorageOUT_EP, &cg_usbd_sd_storage, 1024*64, STORAGE_LUN_NBR);	//USB存储设备初始化(只能调用一次)
	
	//等待插上USB
	while(!USBD_IS_ATTACHED())
	{
		Sleep(10);
	}
	USBD_Start();
	
	while(1) 
	{
		OSTaskSemPend(0, OS_OPT_POST_NONE, &USBD_TaskTs, &err);			//等待一个信号量,超时时间-永久
		USBD_MassStorageHandle(&sg_USBD_MassStorage_Handle);			//USB存储设备处理(OUT接收数据中断中调用)
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cp1300

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值