WCH CH579 USB键鼠例子完美解释版

/ 0评 / 1

官方默认还没处理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);
	}
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注