STM32 keyboard USB HID鍵盤功能的實現

原文地址::http://blog.csdn.net/u011318735/article/details/17429789

 

相關文章

·1STM32完成USB_Keyboard的實驗總結----http://blog.csdn.net/u014980289/article/details/62417601

 

 

下面編寫下USB鍵盤的程序,依然在CustomHID工程上修改。

依舊最先修改的是usb_desc.c文件。我們從設備描述符開始講述。

設備描述符需要修改下bMaxPacketSize(最大包長度)域爲0x08,因爲被本次的工程最大通訊長度就是8字節,正好符合USB規範,所以這裏改成0x08,還要注意在usb_prop.cDEVICE_PROP Device_Property結構體裏註冊的最大長度也要是0x08,與設備描述符的要相同(我們在下文說到)。這裏最好還要修改下PIDVID的域的值,以防該PIDVID對應的設備已經在電腦裏有了驅動而導致功能不正常。

/* USB標準設備描述符*/

const uint8_t Keyboard_DeviceDescriptor[KEYBOARD_SIZ_DEVICE_DESC] =

{

    0x12,                     /*bLength:長度,設備描述符的長度爲18字節*/

    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType:類型,設備描述符的編號是0x01*/

    0x00,                       /*bcdUSB:所使用的USB版本爲2.0*/

    0x02,

    0x00,                       /*bDeviceClass:設備所使用的類代碼*/

    0x00,                       /*bDeviceSubClass:設備所使用的子類代碼*/

    0x00,                       /*bDeviceProtocol:設備所使用的協議*/

    0x08,                       /*bMaxPacketSize:最大包長度爲8字節*/

    0x78,                       /*idVendor:廠商ID0x7788*/

    0x67,

    0x12,                            /*idProduct:產品ID0x1122*/

    0x01,

    0x00,                       /*bcdDevice:設備的版本號爲2.00*/

    0x02,

    1,                          /*iManufacturer:廠商字符串的索引*/

    2,                          /*iProduct:產品字符串的索引*/

    3,                          /*iSerialNumber:設備的序列號字符串索引*/

    0x01                        /*bNumConfiguration:設備有1種配置*/

}; /* keyboard設備描述符 */

接下去修改下配置描述符。找到接口的描述符的bNumEndpoints(該接口所使用的端點數)域,不用修改,但需要提下,還是0x02,表示使用2個端點。修改下接口描述符的nInterfaceProtocol (該接口使用的協議)域爲0x01,表示是鍵盤。在輸入端點描述符中端點設置端點1爲爲中斷傳輸的輸入端點,設置 wMaxPacketSize:(該端點支持的最大包長度)域的值爲0x08,因爲本次鍵盤的工程需要向USB主機發送8字節。在輸出端點描述符設置端點1爲中斷傳輸的輸出端點,設置爲中斷傳輸設置 wMaxPacketSize:(該端點支持的最大包長度)域的值爲0x01,因爲USB主機只會向USB從設備發送1個字節。

/* USB配置描述符集合(配置、接口、端點、類、廠商)(Configuration, Interface, Endpoint, Class, Vendor */

const uint8_t Keyboard_ConfigDescriptor[KEYBOARD_SIZ_CONFIG_DESC] =

{

    0x09,                   /*bLength:長度,設備字符串的長度爲9字節*/

    USB_CONFIGURATION_DESCRIPTOR_TYPE,    /*bDescriptorType:類型,配置描述符的類型編號爲0x2*/

    KEYBOARD_SIZ_CONFIG_DESC,                    /*wTotalLength:配置描述符的總長度爲41字節*/   

    0x00,

    0x01,             /*bNumInterfaces:配置所支持的接口數量1*/

    0x01,             /*bConfigurationValue:該配置的值*/

    0x00,             /*iConfiguration:該配置的字符串的索引值,該值爲0表示沒有字符串*/             

    0xC0,             /* bmAttributes:設備的一些特性,0xc0表示自供電,不支持遠程喚醒

                                          D7:保留必須爲1D6:是否自供電,D5:是否支持遠程喚醒,D4~D0:保留設置爲0*/

    0x32,             /*從總線上獲得的最大電流爲100mA */

//    0x96,         /*MaxPower:設備需要從總線上獲取多少電流,單位爲2mA0x96表示300mA*/

 

    /************** 接口描述符****************/

       /* 09 */

    0x09,             /*bLength:長度,接口描述符的長度爲9字節 */

    USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType:接口描述符的類型爲0x4 */

    0x00,             /*bInterfaceNumber:該接口的編號*/

    0x00,             /*bAlternateSetting:該接口的備用編號 */

    0x02,             /*bNumEndpoints:該接口所使用的端點數*/

    0x03,             /*bInterfaceClass該接口所使用的類爲HID*/

    0x01,             /*bInterfaceSubClass:該接口所用的子類 1=BOOT, 0=no boot */

    0x01,             /*nInterfaceProtocol :該接口使用的協議0=none, 1=keyboard, 2=mouse */

    0,                /*iInterface: 該接口字符串的索引 */

 

    /*****************HID描述符 ********************/

       /* 18 */

    0x09,             /*bLength: HID描述符的長度爲9字節 */

    HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID的描述符類型爲0x21 */

    0x10,             /*bcdHID: HID協議的版本爲1.1 */

    0x01,

    0x21,             /*bCountryCode: 國家代號 */

    0x01,             /*bNumDescriptors: 下級描述符的數量*/

    0x22,             /*bDescriptorType:下級描述符的類型*/

    KEYBOARD_SIZ_REPORT_DESC,/* wItemLength: 下一集描述符的長度*/

    0x00,

 

    /********************輸入端點描述符******************/

       /* 27 */

    0x07,             /* bLength: 端點描述符的長度爲7字節*/

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: 端點描述符的類型爲0x05*/

    0x81,             /* bEndpointAddress: 該端點(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點號*/              

    0x03,             /* bmAttributes: 端點的屬性爲爲中斷端點.

                     D0~D1表示傳輸類型:0(控制傳輸),1(等時傳輸),2(批量傳輸),3(中斷傳輸)

                     非等時傳輸端點:D2~D7:保留爲0

                     等時傳輸端點:

                     D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)

                     D4~D5表示用途:0(數據端點),1(反饋端點),2(暗含反饋的數據端點),3(保留)D6~D7:保留,*/

    0x08,             /* wMaxPacketSize: 該端點支持的最大包長度爲8字節*/

    0x00,

    0x0A,             /* bInterval: 輪詢間隔(32ms) */

 

       /********************輸出端點描述符*******************/

       /* 34 */

       0x07,         /* bLength: 端點描述符的長度爲7字節*/

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: 端點描述符的類型爲0x05*/

    0x01,             /* bEndpointAddress: 該端點(輸入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端點號*/              

    0x03,             /* bmAttributes: 端點的屬性爲爲中斷端點.

                     D0~D1表示傳輸類型:0(控制傳輸),1(等時傳輸),2(批量傳輸),3(中斷傳輸)

                     非等時傳輸端點:D2~D7:保留爲0

                     等時傳輸端點:

                     D2~D3表示同步的類型:0(無同步),1(異步),2(適配),3(同步)

                     D4~D5表示用途:0(數據端點),1(反饋端點),2(暗含反饋的數據端點),3(保留)D6~D7:保留,*/

    0x01,             /* wMaxPacketSize: 該端點支持的最大包長度爲字節*/

    0x00,

    0x0A,             /* bInterval: 輪詢間隔(32ms) */

   /* 41 */

};

講到報告描述符,得將報告描述符的整個替換掉。該報告描述符定義了8字節的輸入域,第一個字節表示特殊件是否按下,鍵盤的特殊鍵包括:ctrl,shift,alt鍵等,該字節D0表示Ctrl鍵,D1表示Shift鍵,D2表示Alt鍵,其他位保留。第二個字節保留,固定值爲0。第三個字節到第八個字節用來保存按鍵值,如果只有一個按鍵按下,則按鍵值保存在第三個字節,如果有兩個或兩個以上的按鍵按下,則依次保存在從第三字節開始的字節中,當然最多隻能6個按鍵同時按下,超過6個鍵值,則不響應。報告描述符還定義了一個字節的輸出域,該字節是是用來控制LED燈的,我們都知道鍵盤的上有幾個LED燈,比如說大小寫鍵的LED燈等,該字節的定義:D0:Num Lock   D1:Cap Lock   D2:Scroll Lock   D3:Compose   D4:Kana,由於我一直用筆記本電腦的鍵盤,所以我也只認識前三個鍵。在這個工程,我只做了Ctrl鍵、Shift鍵、Num Lock鍵,以及A鍵。

/* HID的報告描述符*/

/*定義了8字節發送:

**  第一字節表示特殊件是否按下:D0:Ctrl D1:Shift D2:Alt

**  第二字節保留,值爲0

**  第三~第八字節:普通鍵鍵值的數組,最多能同時按下6個鍵

**定義了1字節接收:對應鍵盤上的LED,這裏只用了兩個位。

**     D0:Num Lock   D1:Cap Lock   D2:Scroll Lock   D3:Compose   D4:Kana*/

const uint8_t Keyboard_ReportDescriptor[KEYBOARD_SIZ_REPORT_DESC] =

{     

       /*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:單元指數(Unit Exponet) 0110:單元(Unit) 0111:數據域大小(Report Size)

       **                                 1000:報告ID(Report ID) 1001:數據域數量(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字節  01:1字節  10:2字節  11:4字節*/

      

       //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 這是個全局條目,說明數據的邏輯值最小值爲0

       0x15, 0x00, // LOGICAL_MINIMUM (0)

       //0x25:0010 01 01 這是個全局條目,說明數據的邏輯值最大值爲1

       0x25, 0x01, // LOGICAL_MAXIMUM (1)

 

       //0x95:1001 01 01 這是個全局條目,數據域的數量爲8

       0x95, 0x08, // REPORT_COUNT (8)

       //0x75:0111 01 01 這是個全局條目,每個數據域的長度爲1

       0x75, 0x01, // REPORT_SIZE (1)       

       //0x81:1000 00 01 這是個主條目,有8*1bit數據域作爲輸入,屬性爲:Data,Var,Abs

       0x81, 0x02, // INPUT (Data,Var,Abs)

 

       //0x95:1001 01 01 這是個全局條目,數據域的數量爲1

       0x95, 0x01, // REPORT_COUNT (1)

       //0x75:0111 01 01 這是個全局條目,每個數據域的長度爲8

       0x75, 0x08, // REPORT_SIZE (8)

       //0x81:1000 00 01 這是個主條目,有1*8bit數據域作爲輸入,屬性爲:Cnst,Var,Abs

       0x81, 0x03, // INPUT (Cnst,Var,Abs)

 

       //0x95:1001 01 01 這是個全局條目,數據域的數量爲6

       0x95, 0x06, // REPORT_COUNT (6)

       //0x75:0111 01 01 這是個全局條目,每個數據域的長度爲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的數據域作爲輸入,屬相爲屬性爲:Data,Var,Abs

       0x81, 0x00, // INPUT (Data,Ary,Abs)

 

       //0x25:0010 01 01 這是個全局條目,邏輯的最大值爲1

       0x25, 0x01, // LOGICAL_MAXIMUM (1)

       //0x95:1001 01 01 這是個全局條目,數據域的數量爲2

       0x95, 0x02, // REPORT_COUNT (2)

       //0x75:0111 01 01 這是個全局條目,每個數據域的長度爲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, 0x02, // USAGE_MAXIMUM (Caps Lock)

       //0x91:1001 00 01 這是個主條目,有2*1bit的數據域作爲輸出,屬性爲:Data,Var,Abs

       0x91, 0x02, // OUTPUT (Data,Var,Abs)

 

       //0x95:1001 01 01 這是個全局條目,數據域的數量爲1

       0x95, 0x01, // REPORT_COUNT (1)

       //0x75:0111 01 01 這是個全局條目,每個數據域的長度爲6bit,正好與前面的2bit組成1字節

       0x75, 0x06, // REPORT_SIZE (6)

       //0x91:1001 00 01 這是個主條目,有1*6bit數據域最爲輸出,屬性爲:Cnst,Var,Abs

       0x91, 0x03, // OUTPUT (Cnst,Var,Abs)

 

       0xc0        // END_COLLECTION

};

說到按鍵的鍵值的定義,那就要參考HID用途表(54頁開始),截個開始的功能表吧,湊夠途中可以看到A鍵的鍵值是4

 

其他的一些描述符,這裏也貼出來:

 

/* 語言ID描述符 */

const uint8_t Keyboard_StringLangID[KEYBOARD_SIZ_STRING_LANGID] =

{

    KEYBOARD_SIZ_STRING_LANGID,   /*bLength:本描述符的長度爲4字節*/

    USB_STRING_DESCRIPTOR_TYPE,           /*bDescriptorType:字符串描述符的類型爲0x03*/

    0x09,                                               /*bString:語言ID0x0409,表示美式英語*/

    0x04

}; /* LangID = 0x0409: U.S. English*/

 

/*廠商字符串描述符*/

const uint8_t Keyboard_StringVendor[KEYBOARD_SIZ_STRING_VENDOR] =

{

    KEYBOARD_SIZ_STRING_VENDOR,    /*bLength:廠商字符串描述符的長度*/

    USB_STRING_DESCRIPTOR_TYPE,    /*bDescriptorType:字符串描述符的類型爲0x03*/

    'B' , 0, 'y', 0, ':' , 0, 'z' , 0, 'i', 0, 'y', 0,'e', 0,'3', 0, '3', 0, '4', 0  /*自定義*/

};

 

/*產品的字符串描述符*/

const uint8_t Keyboard_StringProduct[KEYBOARD_SIZ_STRING_PRODUCT] =

{

    KEYBOARD_SIZ_STRING_PRODUCT,    /* bLength:產品的字符串描述符*/

    USB_STRING_DESCRIPTOR_TYPE,     /* bDescriptorType:字符串描述符的類型爲0x03*/

    'z', 0, 'i', 0, 'y', 0, 'e', 0, '3', 0, '3', 0 ,'4',0/*自定義*/

};

 

/*產品序列號的字符串描述符*/

uint8_t Keyboard_StringSerial[KEYBOARD_SIZ_STRING_SERIAL] =

{

    KEYBOARD_SIZ_STRING_SERIAL,     /* bLength:產品序列號*/

    USB_STRING_DESCRIPTOR_TYPE,     /* bDescriptorType:字符串描述符的類型爲0x03*/

    '1', 0, '2', 0, '3', 0,'4', 0,'5', 0, '6', 0, '7', 0 /*自定義*/

};

這裏還要貼出usb_desc.h中的一些宏定義,這些宏定義在上面的描述符中用到:

#define USB_DEVICE_DESCRIPTOR_TYPE              0x01     //設備描述符類型

#define USB_CONFIGURATION_DESCRIPTOR_TYPE       0x02   //配置描述符類型

#define USB_STRING_DESCRIPTOR_TYPE              0x03     //字符串描述符類型

#define USB_INTERFACE_DESCRIPTOR_TYPE           0x04     //接口描述符類型

#define USB_ENDPOINT_DESCRIPTOR_TYPE            0x05     //端點描述符類型

 

#define HID_DESCRIPTOR_TYPE                     0x21      //HID描述符類型

#define KEYBOARD_SIZ_HID_DESC                  0x09      //HID描述符的長度

#define KEYBOARD_OFF_HID_DESC                  0x12      //HID描述符在配置描述符集合數組中的偏移

 

#define KEYBOARD_SIZ_DEVICE_DESC               18        //設備描述符的長度

#define KEYBOARD_SIZ_CONFIG_DESC               41        //配置描述符的長度

#define KEYBOARD_SIZ_REPORT_DESC               61               //報告描述符的的長度

#define KEYBOARD_SIZ_STRING_LANGID             4         //語言ID字符串描述符的長度

#define KEYBOARD_SIZ_STRING_VENDOR             22        //廠商字符串描述符的長度

#define KEYBOARD_SIZ_STRING_PRODUCT            16        //產品字符串描述符的長度

#define KEYBOARD_SIZ_STRING_SERIAL             26        //序列號字符串描述符的長度

 

#define STANDARD_ENDPOINT_DESC_SIZE            0x09      //

 

#define       REPORT_COUNT                       8             //報告返回長度

 

 

講完了usb_desc.c的文件,下面要修改的是usb_prop.c文件。首先要修改下這個文件只需要修改下DEVICE_PROP Device_Property裏面的內容,把MaxPacketSize域的大小改爲0x08,與設備描述符對應。如下:

DEVICE_PROP Device_Property =             //註冊一些CustomHID函數

{

    Keyboard_init,                 //Keyboard的初始化函數

    Keyboard_Reset,                //Keyboard的復位函數

    Keyboard_Status_In,                   //Keyboard狀態輸入函數

    Keyboard_Status_Out,           //Keyboard狀態輸出函數

    Keyboard_Data_Setup,           //Keyboard的處理有數據階段的特殊類請求函數

    Keyboard_NoData_Setup,         //Keyboard的處理沒有數據階段的特殊類請求函數

    Keyboard_Get_Interface_Setting,       //Keyboard獲取接口及備用接口設置(是否可用) 

    Keyboard_GetDeviceDescriptor,  //Keyboard獲取設備描述符

    Keyboard_GetConfigDescriptor,  //Keyboard獲取配置描述符

    Keyboard_GetStringDescriptor,  //Keyboard獲取字符串描述符

    0,                                                  //當前庫未使用

    0x08 /*MAX PACKET SIZE*/              //最大的包長度爲64字節

};

接下去要修改KeyBoard_Reset()函數。這個函數只要改下端點的配置,配置端點1輸出有次,及端點1輸入有效:

/*******************************************************************************

* Function Name  : Keyboard_Reset.

* Description    : Keyboard reset routine.復位

* Input          : None.

* Output         : None.

* Return         : None.

*******************************************************************************/

void Keyboard_Reset(void)

{

  /* Set KEYBOARD_DEVICE as not configured */

  pInformation->Current_Configuration = 0;             //設置當前的配置爲0,表示沒有配置過

  pInformation->Current_Interface = 0;                  //默認的接口

 

  /* Current Feature initialization */

  pInformation->Current_Feature = Keyboard_ConfigDescriptor[7];//當前的屬性,bmAttributes:設備的一些特性,0xc0表示自供電,不支持遠程喚醒

 

#ifdef STM32F10X_CL  

  /* EP0 is already configured in DFU_Init() by USB_SIL_Init() function */

 

  /* Init EP1 IN snd EP1 OUT as Interrupt endpoint */

  OTG_DEV_EP_Init(EP1_IN, OTG_DEV_EP_TYPE_INT, EP1_SIZE);

  OTG_DEV_EP_Init(EP1_OUT, OTG_DEV_EP_TYPE_INT, EP1_SIZE);

#else

 

  SetBTABLE(BTABLE_ADDRESS);

 

  /* Initialize Endpoint 0 */

  SetEPType(ENDP0, EP_CONTROL);             //設置端點1爲控制端點

  SetEPTxStatus(ENDP0, EP_TX_STALL);  //設置端點0發送延時

  SetEPRxAddr(ENDP0, ENDP0_RXADDR);         //設置端點0的接收緩衝區地址

  SetEPTxAddr(ENDP0, ENDP0_TXADDR);         //設置端點0的發送緩衝區地址

  Clear_Status_Out(ENDP0);                  //清除端點0的狀態

  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);//設置端點0的接收的計數

  SetEPRxValid(ENDP0);                             //使能接收狀態

 

  /* Initialize Endpoint 1 */

       SetEPType(ENDP1, EP_INTERRUPT);      //設置端點1爲中斷控制端點

       SetEPTxAddr(ENDP1, ENDP1_TXADDR); //設置端點1的發送緩衝地址

    SetEPTxCount(ENDP1, 8);                 //設置端點1的接收計數

//     SetEPRxStatus(ENDP1, EP_RX_DIS);  //設置端點1接收無效

       SetEPTxStatus(ENDP1, EP_TX_NAK);

 

       SetEPRxAddr(ENDP1, ENDP1_RXADDR); //設置接收數據的地址

       SetEPRxCount(ENDP1, 1);              //設置接收長度

       SetEPRxStatus(ENDP1, EP_RX_VALID);//設置端點有效,可以接收數據

//

    bDeviceState = ATTACHED;                //設置設備狀態爲 ATTACHED狀態

       /* Set this device to response on default address */

       SetDeviceAddress(0);                 //設置設備爲默認地址

#endif /* STM32F10X_CL */

 

       bDeviceState = ATTACHED;

}

這樣的話,端點就配置好了,我們還要編寫模擬鼠標的功能程序,這裏我使用四按鍵分別模擬鍵盤的。我們首先在hw_config.c中編寫按鍵引腳的配置函數USB_GPIO_Configuration()如下:

/*******************************************************************************

* Function Name  : USB_GPIO_Confguration

* Description    : USB相關引腳配置

* Input          : None.

* Output         : None.

* Return         : None.

*******************************************************************************/

void USB_GPIO_Confguration(void)

{

       GPIO_InitTypeDef  GPIO_InitStructure;

       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);

 

       /*上拉電阻引腳*/

       GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;

       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

       GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);

 

       /*KEY按鍵引腳配置*/

       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;

       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

       GPIO_Init(GPIOA, &GPIO_InitStructure);

      

       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ;

       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

       GPIO_Init(GPIOC, &GPIO_InitStructure);

      

       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;

       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

       GPIO_Init(GPIOD, &GPIO_InitStructure);

}

該函數在BSP.cBSP_Init()中調用。

接下去我們要 編寫按鍵掃描函數Keyboard_Scan(),這個函數不像鼠標工程裏的按鍵掃描函數一樣以檢測到按鍵就返回,這個函數需要檢測按鍵同時按下的情況,所以需要將所有的按鍵檢測一遍在返回,代碼如下:

/*******************************************************************************

* Function Name : Keyboard_Scan.

* Description   : 鍵盤按鍵掃描

* Input         : None.

* Output        : None.

* Return value  : 返回方向的值

*******************************************************************************/

uint8_t Keyboard_Scan(void)

{

       uint8_t temp=0;

       if(KEY_CTRL)           //Ctrl鍵按下

       {

              Delay_ms(50);                       

              if(KEY_CTRL)

              temp|=CTRL_B;

       }

       if(KEY_SHIFT)          //Shift鍵按下

       {

              Delay_ms(50);

              if(KEY_SHIFT)

              temp|=SHIFT_B;

       }

       if(KEY_CAPSLOCK)       //大小寫鍵按下

       {

              Delay_ms(50);

              if(KEY_CAPSLOCK)

              temp|=CAPSLOCK_B;

       }

       if(KEY_CHARA)          //字符'a'鍵按下

       {

              Delay_ms(50);

              if(KEY_CHARA)

              temp|=KEYA_B;

       }

       return temp;

}

之後還要編寫鍵值發送函數:

/*******************************************************************************

* Function Name : KeyBoard_Send.

* Description   : 發送keyboard的信息

* Input         : Keys: 檢測到被按下的按鍵值

* Output        : None.

* Return value  : None.

*******************************************************************************/

void Keyboard_Send(uint8_t key)

{

       uint8_t Keyboad_Buf[8]={0,0,0,0,0,0,0,0};

       uint8_t i=2;

       if(key&CTRL_B)                       //Ctrl是特殊鍵,在第一個字節的D0

       {

              Keyboad_Buf[0]|=0x01;

              printf("CTRL\r\n");

       }

       if(key&SHIFT_B)                      //Shift是特殊鍵,在第一個字節的D1

       {

              Keyboad_Buf[0]|=0x02;

              printf("SHIFT\r\n");

       }

       if(key&CAPSLOCK_B)            //Caps Lock

       {

              Keyboad_Buf[i++]=0x39;//HID用途表中代號是0x39

              printf("CAPS LOCK\r\n");

       }

       if(key&KEYA_B)                       //A             

       {

              Keyboad_Buf[i++]=0x04;//HID用途表中代號爲0x04

              printf("A\r\n");

       }

       USB_SIL_Write(EP1_IN, Keyboad_Buf, 8);

       SetEPTxValid(ENDP1);

}

這個函數有個參數key,我們通過Keyboard_Send(Keyboard_Scan())把掃描到的按鍵值傳遞到Keyboard_Send函數中,在該函數中根據鍵值來填充數組的值,如果檢測到ctrl鍵則置Keyboad_Buf[0]D0位爲1,如果檢測到時shift鍵,則置Keyboad_Buf[0]D11。如果是檢測到是Caps lock鍵或其他鍵,置Keyboad_Buf[2]開始的字節。

最後要編寫main函數了,main()函數很簡答就是在while(1)中掃描按鍵,如果有按鍵按下,則調用Keyboard_Send()發送函數,發送按鍵值:

/********************************************************

函數:main()

描述:程序入口地址

參數:無

返回:無

********************************************************/

int main(void)

{ 

       BSP_Init();

       printf(" |===============================================|\r\n");

       printf("                USB Keyboard   程序開始           \r\n");

    printf("|===============================================|\r\n");

       while (bDeviceState != CONFIGURED);

       while(1)

       {

              if (bDeviceState == CONFIGURED)                         //如果USB已將配置好了

           {

              /* 檢測按鍵狀態,併發送鼠標位置數據 */

               if (Keyboard_Scan()!= 0)                         //檢查是否有按鍵按下

               {

                      Keyboard_Send(Keyboard_Scan());           //如果有,發送鍵值

               }

              else                                                         //沒有按鍵按下

              {

                Keyboard_Send(0);         //發送空數據,如果不發,USB主機會認爲你一直在鍵入上次的鍵值

               }

           }

       }

}

這裏需要注意的是,每次發送鍵值後,都要發送8字節的空數據到USB主機中,否則,我們舉個例子,我按下A鍵,結果USB主機收到後就會在屏幕上輸入'a',但是會一直不停得輸入’a‘,這個問題當時也困擾我許久,最後才發現,原來USB主機會一直保持上次收到的數據進行操作,我們上次按下'a',則電腦就會一直打印'a'不停,所以在發送完按鍵值後,需要在發送空數據給USB主機。

 

最後,我還是貼出我的readme.txt讓大家更詳細瞭解下程序軟硬件:

===================================================================================

下載方式

===================================================================================

SWD JTAG

 

 

===================================================================================

程序功能

===================================================================================

模擬鍵盤的功能,這裏模擬了4個按鍵的功能,分別爲:ctrl,shift,caps lock,A這四個鍵。

 

 

===================================================================================

硬件連接

===================================================================================

神州III號開發板:

ctrl鍵:開發板上的WAKEUP鍵,連接PA0

shift鍵:開發板上的TEMPER鍵,連接PC13

caps lock鍵:開發板上的USER1鍵,連接PA8

A鍵:開發板上的USER2鍵,連接PD3

LED1:開發板上的標號爲OS1LED,大小寫鎖鍵對應的LED

 

===================================================================================

軟件設置

===================================================================================

連接串口2,打開串口調試助手,選擇好相應的COM

  按如下進行設置:

  ----------------

  波特率 | 115200 |

  ----------------

  數據位 |   8    |

  ----------------

  停止位 |   1    |

  ----------------

  校驗位 | None   |

  ----------------

  流控制 | None   |

  ----------------

 

===================================================================================

實驗現象

===================================================================================

按下開發板模擬的ctrl鍵,配合電腦鍵盤上的按鍵,比如ctrl+c,ctrl+v等,實現一些功能。

按下開發板模擬的shift鍵,配合模擬的A鍵,就可以輸出大寫A

當然,同時按下ctrl+shift鍵,實現輸入法的切換

按下開發板模擬的caps Lock鍵,就能進行大小寫切換,大寫時,開發板上的LED1亮,小寫時滅

按下開發板模擬的A鍵,就能輸入a

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章