在端点0由GET_REPORT类请求上传报表
一般来说HID设备的报表在端点数量资源充裕的情况下都走中断端点上传,不会走控制端点即端点0上传,如果资源不足,可以利用端点0上传报表,USB协议中也是支持在端点0上传报表的。
以CH582为例,EVT中的键鼠复合例程中,键盘走端点1而鼠标走端点2上传报表,互不耽误。如果端点资源比较紧张,多个设备的报表比如键鼠的报表,可以走同一个端点上传,这时就要在同一个端点上传的数据中区分报表。区分方法也很简单,在报表开头再添加一个字节作为Report ID,比如说键盘和鼠标的Report ID分别设置为0x01和0x02,上传8字节键盘报表前再加个0x01即可。如果端点资源真的很紧张,其他端点都被占用了,又要跑CDC又要跑MIDI等等,那还有端点0可以使用。当然当端点资源很紧张时需要首先评估一下在端点0上报数据能不能满足稳定性要求,如果非要实现严格的1ms上传一个HID报表,还是建议留足资源。
默认例程代码中没有处理端点0上传的HID类请求,可以按参考下方代码。下方代码中新增了一个结构体记录各标志(ram紧张的话可以压缩压缩,按位标志),在主函数中处理报表的上传。PS:下列代码上传超过64字节的报表时有BUG,待修复。
改动之处见有//XXX注释的代码行,复制到编译器中,在右侧一栏有蓝色标记帮助寻找位置。
#include "CH58x_common.h" #define DEF_USB_GET_REPORT 0x01 //XXX 主机可以使用端点0来获取报表描述符 struct USB_ep0_st //XXX
{
uint8_t ep0_in; //置1时表示收到端点0收到in令牌包
uint8_t ep0_1st_data; //第1个数据包同步切换为data1
uint8_t ep0_getting_report; //置1时表示正在获取报表,报表全部传输完后手动置0
uint8_t ep0_report_choosing; //置1时表示收到了GET_REPORT请求,正在进行报表选择
uint8_t ep0_report_ID; //HID类请求中的report ID
uint16_t ep0_Req_Len; //HID类请求中的report长度
uint16_t ep0_Send_Len; //实际上传发送的长度
}; struct USB_ep0_st ep0_st = {0}; //XXX 帮助进行HID类请求的回复 const uint8_t HID_Descr_Ep0_1[] = { //XXX 先行定义待上传数据
0x03, 0x04
}; const uint8_t HID_Descr_Ep0_2[] = { //XXX 先行定义待上传数据
0x0a, 0x0b, 0x0c, 0x0d, 0x0f
}; #define DevEP0SIZE 0x40
// 设备描述符
const uint8_t MyDevDescr[] = {0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, DevEP0SIZE, 0x3d, 0x41, 0x07, 0x21, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01};
// 配置描述符
const uint8_t MyCfgDescr[] = {
0x09, 0x02, 0x3b, 0x00, 0x02, 0x01, 0x00, 0xA0, 0x32, //配置描述符
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x01, 0x00, //接口描述符,键盘
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3e, 0x00, //HID类描述符
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x0a, //端点描述符
0x09, 0x04, 0x01, 0x00, 0x01, 0x03, 0x01, 0x02, 0x00, //接口描述符,鼠标
0x09, 0x21, 0x10, 0x01, 0x00, 0x01, 0x22, 0x34, 0x00, //HID类描述符
0x07, 0x05, 0x82, 0x03, 0x04, 0x00, 0x0a //端点描述符 };
/* USB速度匹配描述符 */
const uint8_t My_QueDescr[] = {0x0A, 0x06, 0x00, 0x02, 0xFF, 0x00, 0xFF, 0x40, 0x01, 0x00}; /* USB全速模式,其他速度配置描述符 */
uint8_t USB_FS_OSC_DESC[sizeof(MyCfgDescr)] = {
0x09, 0x07, /* 其他部分通过程序复制 */
}; // 语言描述符
const uint8_t MyLangDescr[] = {0x04, 0x03, 0x09, 0x04};
// 厂家信息
const uint8_t MyManuInfo[] = {0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0};
// 产品信息
const uint8_t MyProdInfo[] = {0x0C, 0x03, 'C', 0, 'H', 0, '5', 0, '7', 0, 'x', 0};
/*HID类报表描述符*/
const uint8_t KeyRepDesc[] = {0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25,
0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x03,
0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02, 0x95, 0x05, 0x75, 0x01, 0x91,
0x01, 0x95, 0x06, 0x75, 0x08, 0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x29, 0x91, 0x81,
0x00, 0xC0};
const uint8_t MouseRepDesc[] = {0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29,
0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x03, 0x81, 0x02, 0x75, 0x05, 0x95, 0x01,
0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75,
0x08, 0x95, 0x03, 0x81, 0x06, 0xC0, 0xC0}; /**********************************************************/
uint8_t DevConfig, Ready;
uint8_t SetupReqCode;
uint16_t SetupReqLen;
const uint8_t *pDescr;
uint8_t Report_Value = 0x00;
uint8_t Idle_Value = 0x00;
uint8_t USB_SleepStatus = 0x00; /* USB睡眠状态 */ /*鼠标键盘数据*/
uint8_t HIDMouse[4] = {0x0, 0x0, 0x0, 0x0};
uint8_t HIDKey[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
/******** 用户自定义分配端点RAM ****************************************/
__attribute__((aligned(4))) uint8_t EP0_Databuf[64 + 64 + 64]; //ep0(64)+ep4_out(64)+ep4_in(64)
__attribute__((aligned(4))) uint8_t EP1_Databuf[64 + 64]; //ep1_out(64)+ep1_in(64)
__attribute__((aligned(4))) uint8_t EP2_Databuf[64 + 64]; //ep2_out(64)+ep2_in(64)
__attribute__((aligned(4))) uint8_t EP3_Databuf[64 + 64]; //ep3_out(64)+ep3_in(64) /*********************************************************************
* @fn USB_DevTransProcess
*
* @brief USB 传输处理函数
*
* @return none
*/
void USB_DevTransProcess(void)
{
uint8_t len, chtype;
uint8_t intflag, errflag = 0; intflag = R8_USB_INT_FG;
if(intflag & RB_UIF_TRANSFER)
{
if((R8_USB_INT_ST & MASK_UIS_TOKEN) != MASK_UIS_TOKEN) // 非空闲
{
switch(R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
// 分析操作令牌和端点号
{
case UIS_TOKEN_IN:
{
switch(SetupReqCode)
{
case USB_GET_DESCRIPTOR:
len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen; // 本次传输长度
memcpy(pEP0_DataBuf, pDescr, len); /* 加载上传数据 */
SetupReqLen -= len;
pDescr += len;
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL ^= RB_UEP_T_TOG; // 翻转
break;
case USB_SET_ADDRESS:
R8_USB_DEV_AD = (R8_USB_DEV_AD & RB_UDA_GP_BIT) | SetupReqLen;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break; case USB_SET_FEATURE:
break; default:
R8_UEP0_T_LEN = 0; // 状态阶段完成中断或者是强制上传0长度数据包结束控制传输
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
ep0_st.ep0_in = 0; //XXX 清标志表示已上传完成
}
break; case UIS_TOKEN_OUT:
{
len = R8_USB_RX_LEN;
if(SetupReqCode == 0x09)
{
PRINT("[%s] Num Lock\t", (pEP0_DataBuf[0] & (1<<0)) ? "*" : " ");
PRINT("[%s] Caps Lock\t", (pEP0_DataBuf[0] & (1<<1)) ? "*" : " ");
PRINT("[%s] Scroll Lock\n", (pEP0_DataBuf[0] & (1<<2)) ? "*" : " ");
}
}
break; case UIS_TOKEN_OUT | 1:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP1_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP1_OUT_Deal(len);
}
}
break; case UIS_TOKEN_IN | 1:
R8_UEP1_CTRL ^= RB_UEP_T_TOG;
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break; case UIS_TOKEN_OUT | 2:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP2_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP2_OUT_Deal(len);
}
}
break; case UIS_TOKEN_IN | 2:
R8_UEP2_CTRL ^= RB_UEP_T_TOG;
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break; case UIS_TOKEN_OUT | 3:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{ // 不同步的数据包将丢弃
R8_UEP3_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP3_OUT_Deal(len);
}
}
break; case UIS_TOKEN_IN | 3:
R8_UEP3_CTRL ^= RB_UEP_T_TOG;
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break; case UIS_TOKEN_OUT | 4:
{
if(R8_USB_INT_ST & RB_UIS_TOG_OK)
{
R8_UEP4_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP4_OUT_Deal(len);
}
}
break; case UIS_TOKEN_IN | 4:
R8_UEP4_CTRL ^= RB_UEP_T_TOG;
R8_UEP4_CTRL = (R8_UEP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break; default:
break;
}
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
if(R8_USB_INT_ST & RB_UIS_SETUP_ACT) // Setup包处理
{
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
SetupReqLen = pSetupReqPak->wLength;
SetupReqCode = pSetupReqPak->bRequest;
chtype = pSetupReqPak->bRequestType; len = 0;
errflag = 0;
if((pSetupReqPak->bRequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD)
{
/* 非标准请求 */
/* 其它请求,如类请求,产商请求等 */
if(pSetupReqPak->bRequestType & 0x40)
{
/* 厂商请求 */
}
else if(pSetupReqPak->bRequestType & 0x20)
{
switch(SetupReqCode)
{
case DEF_USB_SET_IDLE: /* 0x0A: SET_IDLE */
Idle_Value = EP0_Databuf[3];
break; //这个一定要有 case DEF_USB_SET_REPORT: /* 0x09: SET_REPORT */
break; case DEF_USB_SET_PROTOCOL: /* 0x0B: SET_PROTOCOL */
Report_Value = EP0_Databuf[2];
break; case DEF_USB_GET_IDLE: /* 0x02: GET_IDLE */
EP0_Databuf[0] = Idle_Value;
len = 1;
break; case DEF_USB_GET_PROTOCOL: /* 0x03: GET_PROTOCOL */
EP0_Databuf[0] = Report_Value;
len = 1;
break; case DEF_USB_GET_REPORT: //XXX
ep0_st.ep0_report_ID = (uint8_t)pSetupReqPak->wValue; //保存report ID
ep0_st.ep0_Req_Len = SetupReqLen; //保存请求中索取的长度
ep0_st.ep0_report_choosing = 1; //置标志表示待选择report ID
PRINT("GET_REPORT_ID\n");
break; default:
errflag = 0xFF;
}
}
}
else /* 标准请求 */
{
switch(SetupReqCode)
{
case USB_GET_DESCRIPTOR:
{
switch(((pSetupReqPak->wValue) >> 8))
{
case USB_DESCR_TYP_DEVICE:
{
pDescr = MyDevDescr;
len = MyDevDescr[0];
}
break; case USB_DESCR_TYP_CONFIG:
{
pDescr = MyCfgDescr;
len = MyCfgDescr[2];
}
break; case USB_DESCR_TYP_HID:
switch((pSetupReqPak->wIndex) & 0xff)
{
/* 选择接口 */
case 0:
pDescr = (uint8_t *)(&MyCfgDescr[18]);
len = 9;
break; case 1:
pDescr = (uint8_t *)(&MyCfgDescr[43]);
len = 9;
break; default:
/* 不支持的字符串描述符 */
errflag = 0xff;
break;
}
break; case USB_DESCR_TYP_REPORT:
{
if(((pSetupReqPak->wIndex) & 0xff) == 0) //接口0报表描述符
{
pDescr = KeyRepDesc; //数据准备上传
len = sizeof(KeyRepDesc);
}
else if(((pSetupReqPak->wIndex) & 0xff) == 1) //接口1报表描述符
{
pDescr = MouseRepDesc; //数据准备上传
len = sizeof(MouseRepDesc);
Ready = 1; //如果有更多接口,该标准位应该在最后一个接口配置完成后有效
}
else
len = 0xff; //本程序只有2个接口,这句话正常不可能执行
}
break; case USB_DESCR_TYP_STRING:
{
switch((pSetupReqPak->wValue) & 0xff)
{
case 1:
pDescr = MyManuInfo;
len = MyManuInfo[0];
break;
case 2:
pDescr = MyProdInfo;
len = MyProdInfo[0];
break;
case 0:
pDescr = MyLangDescr;
len = MyLangDescr[0];
break;
default:
errflag = 0xFF; // 不支持的字符串描述符
break;
}
}
break; case 0x06:
pDescr = (uint8_t *)(&My_QueDescr[0]);
len = sizeof(My_QueDescr);
break; case 0x07:
memcpy(&USB_FS_OSC_DESC[2], &MyCfgDescr[2], sizeof(MyCfgDescr) - 2);
pDescr = (uint8_t *)(&USB_FS_OSC_DESC[0]);
len = sizeof(USB_FS_OSC_DESC);
break; default:
errflag = 0xff;
break;
}
if(SetupReqLen > len)
SetupReqLen = len; //实际需上传总长度
len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
memcpy(pEP0_DataBuf, pDescr, len);
pDescr += len;
}
break; case USB_SET_ADDRESS:
SetupReqLen = (pSetupReqPak->wValue) & 0xff;
break; case USB_GET_CONFIGURATION:
pEP0_DataBuf[0] = DevConfig;
if(SetupReqLen > 1)
SetupReqLen = 1;
break; case USB_SET_CONFIGURATION:
DevConfig = (pSetupReqPak->wValue) & 0xff;
break; case USB_CLEAR_FEATURE:
{
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP) // 端点
{
switch((pSetupReqPak->wIndex) & 0xff)
{
case 0x83:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x03:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
case 0x82:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x02:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
case 0x81:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
break;
case 0x01:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
break;
default:
errflag = 0xFF; // 不支持的端点
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
if(pSetupReqPak->wValue == 1)
{
USB_SleepStatus &= ~0x01;
}
}
else
{
errflag = 0xFF;
}
}
break; case USB_SET_FEATURE:
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
{
/* 端点 */
switch(pSetupReqPak->wIndex)
{
case 0x83:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x03:
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
case 0x82:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x02:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
case 0x81:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
break;
case 0x01:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
break;
default:
/* 不支持的端点 */
errflag = 0xFF; // 不支持的端点
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
if(pSetupReqPak->wValue == 1)
{
/* 设置睡眠 */
USB_SleepStatus |= 0x01;
}
}
else
{
errflag = 0xFF;
}
break; case USB_GET_INTERFACE:
pEP0_DataBuf[0] = 0x00;
if(SetupReqLen > 1)
SetupReqLen = 1;
break; case USB_SET_INTERFACE:
break; case USB_GET_STATUS:
if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
{
/* 端点 */
pEP0_DataBuf[0] = 0x00;
switch(pSetupReqPak->wIndex)
{
case 0x83:
if((R8_UEP3_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break; case 0x03:
if((R8_UEP3_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break; case 0x82:
if((R8_UEP2_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break; case 0x02:
if((R8_UEP2_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break; case 0x81:
if((R8_UEP1_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break; case 0x01:
if((R8_UEP1_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
{
pEP0_DataBuf[0] = 0x01;
}
break;
}
}
else if((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
{
pEP0_DataBuf[0] = 0x00;
if(USB_SleepStatus)
{
pEP0_DataBuf[0] = 0x02;
}
else
{
pEP0_DataBuf[0] = 0x00;
}
}
pEP0_DataBuf[1] = 0;
if(SetupReqLen >= 2)
{
SetupReqLen = 2;
}
break; default:
errflag = 0xff;
break;
}
}
if(errflag == 0xff) // 错误或不支持
{
// SetupReqCode = 0xFF;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; // STALL
}
else if(!ep0_st.ep0_report_choosing) //XXX 如果有待选择的报表,放到主函数里处理
{
if(chtype & 0x80) // 上传
{
len = (SetupReqLen > DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
SetupReqLen -= len;
}
else
len = 0; // 下传
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK; // 默认数据包是DATA1
} R8_USB_INT_FG = RB_UIF_TRANSFER;
}
}
else if(intflag & RB_UIF_BUS_RST)
{
R8_USB_DEV_AD = 0;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_INT_FG = RB_UIF_BUS_RST;
}
else if(intflag & RB_UIF_SUSPEND)
{
if(R8_USB_MIS_ST & RB_UMS_SUSPEND)
{
;
} // 挂起
else
{
;
} // 唤醒
R8_USB_INT_FG = RB_UIF_SUSPEND;
}
else
{
R8_USB_INT_FG = intflag;
}
} /*********************************************************************
* @fn DevHIDMouseReport
*
* @brief 上报鼠标数据
*
* @return none
*/
void DevHIDMouseReport(uint8_t mouse)
{
HIDMouse[0] = mouse;
memcpy(pEP2_IN_DataBuf, HIDMouse, sizeof(HIDMouse));
DevEP2_IN_Deal(sizeof(HIDMouse));
} /*********************************************************************
* @fn DevHIDKeyReport
*
* @brief 上报键盘数据
*
* @return none
*/
void DevHIDKeyReport(uint8_t key)
{
HIDKey[2] = key;
memcpy(pEP1_IN_DataBuf, HIDKey, sizeof(HIDKey));
DevEP1_IN_Deal(sizeof(HIDKey));
} /*********************************************************************
* @fn DevWakeup
*
* @brief 设备模式唤醒主机
*
* @return none
*/
void DevWakeup(void)
{
R16_PIN_ANALOG_IE &= ~(RB_PIN_USB_DP_PU);
R8_UDEV_CTRL |= RB_UD_LOW_SPEED;
mDelaymS(2);
R8_UDEV_CTRL &= ~RB_UD_LOW_SPEED;
R16_PIN_ANALOG_IE |= RB_PIN_USB_DP_PU;
} /*********************************************************************
* @fn DebugInit
*
* @brief 调试初始化
*
* @return none
*/
void DebugInit(void)
{
GPIOA_SetBits(GPIO_Pin_9);
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
} /*********************************************************************
* @fn main
*
* @brief 主函数
*
* @return none
*/
int main()
{
SetSysClock(CLK_SOURCE_PLL_60MHz); DebugInit();
PRINT("start\n"); pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
pEP3_RAM_Addr = EP3_Databuf; USB_DeviceInit(); PFIC_EnableIRQ(USB_IRQn); while(1)
{
if(ep0_st.ep0_report_choosing)
{
switch(ep0_st.ep0_report_ID) //XXX 根据USB传输中断中的赋值选择
{
case 0x03:
ep0_st.ep0_1st_data = 1;
ep0_st.ep0_getting_report = 1; //选好报表后将要上传
PRINT("select id %d\n", ep0_st.ep0_report_ID);
pDescr = HID_Descr_Ep0_1; //选择预设的待上传数据,可改动
ep0_st.ep0_Send_Len = sizeof(HID_Descr_Ep0_1); //实际发送长度预设为实际长度 if (ep0_st.ep0_Send_Len > ep0_st.ep0_Req_Len) //ep0_Req_Len中存放的是HID请求中的长度,ep0_Send_Len存放应该上传的长度
{ //如果实际长度不足,按实际长度发送
ep0_st.ep0_Send_Len = ep0_st.ep0_Req_Len; //如果实际长度太长,按HID请求中的长度发送
PRINT("select IN_if\n");
}
PRINT("select len %d\n", ep0_st.ep0_Send_Len);
break; case 0x0A:
ep0_st.ep0_1st_data = 1;
ep0_st.ep0_getting_report = 1; //选好报表后将要上传
PRINT("select id %d\n", ep0_st.ep0_report_ID);
pDescr = HID_Descr_Ep0_2; //选择预设的待上传数据,可改动
ep0_st.ep0_Send_Len = sizeof(HID_Descr_Ep0_2); //实际发送长度预设为实际长度 if (ep0_st.ep0_Send_Len > ep0_st.ep0_Req_Len) //ep0_Req_Len中存放的是HID请求中的长度,ep0_Send_Len存放应该上传的长度
{ //如果实际长度不足,按实际长度发送
ep0_st.ep0_Send_Len = ep0_st.ep0_Req_Len; //如果实际长度太长,按HID请求中的长度发送
PRINT("select IN_if\n");
}
PRINT("select len %d\n", ep0_st.ep0_Send_Len);
break; default: //无法识别,应该发stall,参考上面的处理,这里没有添加
break;
}
ep0_st.ep0_report_choosing = 0; //清标志,报表数组选择完了
} if((ep0_st.ep0_in == 0) && (ep0_st.ep0_getting_report == 1)) //XXX 上一包IN令牌包完成且正在上传报表
{
PRINT("S_IN\n");
uint16_t len = 0; //存放待发送的长度 if(ep0_st.ep0_Send_Len <= DevEP0SIZE) //检查一包能否传完
{
len = ep0_st.ep0_Send_Len;
}
else
{
len = DevEP0SIZE;
} memcpy(pEP0_DataBuf, pDescr, len);
ep0_st.ep0_in = 1; //置位表示当前IN事务正在上传
ep0_st.ep0_Send_Len -= len; //记录剩下的需要发送的数据长度
pDescr += len; //更新接下来需要发送的数据的起始地址,拷贝函数用
if(!ep0_st.ep0_Send_Len)
{
ep0_st.ep0_getting_report = 0; //报表上传完毕,清标志
}
R8_UEP0_T_LEN = len; if(ep0_st.ep0_1st_data) //控制传输中第一个数据包同步切换为data1
{
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
ep0_st.ep0_1st_data = 0; //清标志
}
else //第一个包之后同步切换
{
R8_UEP0_CTRL ^= RB_UEP_T_TOG; // 同步切换。IN方向(对于单片机就是T方向)的PID中的DATA0和DATA1切换
R8_UEP0_CTRL = (R8_UEP0_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK;
} PRINT("S_%d\n", ep0_st.ep0_Send_Len);
}
}
} /*********************************************************************
* @fn DevEP1_OUT_Deal
*
* @brief 端点1数据处理
*
* @return none
*/
void DevEP1_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i; for(i = 0; i < l; i++)
{
pEP1_IN_DataBuf[i] = ~pEP1_OUT_DataBuf[i];
}
DevEP1_IN_Deal(l);
} /*********************************************************************
* @fn DevEP2_OUT_Deal
*
* @brief 端点2数据处理
*
* @return none
*/
void DevEP2_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i; for(i = 0; i < l; i++)
{
pEP2_IN_DataBuf[i] = ~pEP2_OUT_DataBuf[i];
}
DevEP2_IN_Deal(l);
} /*********************************************************************
* @fn DevEP3_OUT_Deal
*
* @brief 端点3数据处理
*
* @return none
*/
void DevEP3_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i; for(i = 0; i < l; i++)
{
pEP3_IN_DataBuf[i] = ~pEP3_OUT_DataBuf[i];
}
DevEP3_IN_Deal(l);
} /*********************************************************************
* @fn DevEP4_OUT_Deal
*
* @brief 端点4数据处理
*
* @return none
*/
void DevEP4_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
uint8_t i; for(i = 0; i < l; i++)
{
pEP4_IN_DataBuf[i] = ~pEP4_OUT_DataBuf[i];
}
DevEP4_IN_Deal(l);
} /*********************************************************************
* @fn USB_IRQHandler
*
* @brief USB中断函数
*
* @return none
*/
__INTERRUPT
__HIGH_CODE
void USB_IRQHandler(void) /* USB中断服务程序,使用寄存器组1 */
{
USB_DevTransProcess();
}
在端点0由GET_REPORT类请求上传报表的更多相关文章
- maven工程 java 实现文件上传 SSM ajax异步请求上传
java ssm框架实现文件上传 实现:单文件上传.多文件上传(单选和多选),并且用 ajax 异步刷新,在当前界面显示上传的文件 首先springmvc的配置文件要配置上传文件解析器: <!- ...
- SSM框架下,使用ajax请求上传文件(doc\docx\excel\图片等)
1.准备工作 1.1.添加上传必要jar包 <dependency> <groupId>commons-io</groupId> <artifactId> ...
- ASP无惧上传类不能上传中文双引号文件及ASP函数InStr存在bug
ASP无惧上传类不能上传中文双引号文件及ASP函数InStr存在bug 近日发现eWebEditor V2.8 asp 版本上传文件文件名不能包含中文双引号,发现eWebEditor使用ASP“无惧上 ...
- upload三种上传方式(上)---Servlet---post---commons-fileupload.1.2.1.jar方式请求上传文件
上传前进行的配置选项: 1.在下方的Servers中,右键你的tomcat--open,选中下面两个配置. 第一个:Serve modules without publishing 作用:tomcat ...
- tp5.0整合七牛云图片上传
转:https://www.cnblogs.com/adobe-lin/p/7699638.html 这里以上传图片为例 上传其他文件也是大同小异 使用composer安装gmars/tp5-qini ...
- servlet3.0获取参数与文件上传代码示例
转: servlet3.0获取参数与文件上传代码示例 2018年08月26日 20:25:35 苏凯勇往直前 阅读数:98 package com.igeek.servlet; import ...
- POST请求上传多张图片并携带参数
POST请求上传多张图片并携带参数 在iOS中,用POST请求携带参数上传图片是非常恶心的事情,HTTPBody部分完全需要我们自己来配置,这个HTTPBody分为3个部分,头部分可以携带参数,中间部 ...
- el-upload控件一次接口请求上传多个文件
el-upload组件默认情况下上传多少个文件就会请求多少次上传接口,如何一次上传多个文件而不必多次请求上传接口呢?直接看代码 html <el-upload :action="act ...
- 笔谈HTTP Multipart POST请求上传文件
公司一做iOS开发的同事用HTTP Multipart POST请求上传语音数据,但是做了两天都没搞定,项目经理找到我去帮忙弄下.以前做项目只用过get.post,对于现在这个跟服务器交互的表单请求我 ...
- python中使用multipart/form-data请求上传文件
最近测试的接口是上传文件的接口,上传单个文件,我主要使用了2种方法~ 接口例如: URL: http://www.baidu.com/*** method:post 参数: { "salar ...
随机推荐
- 轻松解决 CSS 代码都在一行的问题
前言 最近在做博客园的界面美化,用的是博客园[guangzan]的开源项目,配置超级简单,只需要复制粘贴代码就好啦. 但在粘贴 CSS 代码时遇到一个问题,那就是所有代码都挤在了一行,没有一点排板的样 ...
- 从0开始学Java 第一期 开发前的准备
Java 学习(一) - 开发前的准备 前言 由于一些项目上的需要,我得学习一下 Java 这门语言(主要是想写Android),本人并非0基础,至少在上个学期学习了一门必修的程序设计(C语言),所以 ...
- 【随笔记】FRAM 铁电随机存储器(MB85RC04VPNF)
参考资料 器件规格书:https://atta.szlcsc.com/upload/public/pdf/source/20131104/1457706659695.pdf I2C总线规范:https ...
- 【学习笔记】QT从入门到实战完整版(Lambda)(2)
Lambda Lambda 表达式很有意思,相信很多人初次见到 Lambda 表达式都会不能理解有什么用,我也一样,看了视频教程之后,突然意识到,Lambda 真的是太好用了,它可以在某些情况下可以很 ...
- .NET 6学习笔记(6)——SSL证书的导出和格式转换
除了广阔的互联网,这个世界上还存在很多运行在公司内网的Web Application.假设有团队A提供的网站A,现团队B需要将网站B与之集成.网站A已使用了自签的SSL证书.团队B希望能够导出该SSL ...
- .NET 团队 更新了 .NET 语言策略
2023年2月6日 ,.NET团队在官方博客上发布了.NET 语言策略的更新文章,具体参见 https://devblogs.microsoft.com/dotnet/update-to-the-do ...
- SQLSERVER 的 truncate 和 delete 有区别吗?
一:背景 1. 讲故事 在面试中我相信有很多朋友会被问到 truncate 和 delete 有什么区别 ,这是一个很有意思的话题,本篇我就试着来回答一下,如果下次大家遇到这类问题,我的答案应该可以帮 ...
- 简单了解C语言如何构建多文件项目
简单理解C语言如何构建工程 首先我们最好有一个好用的编辑器,vscode也好,visual studio也罢,dev也行,这里我们使用的是code::block: 一.了解C语言工程的构建原理 首 ...
- 使用一个文件集中管理你的 Nuget 依赖版本号
在 .net 7 以前,项目对于 nuget 依赖项的版本依赖散落与解决方案的各个角落.这导致升级维护和查看的时候都比较麻烦.在 .net 7 中,你可以使用一个文件来集中管理你的 Nuget 依赖版 ...
- K8S Pod Sidecar 应用场景之一-加入 NGINX Sidecar 做反代和 web 服务器
Kubernetes Pod Sidecar 简介 Sidecar 是一个独立的容器,与 Kubernetes pod 中的应用容器一起运行,是一种辅助性的应用. Sidecar 的常见辅助性功能有这 ...