官方默认还没处理OUT,即键盘描述符,主机往从机方向的包,这里也没处理,但是这里对IN实现了,OUT其实也很简单,CH579怎么说呢,就是非常底层的USB控制逻辑,基本一切都要自己控制,不过这样对于初学者是非常好,但是对于已经熟悉一个框架的人来说,工作量就增加了.
#include "CH57x_common.h"
#define DevEP0SIZE 0x40
// 语言描述符
const UINT8 LanguageDescriptor[] = {
0x04, // bLength
0x03, // bDescriptorType (字符串描述符)
0x09, 0x04 // wLANGID (英语/美国)
};
// 厂家信息
const UINT8 ManufacturerDescriptor[] = {
0x0E, // bLength
0x03, // bDescriptorType (字符串描述符)
'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0 // WCH
};
// 产品信息
const UINT8 ProductDescriptor[] = {
0x0C, // bLength
0x03, // bDescriptorType (字符串描述符)
'C', 0, 'H', 0, '5', 0, '7', 0, 'x', 0 // CH57X
};
// 设备描述符
const UINT8 DeviceDescriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB (USB Spec 1.1)
0x00, // bDeviceClass (Class info in Ifc Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
DevEP0SIZE, // bMaxPacketSize0
0x3d, 0x41, // idVendor
0x07, 0x21, // idProduct
0x00, 0x00, // bcdDevice
0x00, // iManufacturer
0x00, // iProduct
0x00, // iSerialNumber
0x01 // bNumConfigurations
};
// 描述符集合
const UINT8 MyCfgDescr[] = {
// 配置描述符
0x09, // bLength
0x02, // bDescriptorType (配置描述符)
0x3b, 0x00, // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xA0, // bmAttributes
0x32, // bMaxPower (100mA)
// 接口描述符
0x09, // bLength
0x04, // bDescriptorType (接口描述符)
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x03, // bInterfaceClass (HID)
0x01, // bInterfaceSubClass (Boot Interface)
0x01, // bInterfaceProtocol (键盘)
0x00, // iInterface
// HID类描述符
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID (v1.11)
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType (Report)
0x40, 0x00, // wDescriptorLength
// 端点描述符
0x07, // bLength
0x05, // Endpoint
0x81, // bDescriptorType (端点描述符)
0x03, // bmAttributes (中断传输)
0x08, 0x00, // wMaxPacketSize (8字节)
0x0a, // bInterval (10ms)
// 接口描述符
0x09, // bLength
0x04, // bDescriptorType (接口描述符)
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x03, // bInterfaceClass (HID)
0x01, // bInterfaceSubClass (Boot Interface)
0x02, // bInterfaceProtocol (鼠标)
0x00, // iInterface
// HID类描述符
0x09, // bLength
0x21, // bDescriptorType (HID)
0x10, 0x01, // bcdHID (v1.10)
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType (Report)
0x34, 0x00, // wDescriptorLength
// 端点描述符
0x07, // bLength
0x05, // Endpoint
0x82, // bDescriptorType (端点描述符)
0x03, // bmAttributes (中断传输)
0x04, 0x00, // wMaxPacketSize (4字节)
0x0a // bInterval (10ms)
};
/*HID类报表描述符*/
const UINT8 KeyboardReportDescriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x03, // USAGE_MAXIMUM (Scroll Lock)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x91, 0x01, // OUTPUT (Cnst,Ary,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x91, // USAGE_MAXIMUM (Keyboard LANG2)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
const UINT8 MouseReportDescriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
// USB 通信过程变量
UINT8 DevConfig, Ready;
UINT8 SetupReqCode;
UINT16 SetupReqLen;
const UINT8 *pDescr;
/*鼠标键盘数据*/
UINT8 HIDMouse[4] = {0x0, 0x0, 0x0, 0x0};
UINT8 HIDKey[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
// 用户自定义分配端点RAM
__align(4) UINT8 EP0_Databuf[64 + 64 + 64]; // ep0(64) + ep4_out(64) + ep4_in(64)
__align(4) UINT8 EP1_Databuf[64 + 64]; // ep1_out(64) + ep1_in(64)
__align(4) UINT8 EP2_Databuf[64 + 64]; // ep2_out(64) + ep2_in(64)
__align(4) UINT8 EP3_Databuf[64 + 64]; // ep3_out(64) + ep3_in(64)
void USB_DevTransProcess(void)
{
UINT8 len, chtype;
UINT8 intflag, errflag = 0;
intflag = R8_USB_INT_FG;
if (intflag & RB_UIF_TRANSFER)
{
// 分析操作令牌和端点号
switch (R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
{
// SETUP 包
case UIS_TOKEN_SETUP:
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
// 收到了多长的包?
len = R8_USB_RX_LEN;
// 这个包是不是SETUP包?通过长度判定.
if (len == sizeof(USB_SETUP_REQ))
{
SetupReqLen = pSetupReqPak->wLength;
SetupReqCode = pSetupReqPak->bRequest;
chtype = pSetupReqPak->bRequestType;
len = 0;
errflag = 0;
if ((pSetupReqPak->bRequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD) /* 非标准请求 */
{
switch (SetupReqCode)
{
case 0x0a:
break; // 忽略 (不能作为错误)
case 0x09:
break; // 忽略 (不能作为错误)
default:
errflag = 0xFF;
}
}
else /* 标准请求 */
{
switch (SetupReqCode)
{
case USB_GET_DESCRIPTOR:
{
// 获取描述符(根据不同描述符返回字符串)
switch (((pSetupReqPak->wValue) >> 8))
{
case USB_DESCR_TYP_DEVICE:
pDescr = DeviceDescriptor;
len = DeviceDescriptor[0];
break;
case USB_DESCR_TYP_CONFIG:
pDescr = MyCfgDescr;
len = MyCfgDescr[2];
break;
case USB_DESCR_TYP_REPORT:
// 由于实现了多个接口,所以要判断读取的是哪个接口?
if (((pSetupReqPak->wIndex) & 0xff) == 0)
{
pDescr = KeyboardReportDescriptor;
len = sizeof(KeyboardReportDescriptor);
}
else if (((pSetupReqPak->wIndex) & 0xff) == 1)
{
pDescr = MouseReportDescriptor;
len = sizeof(MouseReportDescriptor);
// 如果最后一个接口数据都取走了,说明USB枚举过程是全部完成了.
Ready = 1;
}
else
{
len = 0xff; // 本程序只有2个接口,这句话正常不可能执行.
}
break;
case USB_DESCR_TYP_STRING:
switch ((pSetupReqPak->wValue) & 0xff)
{
case 1:
pDescr = ManufacturerDescriptor;
len = ManufacturerDescriptor[0];
break;
case 2:
pDescr = ProductDescriptor;
len = ProductDescriptor[0];
break;
case 0:
pDescr = LanguageDescriptor;
len = LanguageDescriptor[0];
break;
default:
errflag = 0xFF; // 不支持的字符串描述符
break;
}
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 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
errflag = 0xFF;
break;
case USB_GET_INTERFACE:
pEP0_DataBuf[0] = 0x00;
if (SetupReqLen > 1)
SetupReqLen = 1;
break;
case USB_GET_STATUS:
pEP0_DataBuf[0] = 0x00;
pEP0_DataBuf[1] = 0x00;
if (SetupReqLen > 2)
SetupReqLen = 2;
break;
default:
errflag = 0xff;
break;
}
}
}
else
errflag = 0xff;
if (errflag == 0xff) // 如果设置了错误标志
{
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; // 挂起设备,不要继续!
}
else
{
if (chtype & 0x80) // 上传数据
{
// 发送了多少就减少多少,方便下次继续发送(比如包大于EP)!
len = (SetupReqLen > DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
SetupReqLen -= len;
}
else
len = 0; // 下传数据
R8_UEP0_T_LEN = len;
// 默认数据包是DATA1
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
}
break;
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;
default:
R8_UEP0_T_LEN = 0; // 状态阶段完成中断或者是强制上传0长度数据包结束控制传输
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
break;
case UIS_TOKEN_OUT:
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;
}
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;
}
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;
}
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;
}
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;
}
else if (intflag & RB_UIF_BUS_RST)
{
// USB 总线复位
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)
{
// 挂起
__nop();
}
else
{
// 唤醒
__nop();
}
R8_USB_INT_FG = RB_UIF_SUSPEND;
}
else
{
R8_USB_INT_FG = intflag;
}
}
void KeyboardOut(void)
{
pEP1_IN_DataBuf[0] = 0; // L-Ctrl / L-Shift / L-Alt / L-GUI / R-Ctrl / R-Shift / R-Alt / R-GUI
pEP1_IN_DataBuf[1] = 0; // 无用数据 (填充为0)
pEP1_IN_DataBuf[2] = 0x46; // 按键数据0 (0x46 = PrintScreen)
pEP1_IN_DataBuf[3] = 0; // 按键数据1
pEP1_IN_DataBuf[4] = 0; // 按键数据2
pEP1_IN_DataBuf[5] = 0; // 按键数据3
pEP1_IN_DataBuf[6] = 0; // 按键数据4
pEP1_IN_DataBuf[7] = 0; // 按键数据5
DevEP1_IN_Deal(8);
}
void MouseOut(void)
{
pEP2_IN_DataBuf[0] = 0; // Btn 1 / Btn 2 / Btn 3
pEP2_IN_DataBuf[1] = 1; // X轴
pEP2_IN_DataBuf[2] = 0; // Y轴
pEP2_IN_DataBuf[3] = 0; // 滚轮
DevEP2_IN_Deal(4);
}
void USB_IRQHandler(void) /* USB中断服务程序,使用寄存器组1 */
{
USB_DevTransProcess();
}
int main()
{
// CPU 速度 (实测要跑USB,就不能PLL到40MHz)
SetSysClock(CLK_SOURCE_HSE_32MHz);
// 打开PLL (USB依赖PLL)
PWR_UnitModCfg(ENABLE, UNIT_SYS_PLL);
// CPU死等5毫秒(估算值),使得PLL完全打开.
DelayMs(5);
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
pEP3_RAM_Addr = EP3_Databuf;
// 初始化USB寄存器,4个端点,8个通道.
USB_DeviceInit();
NVIC_EnableIRQ(USB_IRQn);
while (1)
{
if (Ready == 1)
{
KeyboardOut();
MouseOut();
}
DelayMs(1000);
}
}