官方提供的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接收数据中断中调用)
}
}