USB-HID是Universal Serial Bus-Human Interface Device的縮寫,由其名稱可以了解HID設(shè)備是直接與人交互的設(shè)備,例如鍵盤、鼠標(biāo)與游戲桿等,也是PC上為數(shù)不多的不需要額外驅(qū)動(dòng)的協(xié)議棧,所以diy一些小設(shè)備非常方便。
之前一直在用STM32芯片,ST已經(jīng)提供了一套完備的USB協(xié)議棧,只要定義好設(shè)備配置符就可以用了,看了下CH32V10x系列的資料,里面只有通過USB模擬CH372設(shè)備的例程。于是借著比賽的機(jī)會(huì),正好把USB的HID協(xié)議學(xué)習(xí)一下。
對(duì)USB感興趣的小伙伴,推薦看下《圈圈教你玩USB》,講的還是很細(xì)致的,雖然是多年前的書了,但畢竟USB也還是原來的那套,本文就不詳細(xì)介紹USB協(xié)議棧了。
移植過程是比較長(zhǎng)的,如果你也是用了CH32V103,并且想要實(shí)現(xiàn)鍵盤鼠標(biāo)功能的話,可以直接參考我共享的代碼:CH32V103-USB-HID-KeyboradMouse: CH32V103 usb HID 的鍵盤鼠標(biāo)庫 (gitee.com)
這里已經(jīng)寫好了針對(duì)鍵盤/鼠標(biāo)/鍵盤鼠標(biāo)混合的功能實(shí)現(xiàn),并參考了arduino的API,實(shí)現(xiàn)鍵盤的按下按鍵、釋放按鍵、釋放所有按鍵、輸入字符等功能,實(shí)現(xiàn)鼠標(biāo)的點(diǎn)擊、移動(dòng)等功能,可以參考arduino的Keyboard和Mouse的使用方法,當(dāng)然這里是C實(shí)現(xiàn)的,所以函數(shù)接口名是做了修改了哈。
使用方法很簡(jiǎn)單,只要進(jìn)行鍵盤/鼠標(biāo)/鍵盤鼠標(biāo)混合的初始化就可以使用了,具體的方法看代碼中的描述。
/*三選一初始化即可*/
USB_HID_Init(HID_KEYBOARD);
USB_HID_Init(HID_MOUSE);
USB_HID_Init(HID_KEYBOARD_AND_MOUSE);
接下來是移植過程:
CH32V103的標(biāo)準(zhǔn)庫中有USB的相關(guān)底層驅(qū)動(dòng),見ch32v10x_usb.c和ch32v10x_usb.h,其中定義了USB協(xié)議棧需要的各種配置符結(jié)構(gòu),我們只要按照需要定義就好了。但定義好了配置,該如何與主機(jī)端通訊呢?可以參考USB模擬CH372設(shè)備的例程,USB通訊都是通過USB中斷函數(shù)中的狀態(tài)機(jī)實(shí)現(xiàn)的。所以只要定義好配置描述符,并按需要修改中斷函數(shù),理論上就完成了移植工作。
1、USB設(shè)備描述符,這里的最大包長(zhǎng)度就按照CH32V103的EndPoint0的緩沖區(qū)大小來設(shè)置了,廠家ID和產(chǎn)品ID都自定義,后面的字符串索引號(hào)是和字符串描述符相關(guān)聯(lián)的,所以要對(duì)應(yīng)上。然后配置只有一種即可。
static const USB_DEV_DESCR USBDevDescr = {
.bLength = 18, /*bLength:長(zhǎng)度,設(shè)備描述符的長(zhǎng)度為18字節(jié)*/
.bDescriptorType = 0x01, /*bDescriptorType:類型,設(shè)備描述符的編號(hào)是0x01*/
.bcdUSB = 0x0200, /*bcdUSB:所使用的USB版本為2.0*/
.bDeviceClass = 0x00, /*bDeviceClass:設(shè)備所使用的類代碼*/
.bDeviceSubClass = 0x00, /*bDeviceSubClass:設(shè)備所使用的子類代碼*/
.bDeviceProtocol = 0x00, /*bDeviceSubClass:設(shè)備所使用的子類代碼*/
.bMaxPacketSize0 = DevEP0SIZE, /*bMaxPacketSize:最大包長(zhǎng)度為64字節(jié)*/
.idVendor = USBD_VID, /*idVendor:廠商ID*/
.idProduct = USBD_PID_FS, /*idProduct:產(chǎn)品ID*/
.bcdDevice = 0x0200, /*bcdDevice:設(shè)備的版本號(hào)為2.00*/
.iManufacturer = 0x01, /*iManufacturer:廠商字符串的索引*/
.iProduct = 0x02, /*iProduct:產(chǎn)品字符串的索引*/
.iSerialNumber = 0x03, /*iSerialNumber:設(shè)備的序列號(hào)字符串索引*/
.bNumConfigurations = 0x01 /*bNumConfiguration:設(shè)備有1種配置*/
};
2、USB配置描述符,下面分別是鍵盤/鼠標(biāo)/鍵盤鼠標(biāo)混合的配置符代碼,不同的差別就在配置描述符的總長(zhǎng)度和所支持的接口數(shù)量。
static const USB_CFG_DESCR USBCfgDescr_KB = {
.bLength = 0x09, /*bLength:長(zhǎng)度,設(shè)備字符串的長(zhǎng)度為9字節(jié)*/
.bDescriptorType = 0x02, /*bDescriptorType:類型,配置描述符的類型編號(hào)為0x2*/
.wTotalLength = USB_CFG_DESCR_LEN_KB, /*wTotalLength:配置描述符的總長(zhǎng)度為50字節(jié)*/
.bNumInterfaces = 0x01, /*bNumInterfaces:配置所支持的接口數(shù)量1個(gè)*/
.bConfigurationValue = 0x01, /*bConfigurationValue:該配置的值*/
.iConfiguration = 0x00, /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
.bmAttributes = 0xA0, /* bmAttributes:設(shè)備的一些特性,0xA0表示不自供電,支持遠(yuǎn)程喚醒
D7:保留必須為1
D6:是否自供電
D5:是否支持遠(yuǎn)程喚醒
D4~D0:保留設(shè)置為0
*/
.MaxPower = 0x32 /*從總線上獲得的最大電流為100mA */
};
static const USB_CFG_DESCR USBCfgDescr_M = {
.bLength = 0x09, /*bLength:長(zhǎng)度,設(shè)備字符串的長(zhǎng)度為9字節(jié)*/
.bDescriptorType = 0x02, /*bDescriptorType:類型,配置描述符的類型編號(hào)為0x2*/
.wTotalLength = USB_CFG_DESCR_LEN_M, /*wTotalLength:配置描述符的總長(zhǎng)度為41字節(jié)*/
.bNumInterfaces = 0x01, /*bNumInterfaces:配置所支持的接口數(shù)量1個(gè)*/
.bConfigurationValue = 0x01, /*bConfigurationValue:該配置的值*/
.iConfiguration = 0x00, /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
.bmAttributes = 0xA0, /* bmAttributes:設(shè)備的一些特性,0xA0表示不自供電,支持遠(yuǎn)程喚醒
D7:保留必須為1
D6:是否自供電
D5:是否支持遠(yuǎn)程喚醒
D4~D0:保留設(shè)置為0
*/
.MaxPower = 0x32 /*從總線上獲得的最大電流為100mA */
};
static const USB_CFG_DESCR USBCfgDescr_KBM = {
.bLength = 0x09, /*bLength:長(zhǎng)度,設(shè)備字符串的長(zhǎng)度為9字節(jié)*/
.bDescriptorType = 0x02, /*bDescriptorType:類型,配置描述符的類型編號(hào)為0x2*/
.wTotalLength = USB_CFG_DESCR_LEN_KBM, /*wTotalLength:配置描述符的總長(zhǎng)度為66字節(jié)*/
.bNumInterfaces = 0x02, /*bNumInterfaces:配置所支持的接口數(shù)量2個(gè)*/
.bConfigurationValue = 0x01, /*bConfigurationValue:該配置的值*/
.iConfiguration = 0x00, /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
.bmAttributes = 0xA0, /* bmAttributes:設(shè)備的一些特性,0xA0表示不自供電,支持遠(yuǎn)程喚醒
D7:保留必須為1
D6:是否自供電
D5:是否支持遠(yuǎn)程喚醒
D4~D0:保留設(shè)置為0
*/
.MaxPower = 0x32 /*從總線上獲得的最大電流為100mA */
};
3、設(shè)備描述符,設(shè)備描述符中包括了接口描述符/HID描述符/端點(diǎn)描述符,因此這里構(gòu)建了個(gè)結(jié)構(gòu)體,方便一起發(fā)送給主機(jī)端,也是分為三種:鍵盤/鼠標(biāo)/鍵盤和鼠標(biāo),其實(shí)就是不同的接口描述符/HID描述符/端點(diǎn)描述符的組合方式。
typedef struct __PACKED {
USB_CFG_DESCR cfg_descr;
USB_ITF_DESCR itf_descr;
USB_HID_DESCR hid_descr;
USB_ENDP_DESCR inendp_descr;
USB_ENDP_DESCR outendp_descr;
} USB_CFG_DESCR_KEYBOARD;
typedef struct __PACKED {
USB_CFG_DESCR cfg_descr;
USB_ITF_DESCR itf_descr;
USB_HID_DESCR hid_descr;
USB_ENDP_DESCR inendp_descr;
} USB_CFG_DESCR_MOUSE;
typedef struct __PACKED {
USB_CFG_DESCR cfg_descr;
USB_ITF_DESCR itf1_descr;
USB_HID_DESCR hid1_descr;
USB_ENDP_DESCR inendp1_descr;
USB_ENDP_DESCR outendp1_descr;
USB_ITF_DESCR itf2_descr;
USB_HID_DESCR hid2_descr;
USB_ENDP_DESCR inendp2_descr;
} USB_CFG_DESCR_KEYBOARD_AND_MOUSE;
static const USB_CFG_DESCR_KEYBOARD CfgDescr_keyboard = {
.cfg_descr = USBCfgDescr_KB,
.itf_descr = KeyBoardItfDescr,
.hid_descr = KeyBoardHIDDescr,
.inendp_descr = KeyBoardINEndpDescr,
.outendp_descr = KeyBoardOUTEndpDescr,
};
static const USB_CFG_DESCR_MOUSE CfgDescr_Mouse = {
.cfg_descr = USBCfgDescr_M,
.itf_descr = MouseItfDescr,
.hid_descr = MouseHIDDescr,
.inendp_descr = MouseEndpDescr,
};
static const USB_CFG_DESCR_KEYBOARD_AND_MOUSE CfgDescr_keyboardAndMouse = {
.cfg_descr = USBCfgDescr_KBM,
.itf1_descr = KeyBoardItfDescr,
.hid1_descr = KeyBoardHIDDescr,
.inendp1_descr = KeyBoardINEndpDescr,
.outendp1_descr = KeyBoardOUTEndpDescr,
.itf2_descr = MouseItfDescr,
.hid2_descr = MouseHIDDescr,
.inendp2_descr = MouseEndpDescr,
};
4、鍵盤的接口描述符,HID描述符,和兩個(gè)端點(diǎn)的描述符,這里我創(chuàng)建了兩個(gè)端點(diǎn)描述符,一個(gè)用于發(fā)送按鍵值,另一個(gè)用于接收主機(jī)端的狀態(tài)值,比如大寫鎖定燈,小鍵盤燈之類的
static const USB_ITF_DESCR KeyBoardItfDescr = {
.bLength = 0x09, /*bLength:長(zhǎng)度,設(shè)備字符串的長(zhǎng)度為9字節(jié)*/
.bDescriptorType = 0x04, /*bDescriptorType:接口描述符的類型為0x4 */
.bInterfaceNumber = 0x00, /*bInterfaceNumber:該接口的編號(hào)*/
.bAlternateSetting = 0x00, /*bAlternateSetting:該接口的備用編號(hào) */
.bNumEndpoints = 0x02, /*bInterfaceNumber:該接口的編號(hào)*/
.bInterfaceClass = 0x03, /*bInterfaceClass該接口所使用的類為HID*/
.bInterfaceSubClass = 0x01, /*bInterfaceSubClass:該接口所用的子類 1=BOOT, 0=no boot */
.bInterfaceProtocol = 0x01, /*nInterfaceProtocol :該接口使用的協(xié)議0=none, 1=keyboard, 2=mouse */
.iInterface = 0x00 /*iInterface: 該接口字符串的索引 */
};
static const USB_HID_DESCR KeyBoardHIDDescr = {
.bLength = 0x09, /*bLength: HID描述符的長(zhǎng)度為9字節(jié) */
.bDescriptorType = 0x21, /*bDescriptorType: HID的描述符類型為0x21 */
.bcdHID = 0x0111, /*bcdHID: HID協(xié)議的版本為1.1 */
.bCountryCode = 0x00, /*bCountryCode: 國(guó)家代號(hào) */
.bNumDescriptors = 0x01, /*bNumDescriptors: 下級(jí)描述符的數(shù)量*/
.bDescriptorTypeX = 0x22, /*bDescriptorType:下級(jí)描述符的類型*/
.wDescriptorLengthL = sizeof(KeyboardRepDescr)&0xFF, /*wItemLength: 下級(jí)描述符的長(zhǎng)度*/
.wDescriptorLengthH = 0x00,
};
static const USB_ENDP_DESCR KeyBoardINEndpDescr = {
.bLength = 0x07,
.bDescriptorType = 0x05,
.bEndpointAddress = 0x81, /* bEndpointAddress: 該端點(diǎn)(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點(diǎn)號(hào)*/
.bmAttributes = 0x03, /* bmAttributes: 端點(diǎn)的屬性為為中斷端點(diǎn).
D0~D1表示傳輸類型:0(控制傳輸),1(等時(shí)傳輸),2(批量傳輸),3(中斷傳輸)
非等時(shí)傳輸端點(diǎn):D2~D7:保留為0
等時(shí)傳輸端點(diǎn):
D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
D4~D5表示用途:0(數(shù)據(jù)端點(diǎn)),1(反饋端點(diǎn)),2(暗含反饋的數(shù)據(jù)端點(diǎn)),3(保留)
D6~D7:保留,
*/
.wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點(diǎn)支持的最大包長(zhǎng)度為DevEP0SIZE字節(jié)*/
.bInterval = 0x0A, /* bInterval: 輪詢間隔(10ms) */
};
static const USB_ENDP_DESCR KeyBoardOUTEndpDescr = {
.bLength = 0x07,
.bDescriptorType = 0x05,
.bEndpointAddress = 0x01, /* bEndpointAddress: 該端點(diǎn)(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點(diǎn)號(hào)*/
.bmAttributes = 0x03, /* bmAttributes: 端點(diǎn)的屬性為為中斷端點(diǎn).
D0~D1表示傳輸類型:0(控制傳輸),1(等時(shí)傳輸),2(批量傳輸),3(中斷傳輸)
非等時(shí)傳輸端點(diǎn):D2~D7:保留為0
等時(shí)傳輸端點(diǎn):
D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
D4~D5表示用途:0(數(shù)據(jù)端點(diǎn)),1(反饋端點(diǎn)),2(暗含反饋的數(shù)據(jù)端點(diǎn)),3(保留)
D6~D7:保留,
*/
.wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點(diǎn)支持的最大包長(zhǎng)度為DevEP0SIZE字節(jié)*/
.bInterval = 0x0A, /* bInterval: 輪詢間隔(10ms) */
};
5、鼠標(biāo)的接口描述符,HID描述符,和端點(diǎn)描述符
static const USB_ITF_DESCR MouseItfDescr = {
.bLength = 0x09, /*bLength:長(zhǎng)度,設(shè)備字符串的長(zhǎng)度為9字節(jié)*/
.bDescriptorType = 0x04, /*bDescriptorType:接口描述符的類型為0x4 */
.bInterfaceNumber = 0x01, /*bInterfaceNumber:該接口的編號(hào)*/
.bAlternateSetting = 0x00, /*bAlternateSetting:該接口的備用編號(hào) */
.bNumEndpoints = 0x01, /*bInterfaceNumber:該接口的編號(hào)*/
.bInterfaceClass = 0x03, /*bInterfaceClass該接口所使用的類為HID*/
.bInterfaceSubClass = 0x01, /*bInterfaceSubClass:該接口所用的子類 1=BOOT, 0=no boot */
.bInterfaceProtocol = 0x02, /*nInterfaceProtocol :該接口使用的協(xié)議0=none, 1=keyboard, 2=mouse */
.iInterface = 0x00 /*iInterface: 該接口字符串的索引 */
};
static const USB_HID_DESCR MouseHIDDescr = {
.bLength = 0x09, /*bLength: HID描述符的長(zhǎng)度為9字節(jié) */
.bDescriptorType = 0x21, /*bDescriptorType: HID的描述符類型為0x21 */
.bcdHID = 0x0111, /*bcdHID: HID協(xié)議的版本為1.1 */
.bCountryCode = 0x00, /*bCountryCode: 國(guó)家代號(hào) */
.bNumDescriptors = 0x01, /*bNumDescriptors: 下級(jí)描述符的數(shù)量*/
.bDescriptorTypeX = 0x22, /*bDescriptorType:下級(jí)描述符的類型*/
.wDescriptorLengthL = sizeof(MouseRepDescr)&0xFF, /*wItemLength: 下級(jí)描述符的長(zhǎng)度*/
.wDescriptorLengthH = 0x00,
};
static const USB_ENDP_DESCR MouseEndpDescr = {
.bLength = 0x07,
.bDescriptorType = 0x05,
.bEndpointAddress = 0x82, /* bEndpointAddress: 該端點(diǎn)(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點(diǎn)號(hào)*/
.bmAttributes = 0x03, /* bmAttributes: 端點(diǎn)的屬性為為中斷端點(diǎn).
D0~D1表示傳輸類型:0(控制傳輸),1(等時(shí)傳輸),2(批量傳輸),3(中斷傳輸)
非等時(shí)傳輸端點(diǎn):D2~D7:保留為0
等時(shí)傳輸端點(diǎn):
D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
D4~D5表示用途:0(數(shù)據(jù)端點(diǎn)),1(反饋端點(diǎn)),2(暗含反饋的數(shù)據(jù)端點(diǎn)),3(保留)
D6~D7:保留,
*/
.wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點(diǎn)支持的最大包長(zhǎng)度為DevEP0SIZE字節(jié)*/
.bInterval = 0x0A, /* bInterval: 輪詢間隔(10ms) */
};
6、上述代碼中的宏定義值
/* Global define */
#define DevEP0SIZE 0x40 // Max 64 bypes
#define USBD_VID 0x026D
#define USBD_PID_FS 0x24CD
#define USB_CFG_DESCR_LEN_KB 50 //ONLY KEYBOARD
#define USB_CFG_DESCR_LEN_M 41 //ONLY MOUSE
#define USB_CFG_DESCR_LEN_KBM 66 //ONLY KEYBOARD AND MOUSE
7、在USB設(shè)備描述符中所提到的字符串描述符,這部分可以通過USB字符串描述符生成器來得到
/* Language Descriptor */
static const UINT8 MyLangDescr[] = {
0x04, /*bLength:本描述符的長(zhǎng)度為4字節(jié)*/
0x03, /*bDescriptorType:字符串描述符的類型為0x03*/
0x09, /*bString:語言ID為0x0409,表示美式英語*/
0x04
};
/* Manufactor Descriptor */
static const UINT8 MyManuInfo[] = {
0x1E, /*bLength:廠商字符串描述符的長(zhǎng)度*/
0x03, /*bDescriptorType:字符串描述符的類型為0x03*/
/*ZealerluStudio*/
0x5A,0x00,0x65,0x00,0x61,0x00,0x6C,0x00,0x65,0x00,0x72,0x00,0x6C,0x00,
0x75,0x00,0x53,0x00,0x74,0x00,0x75,0x00,0x64,0x00,0x69,0x00,0x6F,0x00
};
/* Product Information */
static const UINT8 MyProdInfo[] = {
0x1A, /* bLength:產(chǎn)品的字符串描述符*/
0x03, /* bDescriptorType:字符串描述符的類型為0x03*/
/*Mult-PushRod*/
0x4D,0x00,0x75,0x00,0x6C,0x00,0x74,0x00,0x2D,0x00,0x50,0x00,0x75,0x00,
0x73,0x00,0x68,0x00,0x52,0x00,0x6F,0x00,0x64,0x00
};
/* Product ID Information */
static const UINT8 MyProdIDInfo[] = {
0x26, /* bLength:產(chǎn)品的字符串描述符*/
0x03, /* bDescriptorType:字符串描述符的類型為0x03*/
/*RT-Thread & Risc-V*/
0x52,0x00,0x54,0x00,0x2D,0x00,0x54,0x00,0x68,0x00,0x72,0x00,0x65,0x00,
0x61,0x00,0x64,0x00,0x20,0x00,0x26,0x00,0x20,0x00,0x52,0x00,0x69,0x00,0x73,0x00,
0x63,0x00,0x2D,0x00,0x56,0x00
};
9、鍵盤除了上述配置類的描述符,還需要一個(gè)報(bào)告描述符來描述通訊數(shù)據(jù)的格式
/* HID的報(bào)告描述符*/
/* 定義了8字節(jié)發(fā)送:
** 第一字節(jié)表示特殊件是否按下:D0:Ctrl D1:Shift D2:Alt
** 第二字節(jié)保留,值為0
** 第三~第八字節(jié):普通鍵鍵值的數(shù)組,最多能同時(shí)按下6個(gè)鍵
** 定義了1字節(jié)接收:對(duì)應(yīng)鍵盤上的LED燈,這里只用了兩個(gè)位。
** D0:Num Lock D1:Cap Lock D2:Scroll Lock D3:Compose D4:Kana
** */
static const uint8_t KeyboardRepDescr[] =
{
/*short Item D7~D4:bTag;D3~D2:bType;D1~D0:bSize
**bTag ---主條目 1000:輸入(Input) 1001:輸出(Output) 1011:特性(Feature) 1010:集合(Collection) 1100:關(guān)集合(End Collection)
** 全局條目 0000:用途頁(Usage Page) 0001:邏輯最小值(Logical Minimum) 0010:邏輯最大值(Logical Maximum) 0011:物理最小值(Physical Minimum)
** 0100:物理最大值(Physical Maximum) 0101:單元指數(shù)(Unit Exponet) 0110:單元(Unit) 0111:數(shù)據(jù)域大小(Report Size)
** 1000:報(bào)告ID(Report ID) 1001:數(shù)據(jù)域數(shù)量(Report Count) 1010:壓棧(Push) 1011:出棧(Pop) 1100~1111:保留(Reserved)
** 局部條目 0000:用途(Usage) 0001:用途最小值(Usage Minimum) 0010:用途最大值(Usage Maximum) 0011:標(biāo)識(shí)符索引(Designator Index)
** 0100:標(biāo)識(shí)符最小值(Designator Minimum) 0101:標(biāo)識(shí)符最大值(Designator Maximum) 0111:字符串索引(String Index) 1000:字符串最小值(String Minimum)
** 1001:字符串最大值(String Maximum) 1010:分隔符(Delimiter) 其他:保留(Reserved)
**bType --- 00:主條目(main) 01:全局條目(globle) 10:局部條目(local) 11:保留(reserved)
**bSize --- 00:0字節(jié) 01:1字節(jié) 10:2字節(jié) 11:4字節(jié)*/
//0x05:0000 01 01 這是個(gè)全局條目,用途頁選擇為普通桌面頁
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
//0x09:0000 10 01 這是個(gè)全局條目,用途選擇為鍵盤
0x09, 0x06, // USAGE (Keyboard)
//0xa1:1010 00 01 這是個(gè)主條目,選擇為應(yīng)用集合,
0xa1, 0x01, // COLLECTION (Application)
//0x05:0000 01 11 這是個(gè)全局條目,用途頁選擇為鍵盤/按鍵
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
//0x19:0001 10 01 這是個(gè)局部條目,用途的最小值為0xe0,對(duì)應(yīng)鍵盤上的左ctrl鍵
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
//0x29:0010 10 01 這是個(gè)局部條目,用途的最大值為0xe7,對(duì)應(yīng)鍵盤上的有GUI(WIN)鍵
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
//0x15:0001 01 01 這是個(gè)全局條目,說明數(shù)據(jù)的邏輯值最小值為0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//0x25:0010 01 01 這是個(gè)全局條目,說明數(shù)據(jù)的邏輯值最大值為1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//0x95:1001 01 01 這是個(gè)全局條目,數(shù)據(jù)域的數(shù)量為8個(gè)
0x95, 0x08, // REPORT_COUNT (8)
//0x75:0111 01 01 這是個(gè)全局條目,每個(gè)數(shù)據(jù)域的長(zhǎng)度為1位
0x75, 0x01, // REPORT_SIZE (1)
//0x81:1000 00 01 這是個(gè)主條目,有8*1bit數(shù)據(jù)域作為輸入,屬性為:Data,Var,Abs
0x81, 0x02, // INPUT (Data,Var,Abs)
//0x95:1001 01 01 這是個(gè)全局條目,數(shù)據(jù)域的數(shù)量為1個(gè)
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 這是個(gè)全局條目,每個(gè)數(shù)據(jù)域的長(zhǎng)度為8位
0x75, 0x08, // REPORT_SIZE (8)
//0x81:1000 00 01 這是個(gè)主條目,有1*8bit數(shù)據(jù)域作為輸入,屬性為:Cnst,Var,Abs
0x81, 0x03, // INPUT (Cnst,Var,Abs)
//0x95:1001 01 01 這是個(gè)全局條目,數(shù)據(jù)域的數(shù)量為6個(gè)
0x95, 0x06, // REPORT_COUNT (6)
//0x75:0111 01 01 這是個(gè)全局條目,每個(gè)數(shù)據(jù)域的長(zhǎng)度為8位
0x75, 0x08, // REPORT_SIZE (8)
//0x25:0010 01 01 這是個(gè)全局條目,邏輯最大值為255
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
//0x19:0001 10 01 這是個(gè)局部條目,用途的最小值為0
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
//0x29:0010 10 01 這是個(gè)局部條目,用途的最大值為0x65
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
//0x81:1000 00 01 這是個(gè)主條目,有6*8bit的數(shù)據(jù)域作為輸入,屬相為屬性為:Data,Var,Abs
0x81, 0x00, // INPUT (Data,Ary,Abs)
//0x25:0010 01 01 這是個(gè)全局條目,邏輯的最大值為1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//0x95:1001 01 01 這是個(gè)全局條目,數(shù)據(jù)域的數(shù)量為2
0x95, 0x07, // REPORT_COUNT (2)
//0x75:0111 01 01 這是個(gè)全局條目,每個(gè)數(shù)據(jù)域的長(zhǎng)度為1位
0x75, 0x01, // REPORT_SIZE (1)
//0x05:0000 01 01 這是個(gè)全局條目,用途頁選擇為L(zhǎng)ED頁
0x05, 0x08, // USAGE_PAGE (LEDs)
//0x19:0001 10 01 這是個(gè)局部條目,用途的最小值為0x01,對(duì)應(yīng)鍵盤上的Num Lock
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
//0x29:0010 10 01 這是個(gè)局部條目,用途的最大值為0x02,對(duì)應(yīng)鍵盤上的Caps Lock
0x29, 0x07, // USAGE_MAXIMUM (Caps Lock)
//0x91:1001 00 01 這是個(gè)主條目,有2*1bit的數(shù)據(jù)域作為輸出,屬性為:Data,Var,Abs
0x91, 0x02, // OUTPUT (Data,Var,Abs)
//0x95:1001 01 01 這是個(gè)全局條目,數(shù)據(jù)域的數(shù)量為1個(gè)
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 這是個(gè)全局條目,每個(gè)數(shù)據(jù)域的長(zhǎng)度為6bit,正好與前面的2bit組成1字節(jié)
0x75, 0x01, // REPORT_SIZE (6)
//0x91:1001 00 01 這是個(gè)主條目,有1*6bit數(shù)據(jù)域最為輸出,屬性為:Cnst,Var,Abs
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};
10、鼠標(biāo)也是一樣。這個(gè)報(bào)告描述符可通過HID官網(wǎng)的生成工具來配置得到
static const uint8_t MouseRepDescr[] =
{
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, 0x03, // INPUT (Cnst,Var,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, 0x02, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
11、按照USB協(xié)議來修改中斷函數(shù)中的狀態(tài)機(jī),這里分層去修改,還是比較好理解的。
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)
{
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 HID_GET_REPORT: //GetReport
len = 1;
pEP0_DataBuf[0] = 0xaa;
break;
case HID_SET_IDLE:
R8_UEP0_T_LEN = 0;
break; //這個(gè)一定要有
case HID_SET_REPORT:
HIDInitFlag = 1;
break;
default:
errflag = 0xFF;
}
}
else
{
switch( SetupReqCode )
{
case USB_GET_DESCRIPTOR:
{
switch( ((pSetupReqPak->wValue)>>8) )
{
case USB_DESCR_TYP_DEVICE:
pDescr = (const UINT8*)&USBDevDescr;
len = sizeof(USB_DEV_DESCR);
break;
case USB_DESCR_TYP_CONFIG:
switch(HIDMode)
{
case HID_KEYBOARD:
pDescr = (const UINT8*)&CfgDescr_keyboard;
len = sizeof(USB_CFG_DESCR_KEYBOARD);
break;
case HID_MOUSE:
pDescr = (const UINT8*)&CfgDescr_Mouse;
len = sizeof(USB_CFG_DESCR_MOUSE);
break;
case HID_KEYBOARD_AND_MOUSE:
pDescr = (const UINT8*)&CfgDescr_keyboardAndMouse;
len = sizeof(USB_CFG_DESCR_KEYBOARD_AND_MOUSE);
break;
}
break;
case USB_DESCR_TYP_STRING:
switch( (pSetupReqPak->wValue)&0xff )
{
case 0:
pDescr = MyLangDescr;
len = MyLangDescr[0];
break;
case 1:
pDescr = MyManuInfo;
len = MyManuInfo[0];
break;
case 2:
pDescr = MyProdInfo;
len = MyProdInfo[0];
break;
case 3:
pDescr = MyProdIDInfo;
len = MyProdIDInfo[0];
break;
default:
errflag = 0xFF;
break;
}
break;
case USB_DESCR_TYP_REPORT:
if(((pSetupReqPak->wIndex)&0xff) == 0) //接口0報(bào)表描述符
{
if(HIDMode == HID_KEYBOARD)
{
pDescr = KeyboardRepDescr;
len = sizeof(KeyboardRepDescr);
HIDInitFlag = 1;
}
else if(HIDMode == HID_MOUSE)
{
pDescr = MouseRepDescr;
len = sizeof(MouseRepDescr);
HIDInitFlag = 1;
}
else
{
pDescr = KeyboardRepDescr;
len = sizeof(KeyboardRepDescr);
}
}
else if(((pSetupReqPak->wIndex)&0xff) == 1) //接口1報(bào)表描述符
{
pDescr = MouseRepDescr; //數(shù)據(jù)準(zhǔn)備上傳
len = sizeof(MouseRepDescr);
HIDInitFlag = 1;
}
else len = 0xff; //本程序只有2個(gè)接口,這句話正常不可能執(zhí)行
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 )
{
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;
}
break;
case UIS_TOKEN_IN:
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;
default:
R8_UEP0_T_LEN = 0;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
break;
case UIS_TOKEN_IN | 1:
R8_UEP1_T_LEN = 0;
KeyBoardHIDEndpBusy = 0;
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_IN | 2:
R8_UEP2_T_LEN = 0;
MouseHIDEndpBusy = 0;
R8_UEP2_CTRL ^= RB_UEP_T_TOG;
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
}
break;
case UIS_TOKEN_OUT:
switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
{
case UIS_TOKEN_OUT:
len = R8_USB_RX_LEN;
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;
}
break;
case UIS_TOKEN_SOF:
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;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_INT_FG |= RB_UIF_BUS_RST;
HIDInitFlag = 0;
}
else if( intflag & RB_UIF_SUSPEND )
{
if ( R8_USB_MIS_ST & RB_UMS_SUSPEND ) {;}
else{;}
R8_USB_INT_FG = RB_UIF_SUSPEND;
HIDInitFlag = 0;
}
else
{
R8_USB_INT_FG = intflag;
}
}
12、鍵盤發(fā)送按鍵和接收指示燈狀態(tài)
static void KB_Send(UINT8 *kb)
{
if(HIDInitFlag == 0)
return;
while( KeyBoardHIDEndpBusy )
{
; //如果忙(上一包數(shù)據(jù)沒有傳上去),則等待。
}
KeyBoardHIDEndpBusy = 1; //設(shè)置為忙狀態(tài)
memcpy(pEP1_IN_DataBuf, kb, 8);
DevEP1_IN_Deal(8);
}
/*******************************************************************************
* Function Name : DevEP1_OUT_Deal
* Description : Deal device Endpoint 1 OUT.
* Input : l: Data length.
* Return : None
*******************************************************************************/
void DevEP1_OUT_Deal( UINT8 l )
{
KeyLedStatus = pEP1_OUT_DataBuf[0];
}
13、鼠標(biāo)的發(fā)送接口
static void MS_Send(UINT8 *ms)
{
if(HIDInitFlag == 0)
return;
while( MouseHIDEndpBusy )
{
; //如果忙(上一包數(shù)據(jù)沒有傳上去),則等待。
}
MouseHIDEndpBusy = 1; //設(shè)置為忙狀態(tài)
memcpy(pEP1_IN_DataBuf, ms, 3);
DevEP1_IN_Deal(3);
}
14、移植arduinio的鍵盤功能
#define SHIFT 0x80
const UINT8 asciimap[128] =
{
0x00, // NUL
0x00, // SOH
0x00, // STX
0x00, // ETX
0x00, // EOT
0x00, // ENQ
0x00, // ACK
0x00, // BEL
0x2a, // BS Backspace
0x2b, // TAB Tab
0x28, // LF Enter
0x00, // VT
0x00, // FF
0x00, // CR
0x00, // SO
0x00, // SI
0x00, // DEL
0x00, // DC1
0x00, // DC2
0x00, // DC3
0x00, // DC4
0x00, // NAK
0x00, // SYN
0x00, // ETB
0x00, // CAN
0x00, // EM
0x00, // SUB
0x00, // ESC
0x00, // FS
0x00, // GS
0x00, // RS
0x00, // US
0x2c, // ' '
0x1e|SHIFT, // !
0x34|SHIFT, // "
0x20|SHIFT, // #
0x21|SHIFT, // $
0x22|SHIFT, // %
0x24|SHIFT, // &
0x34, // '
0x26|SHIFT, // (
0x27|SHIFT, // )
0x25|SHIFT, // *
0x2e|SHIFT, // +
0x36, // ,
0x2d, // -
0x37, // .
0x38, // /
0x27, // 0
0x1e, // 1
0x1f, // 2
0x20, // 3
0x21, // 4
0x22, // 5
0x23, // 6
0x24, // 7
0x25, // 8
0x26, // 9
0x33|SHIFT, // :
0x33, // ;
0x36|SHIFT, // <
0x2e, // =
0x37|SHIFT, // >
0x38|SHIFT, // ?
0x1f|SHIFT, // @
0x04|SHIFT, // A
0x05|SHIFT, // B
0x06|SHIFT, // C
0x07|SHIFT, // D
0x08|SHIFT, // E
0x09|SHIFT, // F
0x0a|SHIFT, // G
0x0b|SHIFT, // H
0x0c|SHIFT, // I
0x0d|SHIFT, // J
0x0e|SHIFT, // K
0x0f|SHIFT, // L
0x10|SHIFT, // M
0x11|SHIFT, // N
0x12|SHIFT, // O
0x13|SHIFT, // P
0x14|SHIFT, // Q
0x15|SHIFT, // R
0x16|SHIFT, // S
0x17|SHIFT, // T
0x18|SHIFT, // U
0x19|SHIFT, // V
0x1a|SHIFT, // W
0x1b|SHIFT, // X
0x1c|SHIFT, // Y
0x1d|SHIFT, // Z
0x2f, // [
0x31, // bslash
0x30, // ]
0x23|SHIFT, // ^
0x2d|SHIFT, // _
0x35, // `
0x04, // a
0x05, // b
0x06, // c
0x07, // d
0x08, // e
0x09, // f
0x0a, // g
0x0b, // h
0x0c, // i
0x0d, // j
0x0e, // k
0x0f, // l
0x10, // m
0x11, // n
0x12, // o
0x13, // p
0x14, // q
0x15, // r
0x16, // s
0x17, // t
0x18, // u
0x19, // v
0x1a, // w
0x1b, // x
0x1c, // y
0x1d, // z
0x2f|SHIFT, // {
0x31|SHIFT, // |
0x30|SHIFT, // }
0x35|SHIFT, // ~
0 // DEL
};
// Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
UINT8 modifiers;
UINT8 reserved;
UINT8 keys[6];
} KeyReport;
/*KeyBoard Function*/
// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report. Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
UINT8 KB_Press(UINT8 k)
{
UINT8 i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers |= (1<<(k-128));
k = 0;
} else { // it's a printing key
k = asciimap[k];
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers |= 0x02; // the left shift modifier
k &= 0x7F;
}
}
// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
break;
}
}
if (i == 6) {
// rt_kprintf("press error.\r\n");
return 0;
}
}
KB_Send((UINT8 *)&_keyReport);
return 1;
}
// release() takes the specified key out of the persistent key report and
// sends the report. This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
UINT8 KB_Release(UINT8 k)
{
uint8_t i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers &= ~(1<<(k-128));
k = 0;
} else { // it's a printing key
k = asciimap[k];
if (!k) {
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
}
// Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}
KB_Send((UINT8 *)&_keyReport);
return 1;
}
void KB_ReleaseAll(void)
{
_keyReport.keys[0] = 0;
_keyReport.keys[1] = 0;
_keyReport.keys[2] = 0;
_keyReport.keys[3] = 0;
_keyReport.keys[4] = 0;
_keyReport.keys[5] = 0;
_keyReport.modifiers = 0;
KB_Send((UINT8 *)&_keyReport);
}
UINT8 KB_Write(UINT8 c)
{
uint8_t p = KB_Press(c); // Keydown
KB_Release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
UINT8 KB_Write_str(const UINT8 *buffer, size_t size) {
size_t n = 0;
while (size--) {
if (*buffer != '\r') {
if (KB_Write(*buffer)) {
n++;
} else {
break;
}
}
buffer++;
}
return n;
}
bool KB_Read_num_lock(void)
{
return KeyLedStatus & 0x01;
}
bool KB_Read_caps_lock(void)
{
return KeyLedStatus & 0x02;
}
15、移植arduino的鼠標(biāo)功能
typedef struct
{
UINT8 button;
UINT8 X;
UINT8 Y;
UINT8 Z;
} MouseReport;
/*Mouse Function*/
void MS_Move(INT8 x, INT8 y, INT8 wheel)
{
_mouseReport.X = x;
_mouseReport.Y = y;
_mouseReport.Z = wheel;
MS_Send((UINT8 *)&_mouseReport);
}
void MS_Click(UINT8 b)
{
_mouseReport.button = b;
MS_Move(0,0,0);
_mouseReport.button = 0;
MS_Move(0,0,0);
}
void MS_Buttons(UINT8 b)
{
if (b != _mouseReport.button)
{
_mouseReport.button = b;
MS_Move(0,0,0);
}
}
void MS_Press(UINT8 b)
{
MS_Buttons(_mouseReport.button | b);
}
void MS_Release(UINT8 b)
{
MS_Buttons(_mouseReport.button & ~b);
}
bool MS_isPressed(uint8_t b)
{
if ((b & _mouseReport.button) > 0)
return true;
return false;
}
嗯,其實(shí)整體還是比較簡(jiǎn)單的,只是代碼比較多,最好還是直接讀上面我的代碼倉庫里面的。
-
嵌入式
+關(guān)注
關(guān)注
5125文章
19438瀏覽量
313070 -
鍵盤
+關(guān)注
關(guān)注
4文章
862瀏覽量
40301 -
RTT
+關(guān)注
關(guān)注
0文章
66瀏覽量
17462 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1348瀏覽量
41368 -
ch32
+關(guān)注
關(guān)注
0文章
73瀏覽量
815
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
【RTT大賽作品連載】AB32VG1評(píng)估板到貨控制彩燈測(cè)試

【RTT大賽作品連載】CH32V103開發(fā)板資料及上電首測(cè)

【RTT大賽作品連載】中科藍(lán)訊AB32VG1開發(fā)板開箱篇

【RTT大賽作品連載】CH32V RTT微秒延時(shí)的實(shí)現(xiàn)

CH32V103數(shù)據(jù)手冊(cè)
【文章連載】RT-Thread創(chuàng)新應(yīng)用大賽文章匯總
【二等獎(jiǎng)】RT-Thread創(chuàng)新應(yīng)用設(shè)計(jì)大賽作品1
CH32V103復(fù)位如何保持變量?
CH32V103基礎(chǔ)教程83-USB模擬鼠標(biāo)鍵盤設(shè)備
RISC-V MCU應(yīng)用開發(fā)教程之CH32V103

評(píng)論