0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

【RTT大賽作品連載】CH32V103 USBHID鍵盤鼠標

煲仔鹵煮的煉鋼 ? 來源:煲仔鹵煮的煉鋼 ? 作者:煲仔鹵煮的煉鋼 ? 2021-11-29 08:49 ? 次閱讀

USB-HID是Universal Serial Bus-Human Interface Device的縮寫,由其名稱可以了解HID設備是直接與人交互的設備,例如鍵盤、鼠標與游戲桿等,也是PC上為數(shù)不多的不需要額外驅(qū)動的協(xié)議棧,所以diy一些小設備非常方便。

之前一直在用STM32芯片,ST已經(jīng)提供了一套完備的USB協(xié)議棧,只要定義好設備配置符就可以用了,看了下CH32V10x系列的資料,里面只有通過USB模擬CH372設備的例程。于是借著比賽的機會,正好把USB的HID協(xié)議學習一下。

對USB感興趣的小伙伴,推薦看下《圈圈教你玩USB》,講的還是很細致的,雖然是多年前的書了,但畢竟USB也還是原來的那套,本文就不詳細介紹USB協(xié)議棧了。

移植過程是比較長的,如果你也是用了CH32V103,并且想要實現(xiàn)鍵盤鼠標功能的話,可以直接參考我共享的代碼:CH32V103-USB-HID-KeyboradMouse: CH32V103 usb HID 的鍵盤鼠標庫 (gitee.com)

這里已經(jīng)寫好了針對鍵盤/鼠標/鍵盤鼠標混合的功能實現(xiàn),并參考了arduino的API,實現(xiàn)鍵盤的按下按鍵、釋放按鍵、釋放所有按鍵、輸入字符等功能,實現(xiàn)鼠標的點擊、移動等功能,可以參考arduino的Keyboard和Mouse的使用方法,當然這里是C實現(xiàn)的,所以函數(shù)接口名是做了修改了哈。

使用方法很簡單,只要進行鍵盤/鼠標/鍵盤鼠標混合的初始化就可以使用了,具體的方法看代碼中的描述。

/*三選一初始化即可*/
USB_HID_Init(HID_KEYBOARD);
USB_HID_Init(HID_MOUSE);
USB_HID_Init(HID_KEYBOARD_AND_MOUSE);

接下來是移植過程:

CH32V103的標準庫中有USB的相關底層驅(qū)動,見ch32v10x_usb.c和ch32v10x_usb.h,其中定義了USB協(xié)議棧需要的各種配置符結構,我們只要按照需要定義就好了。但定義好了配置,該如何與主機端通訊呢?可以參考USB模擬CH372設備的例程,USB通訊都是通過USB中斷函數(shù)中的狀態(tài)機實現(xiàn)的。所以只要定義好配置描述符,并按需要修改中斷函數(shù),理論上就完成了移植工作。

1、USB設備描述符,這里的最大包長度就按照CH32V103的EndPoint0的緩沖區(qū)大小來設置了,廠家ID和產(chǎn)品ID都自定義,后面的字符串索引號是和字符串描述符相關聯(lián)的,所以要對應上。然后配置只有一種即可。

static const USB_DEV_DESCR USBDevDescr = {
    .bLength = 18,                      /*bLength:長度,設備描述符的長度為18字節(jié)*/
    .bDescriptorType = 0x01,            /*bDescriptorType:類型,設備描述符的編號是0x01*/
    .bcdUSB = 0x0200,                   /*bcdUSB:所使用的USB版本為2.0*/
    .bDeviceClass = 0x00,               /*bDeviceClass:設備所使用的類代碼*/
    .bDeviceSubClass = 0x00,            /*bDeviceSubClass:設備所使用的子類代碼*/
    .bDeviceProtocol = 0x00,            /*bDeviceSubClass:設備所使用的子類代碼*/
    .bMaxPacketSize0 = DevEP0SIZE,      /*bMaxPacketSize:最大包長度為64字節(jié)*/
    .idVendor = USBD_VID,               /*idVendor:廠商ID*/
    .idProduct = USBD_PID_FS,           /*idProduct:產(chǎn)品ID*/
    .bcdDevice = 0x0200,                /*bcdDevice:設備的版本號為2.00*/
    .iManufacturer = 0x01,              /*iManufacturer:廠商字符串的索引*/
    .iProduct = 0x02,                   /*iProduct:產(chǎn)品字符串的索引*/
    .iSerialNumber = 0x03,              /*iSerialNumber:設備的序列號字符串索引*/
    .bNumConfigurations = 0x01          /*bNumConfiguration:設備有1種配置*/
};

2、USB配置描述符,下面分別是鍵盤/鼠標/鍵盤鼠標混合的配置符代碼,不同的差別就在配置描述符的總長度和所支持的接口數(shù)量。

static const USB_CFG_DESCR  USBCfgDescr_KB = {
    .bLength = 0x09,                        /*bLength:長度,設備字符串的長度為9字節(jié)*/
    .bDescriptorType = 0x02,                /*bDescriptorType:類型,配置描述符的類型編號為0x2*/
    .wTotalLength = USB_CFG_DESCR_LEN_KB,      /*wTotalLength:配置描述符的總長度為50字節(jié)*/
    .bNumInterfaces = 0x01,                 /*bNumInterfaces:配置所支持的接口數(shù)量1個*/
    .bConfigurationValue = 0x01,            /*bConfigurationValue:該配置的值*/
    .iConfiguration = 0x00,                 /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
    .bmAttributes = 0xA0,                   /* bmAttributes:設備的一些特性,0xA0表示不自供電,支持遠程喚醒
                                                D7:保留必須為1
                                                D6:是否自供電
                                                D5:是否支持遠程喚醒
                                                D4~D0:保留設置為0
                                            */
    .MaxPower = 0x32                        /*從總線上獲得的最大電流為100mA */
};
static const USB_CFG_DESCR  USBCfgDescr_M = {
    .bLength = 0x09,                        /*bLength:長度,設備字符串的長度為9字節(jié)*/
    .bDescriptorType = 0x02,                /*bDescriptorType:類型,配置描述符的類型編號為0x2*/
    .wTotalLength = USB_CFG_DESCR_LEN_M,      /*wTotalLength:配置描述符的總長度為41字節(jié)*/
    .bNumInterfaces = 0x01,                 /*bNumInterfaces:配置所支持的接口數(shù)量1個*/
    .bConfigurationValue = 0x01,            /*bConfigurationValue:該配置的值*/
    .iConfiguration = 0x00,                 /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
    .bmAttributes = 0xA0,                   /* bmAttributes:設備的一些特性,0xA0表示不自供電,支持遠程喚醒
                                                D7:保留必須為1
                                                D6:是否自供電
                                                D5:是否支持遠程喚醒
                                                D4~D0:保留設置為0
                                            */
    .MaxPower = 0x32                        /*從總線上獲得的最大電流為100mA */
};

static const USB_CFG_DESCR  USBCfgDescr_KBM = {
    .bLength = 0x09,                        /*bLength:長度,設備字符串的長度為9字節(jié)*/
    .bDescriptorType = 0x02,                /*bDescriptorType:類型,配置描述符的類型編號為0x2*/
    .wTotalLength = USB_CFG_DESCR_LEN_KBM,      /*wTotalLength:配置描述符的總長度為66字節(jié)*/
    .bNumInterfaces = 0x02,                 /*bNumInterfaces:配置所支持的接口數(shù)量2個*/
    .bConfigurationValue = 0x01,            /*bConfigurationValue:該配置的值*/
    .iConfiguration = 0x00,                 /*iConfiguration:該配置的字符串的索引值,該值為0表示沒有字符串*/
    .bmAttributes = 0xA0,                   /* bmAttributes:設備的一些特性,0xA0表示不自供電,支持遠程喚醒
                                                D7:保留必須為1
                                                D6:是否自供電
                                                D5:是否支持遠程喚醒
                                                D4~D0:保留設置為0
                                            */
    .MaxPower = 0x32                        /*從總線上獲得的最大電流為100mA */
};

3、設備描述符,設備描述符中包括了接口描述符/HID描述符/端點描述符,因此這里構建了個結構體,方便一起發(fā)送給主機端,也是分為三種:鍵盤/鼠標/鍵盤和鼠標,其實就是不同的接口描述符/HID描述符/端點描述符的組合方式。

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描述符,和兩個端點的描述符,這里我創(chuàng)建了兩個端點描述符,一個用于發(fā)送按鍵值,另一個用于接收主機端的狀態(tài)值,比如大寫鎖定燈,小鍵盤燈之類的


static const USB_ITF_DESCR  KeyBoardItfDescr = {
    .bLength = 0x09,                    /*bLength:長度,設備字符串的長度為9字節(jié)*/
    .bDescriptorType = 0x04,            /*bDescriptorType:接口描述符的類型為0x4 */
    .bInterfaceNumber = 0x00,           /*bInterfaceNumber:該接口的編號*/
    .bAlternateSetting = 0x00,          /*bAlternateSetting:該接口的備用編號 */
    .bNumEndpoints = 0x02,              /*bInterfaceNumber:該接口的編號*/
    .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描述符的長度為9字節(jié) */
    .bDescriptorType = 0x21,                        /*bDescriptorType: HID的描述符類型為0x21 */
    .bcdHID = 0x0111,                               /*bcdHID: HID協(xié)議的版本為1.1 */
    .bCountryCode = 0x00,                           /*bCountryCode: 國家代號 */
    .bNumDescriptors = 0x01,                        /*bNumDescriptors: 下級描述符的數(shù)量*/
    .bDescriptorTypeX = 0x22,                       /*bDescriptorType:下級描述符的類型*/
    .wDescriptorLengthL = sizeof(KeyboardRepDescr)&0xFF,   /*wItemLength: 下級描述符的長度*/
    .wDescriptorLengthH = 0x00,
};

static const USB_ENDP_DESCR  KeyBoardINEndpDescr = {
    .bLength = 0x07,
    .bDescriptorType = 0x05,
    .bEndpointAddress = 0x81,     /* bEndpointAddress: 該端點(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點號*/
    .bmAttributes = 0x03,         /* bmAttributes: 端點的屬性為為中斷端點.
                                     D0~D1表示傳輸類型:0(控制傳輸),1(等時傳輸),2(批量傳輸),3(中斷傳輸)
                                                                                             非等時傳輸端點:D2~D7:保留為0
                                                                                             等時傳輸端點:
                                     D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
                                     D4~D5表示用途:0(數(shù)據(jù)端點),1(反饋端點),2(暗含反饋的數(shù)據(jù)端點),3(保留)
                                     D6~D7:保留,
                                  */
    .wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點支持的最大包長度為DevEP0SIZE字節(jié)*/
    .bInterval = 0x0A,            /* bInterval: 輪詢間隔(10ms) */
};

static const USB_ENDP_DESCR  KeyBoardOUTEndpDescr = {
    .bLength = 0x07,
    .bDescriptorType = 0x05,
    .bEndpointAddress = 0x01,     /* bEndpointAddress: 該端點(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點號*/
    .bmAttributes = 0x03,         /* bmAttributes: 端點的屬性為為中斷端點.
                                     D0~D1表示傳輸類型:0(控制傳輸),1(等時傳輸),2(批量傳輸),3(中斷傳輸)
                                                                                             非等時傳輸端點:D2~D7:保留為0
                                                                                             等時傳輸端點:
                                     D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
                                     D4~D5表示用途:0(數(shù)據(jù)端點),1(反饋端點),2(暗含反饋的數(shù)據(jù)端點),3(保留)
                                     D6~D7:保留,
                                  */
    .wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點支持的最大包長度為DevEP0SIZE字節(jié)*/
    .bInterval = 0x0A,            /* bInterval: 輪詢間隔(10ms) */
};

5、鼠標的接口描述符,HID描述符,和端點描述符

static const USB_ITF_DESCR  MouseItfDescr = {
    .bLength = 0x09,                    /*bLength:長度,設備字符串的長度為9字節(jié)*/
    .bDescriptorType = 0x04,            /*bDescriptorType:接口描述符的類型為0x4 */
    .bInterfaceNumber = 0x01,           /*bInterfaceNumber:該接口的編號*/
    .bAlternateSetting = 0x00,          /*bAlternateSetting:該接口的備用編號 */
    .bNumEndpoints = 0x01,              /*bInterfaceNumber:該接口的編號*/
    .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描述符的長度為9字節(jié) */
    .bDescriptorType = 0x21,                        /*bDescriptorType: HID的描述符類型為0x21 */
    .bcdHID = 0x0111,                               /*bcdHID: HID協(xié)議的版本為1.1 */
    .bCountryCode = 0x00,                           /*bCountryCode: 國家代號 */
    .bNumDescriptors = 0x01,                        /*bNumDescriptors: 下級描述符的數(shù)量*/
    .bDescriptorTypeX = 0x22,                       /*bDescriptorType:下級描述符的類型*/
    .wDescriptorLengthL = sizeof(MouseRepDescr)&0xFF,      /*wItemLength: 下級描述符的長度*/
    .wDescriptorLengthH = 0x00,
};

static const USB_ENDP_DESCR  MouseEndpDescr = {
    .bLength = 0x07,
    .bDescriptorType = 0x05,
    .bEndpointAddress = 0x82,     /* bEndpointAddress: 該端點(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點號*/
    .bmAttributes = 0x03,         /* bmAttributes: 端點的屬性為為中斷端點.
                                     D0~D1表示傳輸類型:0(控制傳輸),1(等時傳輸),2(批量傳輸),3(中斷傳輸)
                                                                                             非等時傳輸端點:D2~D7:保留為0
                                                                                             等時傳輸端點:
                                     D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)
                                     D4~D5表示用途:0(數(shù)據(jù)端點),1(反饋端點),2(暗含反饋的數(shù)據(jù)端點),3(保留)
                                     D6~D7:保留,
                                  */
    .wMaxPacketSize = DevEP0SIZE, /* wMaxPacketSize: 該端點支持的最大包長度為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設備描述符中所提到的字符串描述符,這部分可以通過USB字符串描述符生成器來得到

/* Language Descriptor */
static const UINT8  MyLangDescr[] = {
    0x04,               /*bLength:本描述符的長度為4字節(jié)*/
    0x03,               /*bDescriptorType:字符串描述符的類型為0x03*/
    0x09,               /*bString:語言ID為0x0409,表示美式英語*/
    0x04
};

/* Manufactor Descriptor */
static const UINT8 MyManuInfo[] = {
    0x1E,                /*bLength:廠商字符串描述符的長度*/
    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、鍵盤除了上述配置類的描述符,還需要一個報告描述符來描述通訊數(shù)據(jù)的格式

/* HID的報告描述符*/
/*  定義了8字節(jié)發(fā)送:
**  第一字節(jié)表示特殊件是否按下:D0:Ctrl D1:Shift D2:Alt
**  第二字節(jié)保留,值為0
**  第三~第八字節(jié):普通鍵鍵值的數(shù)組,最多能同時按下6個鍵
**  定義了1字節(jié)接收:對應鍵盤上的LED燈,這里只用了兩個位。
**     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:關集合(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:報告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:標識符索引(Designator Index)
       **                0100:標識符最小值(Designator Minimum) 0101:標識符最大值(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 這是個全局條目,用途頁選擇為普通桌面頁
       0x05, 0x01, // USAGE_PAGE (Generic Desktop)
       //0x09:0000 10 01 這是個全局條目,用途選擇為鍵盤
       0x09, 0x06, // USAGE (Keyboard)
       //0xa1:1010 00 01 這是個主條目,選擇為應用集合,
       0xa1, 0x01, // COLLECTION (Application)
       //0x05:0000 01 11 這是個全局條目,用途頁選擇為鍵盤/按鍵
       0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)

       //0x19:0001 10 01 這是個局部條目,用途的最小值為0xe0,對應鍵盤上的左ctrl鍵
       0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
       //0x29:0010 10 01 這是個局部條目,用途的最大值為0xe7,對應鍵盤上的有GUI(WIN)鍵
       0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
       //0x15:0001 01 01 這是個全局條目,說明數(shù)據(jù)的邏輯值最小值為0
       0x15, 0x00, // LOGICAL_MINIMUM (0)
       //0x25:0010 01 01 這是個全局條目,說明數(shù)據(jù)的邏輯值最大值為1
       0x25, 0x01, // LOGICAL_MAXIMUM (1)
       //0x95:1001 01 01 這是個全局條目,數(shù)據(jù)域的數(shù)量為8個
       0x95, 0x08, // REPORT_COUNT (8)
       //0x75:0111 01 01 這是個全局條目,每個數(shù)據(jù)域的長度為1位
       0x75, 0x01, // REPORT_SIZE (1)
       //0x81:1000 00 01 這是個主條目,有8*1bit數(shù)據(jù)域作為輸入,屬性為:Data,Var,Abs
       0x81, 0x02, // INPUT (Data,Var,Abs)

       //0x95:1001 01 01 這是個全局條目,數(shù)據(jù)域的數(shù)量為1個
       0x95, 0x01, // REPORT_COUNT (1)
       //0x75:0111 01 01 這是個全局條目,每個數(shù)據(jù)域的長度為8位
       0x75, 0x08, // REPORT_SIZE (8)
       //0x81:1000 00 01 這是個主條目,有1*8bit數(shù)據(jù)域作為輸入,屬性為:Cnst,Var,Abs
       0x81, 0x03, // INPUT (Cnst,Var,Abs)

       //0x95:1001 01 01 這是個全局條目,數(shù)據(jù)域的數(shù)量為6個
       0x95, 0x06, // REPORT_COUNT (6)
       //0x75:0111 01 01 這是個全局條目,每個數(shù)據(jù)域的長度為8位
       0x75, 0x08, // REPORT_SIZE (8)
       //0x25:0010 01 01 這是個全局條目,邏輯最大值為255
       0x25, 0xFF, // LOGICAL_MAXIMUM (255)
       //0x19:0001 10 01 這是個局部條目,用途的最小值為0
       0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
       //0x29:0010 10 01 這是個局部條目,用途的最大值為0x65
       0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
       //0x81:1000 00 01 這是個主條目,有6*8bit的數(shù)據(jù)域作為輸入,屬相為屬性為:Data,Var,Abs
       0x81, 0x00, // INPUT (Data,Ary,Abs)

       //0x25:0010 01 01 這是個全局條目,邏輯的最大值為1
       0x25, 0x01, // LOGICAL_MAXIMUM (1)

       //0x95:1001 01 01 這是個全局條目,數(shù)據(jù)域的數(shù)量為2
       0x95, 0x07, // REPORT_COUNT (2)
       //0x75:0111 01 01 這是個全局條目,每個數(shù)據(jù)域的長度為1位
       0x75, 0x01, // REPORT_SIZE (1)
       //0x05:0000 01 01 這是個全局條目,用途頁選擇為LED頁
       0x05, 0x08, // USAGE_PAGE (LEDs)
       //0x19:0001 10 01 這是個局部條目,用途的最小值為0x01,對應鍵盤上的Num Lock
       0x19, 0x01, // USAGE_MINIMUM (Num Lock)
       //0x29:0010 10 01 這是個局部條目,用途的最大值為0x02,對應鍵盤上的Caps Lock
       0x29, 0x07, // USAGE_MAXIMUM (Caps Lock)
       //0x91:1001 00 01 這是個主條目,有2*1bit的數(shù)據(jù)域作為輸出,屬性為:Data,Var,Abs
       0x91, 0x02, // OUTPUT (Data,Var,Abs)

       //0x95:1001 01 01 這是個全局條目,數(shù)據(jù)域的數(shù)量為1個
       0x95, 0x01, // REPORT_COUNT (1)
       //0x75:0111 01 01 這是個全局條目,每個數(shù)據(jù)域的長度為6bit,正好與前面的2bit組成1字節(jié)
       0x75, 0x01, // REPORT_SIZE (6)
       //0x91:1001 00 01 這是個主條目,有1*6bit數(shù)據(jù)域最為輸出,屬性為:Cnst,Var,Abs
       0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
       0xc0        // END_COLLECTION
};

10、鼠標也是一樣。這個報告描述符可通過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)機,這里分層去修改,還是比較好理解的。

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;    //這個一定要有
                            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報表描述符
                                            {
                                                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報表描述符
                                            {
                                                pDescr = MouseRepDescr;                      //數(shù)據(jù)準備上傳
                                                len = sizeof(MouseRepDescr);
                                                HIDInitFlag = 1;
                                            }
                                            else len = 0xff;                                //本程序只有2個接口,這句話正常不可能執(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;                                      //設置為忙狀態(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、鼠標的發(fā)送接口

static void MS_Send(UINT8 *ms)
{
    if(HIDInitFlag == 0)
        return;
    while( MouseHIDEndpBusy )
    {
        ;                                               //如果忙(上一包數(shù)據(jù)沒有傳上去),則等待。
    }
    MouseHIDEndpBusy = 1;                                      //設置為忙狀態(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的鼠標功能

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;
}

嗯,其實整體還是比較簡單的,只是代碼比較多,最好還是直接讀上面我的代碼倉庫里面的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5046

    文章

    18832

    瀏覽量

    298779
  • 鍵盤
    +關注

    關注

    4

    文章

    854

    瀏覽量

    39365
  • RTT
    RTT
    +關注

    關注

    0

    文章

    64

    瀏覽量

    16992
  • RT-Thread
    +關注

    關注

    31

    文章

    1239

    瀏覽量

    39452
  • ch32
    +關注

    關注

    0

    文章

    73

    瀏覽量

    589
收藏 人收藏

    評論

    相關推薦

    RTT大賽作品連載】AB32VG1評估板到貨控制彩燈測試

    RTT大賽作品連載】AB32VG1評估板到貨控制彩燈測試篇;接下來看看在如何AB32VG1評估板控制彩燈! 在RT-ThreadStudio新建項目到對應開發(fā)配置及下載及驗證測試!
    的頭像 發(fā)表于 11-07 19:39 ?5069次閱讀
    【<b class='flag-5'>RTT</b><b class='flag-5'>大賽</b><b class='flag-5'>作品</b><b class='flag-5'>連載</b>】AB32VG1評估板到貨控制彩燈測試

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

    簡介 ? 參加論壇舉辦的rtt大賽也有幾天了,我選用的是CH32V103的板子,第一步就是收集板卡的相關資料了。 CH32V103是 沁恒微電子設計的一款RSIC-
    的頭像 發(fā)表于 11-15 08:45 ?8379次閱讀
    【<b class='flag-5'>RTT</b><b class='flag-5'>大賽</b><b class='flag-5'>作品</b><b class='flag-5'>連載</b>】<b class='flag-5'>CH32V103</b>開發(fā)板資料及上電首測

    RTT大賽作品連載】AB32VG1評估板 音樂播放器

    RTT大賽作品連載】AB32VG1評估板 音樂播放器
    的頭像 發(fā)表于 11-12 21:11 ?6109次閱讀
    【<b class='flag-5'>RTT</b><b class='flag-5'>大賽</b><b class='flag-5'>作品</b><b class='flag-5'>連載</b>】AB32VG1評估板 音樂播放器

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

    介紹電路原理圖分析接口說明,AB32VG1開發(fā)板是以中科藍訊(Bluetrum)公司推出的基于RISC-V架構的高配置芯片AB5301A為核心所組成的。【RTT大賽作品
    的頭像 發(fā)表于 11-13 10:01 ?1w次閱讀
    【<b class='flag-5'>RTT</b><b class='flag-5'>大賽</b><b class='flag-5'>作品</b><b class='flag-5'>連載</b>】中科藍訊AB32VG1開發(fā)板開箱篇

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

    CH32V103是沁恒出的一款基于RiscV核心的單片機,在官網(wǎng)有該芯片的資料、庫函數(shù)和例程,另外沁恒還開發(fā)了一款IDE: MounRiver Studio 。里面也提供了不少的例程可以參考,其中
    的頭像 發(fā)表于 11-29 08:30 ?7606次閱讀
    【<b class='flag-5'>RTT</b><b class='flag-5'>大賽</b><b class='flag-5'>作品</b><b class='flag-5'>連載</b>】<b class='flag-5'>CH32V</b> <b class='flag-5'>RTT</b>微秒延時的實現(xiàn)

    CH32V103數(shù)據(jù)手冊

    教程基于沁恒32位通用增強型RISC-V架構MCU CH32V103,力爭全面分析CH32V103的每個外設功能及使用方法,手把手教大家玩轉(zhuǎn)RISC-V MCU應用開發(fā)。教程側(cè)重于外設
    發(fā)表于 07-19 07:55

    【文章連載】RT-Thread創(chuàng)新應用大賽文章匯總

    連載】中科藍訊AB32VG1開發(fā)板開箱篇專欄作者:煲仔鹵煮的煉鋼【RTT大賽作品連載CH32V103
    發(fā)表于 10-11 15:13

    【二等獎】RT-Thread創(chuàng)新應用設計大賽作品1

    鼠標鍵盤的HID庫,寫在了【RTT大賽作品連載CH32V103
    發(fā)表于 01-25 11:49

    如何入門CH32V103

    如何入門CH32V103?
    發(fā)表于 02-16 07:12

    CH32V103復位如何保持變量?

    CH32V103復位如何保持變量,只有上電才清零. 原來用stm32f103可以設置 __attribute__((unused, section(".noinit")))改用 CH32V103 后, 每次復位都會得到一固定的值
    發(fā)表于 06-15 06:13

    CH32V103基礎教程83-USB模擬鼠標鍵盤設備

    本章教學主要使用CH32V103 USB模擬鼠標鍵盤設備。 1、USB簡介及相關函數(shù)介紹關于USB工具介紹,可參考前面章節(jié)。 2、硬件設計本章教程主要進行USB模擬鼠標鍵盤實驗,只需
    發(fā)表于 05-09 16:56

    RISC-V MCU應用開發(fā)教程之CH32V103

    教程基于沁恒32位通用增強型RISC-V架構MCU CH32V103,力爭全面分析CH32V103的每個外設功能及使用方法,手把手教大家玩轉(zhuǎn)RISC-V MCU應用開發(fā)。教程側(cè)重于外設
    發(fā)表于 11-18 16:21 ?18次下載
    RISC-<b class='flag-5'>V</b> MCU應用開發(fā)教程之<b class='flag-5'>CH32V103</b>

    ch32v103應用教程 ch32v103c8t6兼容 ch32v103c8t6燒錄器

    ch32v103應用教程 ch32v103c8t6兼容 ch32v103c8t6燒錄器 ch32v103c8t6編程器 CH32V103應用
    的頭像 發(fā)表于 08-22 15:19 ?1591次閱讀

    ch32v103和stm32的區(qū)別

    ch32v103和stm32的區(qū)別? 在現(xiàn)代工業(yè)和科技領域中,集成電路是不可或缺的一個組成部分。集成電路向來是在研發(fā)過程中不斷進步和發(fā)展,而其中最為著名和廣泛使用的兩種芯片,就是ch32v103
    的頭像 發(fā)表于 08-22 15:48 ?1239次閱讀

    ch32v103與stm32f103的區(qū)別

    ch32v103與stm32f103的區(qū)別? Ch32v103與STM32f103是兩種不同的芯片,雖然它們都是基于ARM Cortex-M3內(nèi)核的32位微控制器,但它們在硬件配置、功
    的頭像 發(fā)表于 08-22 15:49 ?2098次閱讀