CH579M实现键盘输入
作者:互联网
在官方例程的基础上增加一个发送函数KeyHIDKeyreport即可,具体的代码如下:
CH579M HID示例代码
/********************************** (C) COPYRIGHT *******************************
* File Name : Main.c
* Author : WCH
* Version : V1.0
* Date : 2020/02/20
* Description : 模拟USB复合设备,键鼠,支持类命令
*******************************************************************************/
#include "CH57x_common.h"
#define DevEP0SIZE 0x40
// 设备描述符
const UINT8 MyDevDescr[] = {
0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, DevEP0SIZE,
0x3d, 0x41, 0x07, 0x21, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01
};
// 配置描述符
const UINT8 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 //端点描述符
};
// 语言描述符
const UINT8 MyLangDescr[] = { 0x04, 0x03, 0x09, 0x04 };
// 厂家信息
const UINT8 MyManuInfo[] = { 0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0 };
// 产品信息
const UINT8 MyProdInfo[] = { 0x0C, 0x03, 'C', 0, 'H', 0, '5', 0, '7', 0, 'x', 0 };
/*HID类报表描述符*/
const UINT8 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 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 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)) // 分析操作令牌和端点号
{
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;
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;
}
//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_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;
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) // 错误或不支持
{
// SetupReqCode = 0xFF;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; // STALL
} else {
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
}
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:
len = R8_USB_RX_LEN;
if (SetupReqCode == 0x09) {
if (pEP0_DataBuf[0]) {
printf("Light on Num Lock LED!\n");
} else if (pEP0_DataBuf[0] == 0) {
printf("Light off Num Lock LED!\n");
}
}
break;
case UIS_TOKEN_OUT | 1:
if (R8_USB_INT_ST & RB_UIS_TOG_OK) { // 不同步的数据包将丢弃
len = R8_USB_RX_LEN;
DevEP1_OUT_Deal(len);
}
break;
case UIS_TOKEN_IN | 1:
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) { // 不同步的数据包将丢弃
len = R8_USB_RX_LEN;
DevEP2_OUT_Deal(len);
}
break;
case UIS_TOKEN_IN | 2:
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) { // 不同步的数据包将丢弃
len = R8_USB_RX_LEN;
DevEP3_OUT_Deal(len);
}
break;
case UIS_TOKEN_IN | 3:
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;
} 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 | RB_UEP_AUTO_TOG;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
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;
}
}
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();
}
void DevHIDKeyReport(uint8_t key)
{
HIDKey[2] = key;
memcpy(pEP1_IN_DataBuf, HIDKey, sizeof(HIDKey));
DevEP1_IN_Deal(sizeof(HIDKey));
}
int main()
{
UINT8 i=0;
SetSysClock(CLK_SOURCE_HSE_32MHz);
PWR_UnitModCfg(ENABLE, UNIT_SYS_PLL); // 打开PLL
DelayMs(5);
DebugInit();
printf("start\n");
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
pEP3_RAM_Addr = EP3_Databuf;
USB_DeviceInit();
NVIC_EnableIRQ(USB_IRQn);
while (1) {
mDelaymS(1000);
mDelaymS(1000);
mDelaymS(1000);
for(i=0;i<4;i++)
{
mDelaymS(50);
DevHIDKeyReport(0x04+i);
mDelaymS(10);
DevHIDKeyReport(0x00);
mDelaymS(50);
DevHIDKeyReport(0x1E+i);
mDelaymS(10);
DevHIDKeyReport(0x00);
mDelaymS(50);
}
}
}
void DevEP1_OUT_Deal(UINT8 l)
{ /* 用户可自定义 */
UINT8 i;
for (i = 0; i < l; i++) {
pEP1_IN_DataBuf[i] = ~pEP1_OUT_DataBuf[i];
}
DevEP1_IN_Deal(l);
}
void DevEP2_OUT_Deal(UINT8 l)
{ /* 用户可自定义 */
UINT8 i;
for (i = 0; i < l; i++) {
pEP2_IN_DataBuf[i] = ~pEP2_OUT_DataBuf[i];
}
DevEP2_IN_Deal(l);
}
void DevEP3_OUT_Deal(UINT8 l)
{ /* 用户可自定义 */
UINT8 i;
for (i = 0; i < l; i++) {
pEP3_IN_DataBuf[i] = ~pEP3_OUT_DataBuf[i];
}
DevEP3_IN_Deal(l);
}
void DevEP4_OUT_Deal(UINT8 l)
{ /* 用户可自定义 */
UINT8 i;
for (i = 0; i < l; i++) {
pEP4_IN_DataBuf[i] = ~pEP4_OUT_DataBuf[i];
}
DevEP4_IN_Deal(l);
}
void USB_IRQHandler(void) /* USB中断服务程序,使用寄存器组1 */
{
USB_DevTransProcess();
}
在上面代码的基础上,可以自己定义输入的内容。需要用到一个HID键值表——
USB_HID_UsageTables下载地址:https://usb.org/sites/default/files/hut1_3_0.pdf
上述地址是官方的下载地址,我为国内诸多平台将国外免费的资料据为己有同时要求客户付费下载的行为感到羞耻。
下面是我自己常用的几个键值,已经高亮处理。
标签:USB,实现,RES,R8,键盘输入,break,len,CH579M,UEP 来源: https://www.cnblogs.com/realiot/p/16313609.html