3

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

 1 year ago
source link: https://www.taterli.com/9235/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK