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 PHYSYS_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); //复位地址为0USBD_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); //复位地址为0AIC_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(); //启动DMAwhile(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); //端点地址使用端点号+1for (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); //端点地址使用端点号+1for (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 hostswitch (g_USBD_Setup.Request) {case USBD_GET_CONFIGURATION: //获取设备配置描述符-主机枚举设备{uart_printf("GET_CONFIGURATION\r\n");// Return current configuration settingUSBD_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");// Deviceif (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 deviceswitch (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 enableswitch (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(); //复位DMAfor(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端点AUSBD_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_MaxPackSizeUSBD_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(); //初始化USBDUSBD_InterfaceConfig(NULL, NULL, USBD_ClassRequest, NULL, &cg_USBD_Descriptor);//USBD所需接口配置USBD_GetIdleEp(&g_USBD_MassStorageIN_EP); //获取一个空闲的EP通道 INUSBD_SetEpConfigData( g_USBD_MassStorageIN_EP, //INUSBD_EP_TYPE_BULK, //BULKUSBD_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通道 OUTUSBD_SetEpConfigData( g_USBD_MassStorageOUT_EP, //OUTUSBD_EP_TYPE_BULK, //BULKUSBD_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存储设备初始化(只能调用一次)//等待插上USBwhile(!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接收数据中断中调用)}
}
NUC970 裸机USBD驱动(第一章)相关推荐
- 【正点原子MP157连载】第一章 本书学习方法-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- Android项目驱动式开发教程 第2版,《Android项目驱动式开发教程》第一章开发入门.ppt...
<Android项目驱动式开发教程>第一章开发入门 1.4 项目框架分析 4 android:versionName="1.0" > 5 8 第9行代码andro ...
- 微型计算机与裸机,第一章 微型计算机基础
考点一.理解微处理器.微型计算机和微型计算机系统的概念及其相互关系. 1.1微信计算机系统组成 微型计算机是指以微处理器为核心,配上存储器,输入/输出接口电路等所组成的计算机. 微型计算机系统是指以微 ...
- [转]写一个块设备驱动(第一章)
写一个块设备驱动1,2(转) 2009/09/03 15:42 第1章 +---------------------------------------------------+ | ...
- 一步一步走进块驱动之第一章
第一章 本教程修改自赵磊的网上的一系列教程.本人觉得该系列教程写的非常不错.以风趣幽默的语言将块驱动写的非常详细,对于入门教程,应该属于一份经典了. 本人在这对此系列教程对细微的修改,仅针对Linux ...
- 【正点原子MP157连载】第一章 Ubuntu系统安装-摘自【正点原子】【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- 知道第一章计算机基础知识作业答案,大学计算机基础作业答案
大学计算机基础作业答案 第一章 现代社会与计算机 1. 什么事信息,其主要特征是什么? 答:信息是可传递和共享的,可消除人们认知上的不确定因素,对人们的决策具有现实或潜在价值的知识. 特征:普遍性.依 ...
- 【王道考研】操作系统 笔记 第一章
特此鸣谢王道考研 本文参考王道考研的相关课程 若有侵权请联系,立删 其余笔记链接: [王道考研]操作系统笔记 第一章_才疏学浅743的博客-CSDN博客 [王道考研]操作系统 笔记 第二章上 进程调度 ...
- 操作系统王道考研复习——第一章(计算机系统概述)
操作系统王道考研复习--第一章(计算机系统概述) 计算机系统概述 1.1操作系统的基本概念 1.1.1 操作系统的概念 1.1.2 操作系统的特征 1. 并发 2. 共享 3. 虚拟 4. 异步 1. ...
最新文章
- Nature:剖腹产到底好不好?——肠道菌群的视角
- SecureRandom
- IOS的pch文件,NSTimer定时器,运行消息循环,随机色使用
- Codeforces Round #686 (Div. 3) E. Number of Simple Paths 基环树 + 容斥
- arm linux挂载ubi,挂载ubifs文件系统分区
- DT CMS致力于Spring Boot2.3.5 Vue前后端分离的RBAC权限框架(盛世美颜!)
- ASP.NET Core:使用IdentityServer构建可靠的身份验证和授权系统
- socket编程之回声服务器
- vue-router parmas与query的区别
- 《Java程序员职场全功略:从小工到专家》连载六:亚洲企业工作模式
- jenkins安装与自动部署详细说明
- 手把手写一个vue3的组件
- python3生成exe文件_python3.7打包成exe就三步
- 让你的网站用上炫酷的中文字体
- 互联网寒冬、裁员,作为程序员的我们,应该如何去应对?
- python urllib.parse_Python3的urllib.parse常用函数小结(urlencode,quote,quote_plus,unquote,unquote_plus等)...
- Linux--详细安装教程
- 网络爬虫技术是什么,网络爬虫的基本工作流程是什么?
- [ATF] ARM Trusted firmware 构建选项
- Hexo系列(四) NexT主题配置