網站導航:首頁開源項目 USB開源項目:Easy USB 51 Programer  

目錄導航

   
  1、項目簡介
USB開發基礎
1、USB接口的優點及開發難易度
2、USB設備的開發流程
3、USB接口芯片(USB控制器)的選擇
4、了解USB的通訊過程
5、USB命令(請求)和USB描述符
6、實例講解USB的枚舉(配置)過程
准備工作
1、需要哪些工具
2、電路原理圖
3、手工制作您的電路板
4、測試您的硬件
HID設備類
1、讓PC機找到我們的硬件
2、如何成爲一個HID設備(模擬鼠標)
3、如何成爲一個HID設備(模擬鍵盤)
4、如何與HID設備通訊(一)
5、如何與HID設備通訊(二)
6、51編程器的實現
Windows USB 驱动程序(自定义设备)
1、Windows驅動開發基礎
2、開發環境配置
3、第一个实例-Hello Wdm(一)
4、第一个实例-Hello Wdm(二)
5、真正的實例—驅動我們的實驗板
6、真正的實例—測試驅動程序
7、真正的實例—控制LED及讀取按鍵狀態
8、如何編寫應用程序
   

相关産品    淘寶網店
     
 

 
  更多...  
 
 
如何成爲一個HID設備(模擬鍵盤) 查看/参与此開源項目相关讨论
 

1、基礎知識

  通過《USB HID 设备类协议入门》一文和上一節的實例我們知道決定HID設備“身份”的因素有

1)5個標准描述符中與HID設備有關的部分有:

  • 設備描述符中bDeviceClass、bDeviceSubClass和bDeviceProtocol三個字段的值必須爲零。
  • 接口描述符中bInterfaceClass的值必须为0x03,bInterfaceSubClass的值为0或1,为1表示HID设备符是一个启动设备(Boot Device,一般对PC机而言才有意义,意思是BIOS启动时能识别并使用您的HID设备,且只有标准鼠標或鍵盤类设备才能成为Boot Device。 bInterfaceProtocol的取值含義如下表所示: 

    HID接口描述符中bInterfaceProtocol的含義
    bInterfaceProtocol的取值(十進制) 含義
    0 NONE
    1 鼠標
    2 鍵盤
    3~255 保留

2)HID設備的描述符除了5個USB的標准描述符(設備描述符、配置描述符、接口描述符、端點描述符、字符串描述符,見百合電子工作室的另一篇文章:USB開發基礎--USB命令(请求)和USB描述符)外,还包括3个HID設備類特定描述符:HID描述符、报告描述符、实体描述符。

2、在上一節实例的基础上作一些修改来将Easy USB 51 Programer改造成鍵盤

1)下載上一節實例

2)修改接口描述符中bInterfaceProtocol的值爲0x02

在Descriptor.c中找到以下代碼

 
  1. 1,                          //bInterfaceProtocol为1代表鼠標  

將其修改爲

 
  1. 2,                                      //bInterfaceProtocol为2代表鍵盤  

 3)在上一節中也提到“虽然我们将接口描述符中的bInterfaceProtocol设为1(代表鼠標),但这只是针对启动设备(Boot Device)而言有才有效果(即PC机的BIOS加载后能识别和使用),但真正对HID设备的数据流格式进行描述的是报告描述符,所以 bInterfaceProtocol的取值实际意义不大”,所以现在我们来修改报告描述符

在Descriptor.c中找到以下代碼:

 
  1. code char MouseReportDescriptor[52] = {   
  2.     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)   
  3.     0x09, 0x02,                    // USAGE (Mouse)   
  4.     0xa1, 0x01,                    // COLLECTION (Application)   
  5.     0x09, 0x01,                    //   USAGE (Pointer)   
  6.     0xa1, 0x00,                    //   COLLECTION (Physical)   
  7.     0x05, 0x09,                    //     USAGE_PAGE (Button)   
  8.     0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)   
  9.     0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)   
  10.     0x15, 0x00,                    //     LOGICAL_MINIMUM (0)   
  11.     0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)   
  12.     0x95, 0x03,                    //     REPORT_COUNT (3)   
  13.     0x75, 0x01,                    //     REPORT_SIZE (1)   
  14.     0x81, 0x02,                    //     INPUT (Data,Var,Abs)   
  15.     0x95, 0x01,                    //     REPORT_COUNT (1)   
  16.     0x75, 0x05,                    //     REPORT_SIZE (5)   
  17.     0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)   
  18.     0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)   
  19.     0x09, 0x30,                    //     USAGE (X)   
  20.     0x09, 0x31,                    //     USAGE (Y)   
  21.     0x09, 0x38,                    //     USAGE (Wheel)   
  22.     0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)   
  23.     0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)   
  24.     0x75, 0x08,                    //     REPORT_SIZE (8)   
  25.     0x95, 0x03,                    //     REPORT_COUNT (3)   
  26.     0x81, 0x06,                    //     INPUT (Data,Var,Rel)   
  27.     0xc0,                          //   END_COLLECTION   
  28.     0xc0                           // END_COLLECTION   
  29. };  

將其修改爲

 
  1. code char MouseReportDescriptor[63] = {   
  2.     //表示用途頁爲通用桌面設備   
  3.     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)   
  4.   
  5.     //表示用途为鍵盤   
  6.     0x09, 0x06,                    // USAGE (Keyboard)   
  7.        
  8.     //表示應用集合,必須要以END_COLLECTION來結束它,見最後的END_COLLECTION   
  9.     0xa1, 0x01,                    // COLLECTION (Application)   
  10.        
  11.     //表示用途頁爲按鍵   
  12.     0x05, 0x07,                    //   USAGE_PAGE (Keyboard)   
  13.   
  14.     //用途最小值,這裏爲左ctrl鍵   
  15.     0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)   
  16.     //用途最大值,這裏爲右GUI鍵,即window鍵   
  17.     0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)   
  18.     //邏輯最小值爲0   
  19.     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)   
  20.     //邏輯最大值爲1   
  21.     0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)   
  22.     //報告大小(即這個字段的寬度)爲1bit,所以前面的邏輯最小值爲0,邏輯最大值爲1   
  23.     0x75, 0x01,                    //   REPORT_SIZE (1)   
  24.     //報告的個數爲8,即總共有8個bits   
  25.     0x95, 0x08,                    //   REPORT_COUNT (8)   
  26.     //输入用,变量,值,绝对值。像鍵盤这类一般报告绝对值,   
  27.     //而鼠標移动这样的则报告相对值,表示鼠標移动多少   
  28.     0x81, 0x02,                    //   INPUT (Data,Var,Abs)   
  29.     //上面這這幾項描述了一個輸入用的字段,總共爲8個bits,每個bit表示一個按鍵   
  30.     //分別從左ctrl鍵到右GUI鍵。這8個bits剛好構成一個字節,它位于報告的第一個字節。   
  31.     //它的最低位,即bit-0對應著左ctrl鍵,如果返回的數據該位爲1,則表示左ctrl鍵被按下,   
  32.     //否則,左ctrl鍵沒有按下。最高位,即bit-7表示右GUI鍵的按下情況。中間的幾個位,   
  33.     //需要根據HID協議中規定的用途頁表(HID Usage Tables)來確定。這裏通常用來表示   
  34.     //特殊鍵,例如ctrl,shift,del鍵等   
  35.     
  36.   
  37.     //這樣的數據段個數爲1   
  38.     0x95, 0x01,                    //   REPORT_COUNT (1)   
  39.     //每個段長度爲8bits   
  40.     0x75, 0x08,                    //   REPORT_SIZE (8)   
  41.     //輸入用,常量,值,絕對值   
  42.     0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)   
  43.        
  44.     //上面這8個bit是常量,設備必須返回0   
  45.   
  46.   
  47.     //這樣的數據段個數爲5   
  48.     0x95, 0x05,                    //   REPORT_COUNT (5)   
  49.     //每個段大小爲1bit   
  50.     0x75, 0x01,                    //   REPORT_SIZE (1)   
  51.     //用途是LED,即用来控制鍵盤上的LED用的,因此下面会说明它是输出用   
  52.     0x05, 0x08,                    //   USAGE_PAGE (LEDs)   
  53.     //用途最小值是Num Lock,即數字鍵鎖定燈   
  54.     0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)   
  55.     //用途最大值是Kana,這個是什麽燈我也不清楚^_^   
  56.     0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)   
  57.     //如前面所說,這個字段是輸出用的,用來控制LED。變量,值,絕對值。   
  58.     //1表示燈亮,0表示燈滅   
  59.     0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)   
  60.   
  61.     //這樣的數據段個數爲1   
  62.     0x95, 0x01,                    //   REPORT_COUNT (1)   
  63.     //每個段大小爲3bits   
  64.     0x75, 0x03,                    //   REPORT_SIZE (3)   
  65.     //輸出用,常量,值,絕對   
  66.     0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)       
  67.     //由于要按字節對齊,而前面控制LED的只用了5個bit,   
  68.     //所以後面需要附加3個不用bit,設置爲常量。    
  69.   
  70.     //報告個數爲6   
  71.     0x95, 0x06,                    //   REPORT_COUNT (6)   
  72.     //每個段大小爲8bits   
  73.     0x75, 0x08,                    //   REPORT_SIZE (8)   
  74.     //邏輯最小值0   
  75.     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)   
  76.     //邏輯最大值255   
  77.     0x25, 0xFF,                    //   LOGICAL_MAXIMUM (255)   
  78.     //用途頁爲按鍵   
  79.     0x05, 0x07,                    //   USAGE_PAGE (Keyboard)   
  80.     //使用最小值爲0   
  81.     0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))   
  82.     //使用最大值爲0x65   
  83.     0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)   
  84.     //輸入用,變量,數組,絕對值   
  85.     0x81, 0x00,                    //   INPUT (Data,Ary,Abs)   
  86.     //以上定義了6個8bit寬的數組,每個8bit(即一個字節)用來表示一個按鍵,所以可以同時   
  87.     //有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致鍵盤扫描系统   
  88.     //無法區分按鍵時,則全部返回0x01,即6個0x01。如果有一個鍵按下,則這6個字節中的第一   
  89.     //個字節爲相應的鍵值(具體的值參看HID Usage Tables),如果兩個鍵按下,則第1、2兩個   
  90.     //字節分別爲相應的鍵值,以次類推。   
  91.   
  92.     //關集合,跟上面的對應   
  93.     0xc0                           // END_COLLECTION   
  94. };  

這們還需要將Descriptor.h中的以下代碼

 
  1. extern code char MouseReportDescriptor[52];  

修改爲

 
  1. extern code char MouseReportDescriptor[63];  

   上面的報告描述符中只有一個報告,所以沒有報告ID,
因此返回的都是實際使用的數據共有8字節輸入,1字節輸出。其中輸入的
第一字節用來表示特殊按鍵,第二字節保留,後面的六字節爲普通按鍵。如果
只有左ctrl键按下,则返回01 00 00 00 00 00 00 00(十六进制),如果
只有数字键1 按下,则返回00 00 59 00 00 00 00 00,如果数字
键1 和2 同时按下,则返回00 00 59 5A 00 00 00 00,如果
再按下左shift 键,则返回02 00 59 5A 00 00 00 00,
然后再释放1   键,则返回02 00 5A 00 00 00 00 00,
然后全部按键释放,则返回00 00 00 00 00 00 00 00。
这些数据(即报告)都是通过中断端点返回的。当按下Num Lock键时,PC会发送
输出报告,从报告描述符中我们知道,Num Lock的LED对应着输出报告的最低位,
当数字小鍵盤打开时,输出xxxxxxx1(二进制,打x的由其它的LED状态决定);
当数字小鍵盤关闭时,输出xxxxxxx0(同前)。取出最低位就可以控制数字键锁定LED了。

 (注:以上說明摘自computer00的《USB HID報告及報告描述符簡介》一文)

5)將Descriptor中如下語句

 
  1. 0x66,0x02,                      //设备制造商定的産品ID  

修改爲

 
  1. 0x66,0x03,                      //设备制造商定的産品ID  

 

4)模拟NumLock鍵盤和Windows键

  我們定義擴展板EXT-BOARD-A上的K1鍵對應Windows鍵(即USB HID Usage Table中定义的GUI键,Left GUI或Right GUI,我们这里就选Left GUI吧),而K2鍵盤对应NumLock键,当NumLock使能时应点亮NumLock指示灯,我定义D0为NumLock指示灯。

  根据报告描述符的定义,再参考USB HID Usage Table,要模拟Windows鍵盤,应将送给主机的8个字节的第一个字节置为0x04,要模拟NumLock键,应将第三个字节置为0x53。如果要模拟NumLock指示灯,不应该在主控芯片里判断当到K2按下后就打开或熄灭LED,其正确的方法是:当主机接收到NumLock按键信息后,会根据系统当前NumLock的状态决定打开还是关闭LED指示灯,然后将这一信息通过传给设备(发送一个字节的数据给设置,最低位表示NumLock的状态)。

更改Main.c文件中的main函數爲:

 
  1. void main()   
  2. {      
  3.     unsigned char i = 0;   
  4.     signed char cKeyIn[8];   
  5.     static bit bKeyPressed  = 0;        //鍵按下標志,防止重入   
  6.        
  7.     if (Init_D12()!=0)                  //初始化D12   
  8.         return;                         //如果初始化不成功,返回   
  9.   
  10.     IT0 = 0;                            //外部中斷0爲電平觸發方式   
  11.        
  12.     EX0 = 1;                            //開外部中斷0   
  13.     PX0 = 0;                            //設置外部中斷0中斷優先級   
  14.     EA  = 1;                                //開80C51總中斷   
  15.        
  16.     P0  = 0;   
  17.   
  18.     while(1)   
  19.     {   
  20.         usbserve();                     //處理USB事件   
  21.         if(bEPPflags.bits.configuration)   
  22.         {   
  23.             //在這裏添加端點操作代碼                      
  24.             if(bEPPflags.bits.ep2_rxdone )  //主端點接收到數據(從主機發往設備的數據)   
  25.             {   
  26.                 bEPPflags.bits.ep2_rxdone       = 0;   
  27.                    
  28.                 //判斷NumLock狀態   
  29.                 if(EpBuf[0] & 0x01) //EpBuf爲接收緩沖   
  30.                 {   
  31.                     P0  = 0x01;    
  32.                 }   
  33.                 else  
  34.                 {   
  35.                     P0  = 0x00;   
  36.                 }                  
  37.                            
  38.             }   
  39.                
  40.             K1  = 1;        //P3.5   
  41.             K2  = 1;        //P3.6   
  42.                
  43.             for(i=0;i<100;i++); //延時    
  44.                 
  45.             if(~K1 & K2)    //K1按下(模擬左Windows鍵)   
  46.             {   
  47.                 if(!bKeyPressed)       
  48.                 {   
  49.                     bKeyPressed = 1;   
  50.                        
  51.                     cKeyIn[0]=0x08;        
  52.                     cKeyIn[1]=0;            //保留   
  53.                     cKeyIn[2]=0;               
  54.                     cKeyIn[3]=0;   
  55.                     cKeyIn[4]=0;   
  56.                     cKeyIn[5]=0;   
  57.                     cKeyIn[6]=0;   
  58.                     cKeyIn[7]=0;           
  59.                        
  60.                     D12_WriteEndpoint(5,8,cKeyIn);          //發8個字節到PC機   
  61.                 }      
  62.             }   
  63.             else if(K1 & ~K2)   //K2按下(模擬NumLock鍵)   
  64.             {   
  65.                 if(!bKeyPressed)       
  66.                 {   
  67.                     bKeyPressed = 1;   
  68.                        
  69.                     cKeyIn[0]=0;           
  70.                     cKeyIn[1]=0;            //保留   
  71.                     cKeyIn[2]=0x53;            
  72.                     cKeyIn[3]=0;   
  73.                     cKeyIn[4]=0;   
  74.                     cKeyIn[5]=0;   
  75.                     cKeyIn[6]=0;   
  76.                     cKeyIn[7]=0;           
  77.                        
  78.                     D12_WriteEndpoint(5,8,cKeyIn);          //發8個字節到PC機   
  79.                 }   
  80.             }   
  81.             else if(~K1 & ~K2)  //K1和K2同時按下(Window和NumLock同時按下)   
  82.             {   
  83.                    
  84.                 if(!bKeyPressed)       
  85.                 {   
  86.                     bKeyPressed = 1;   
  87.                        
  88.                     cKeyIn[0]=0x08;        
  89.                     cKeyIn[1]=0;            //保留   
  90.                     cKeyIn[2]=0x53;            
  91.                     cKeyIn[3]=0;   
  92.                     cKeyIn[4]=0;   
  93.                     cKeyIn[5]=0;   
  94.                     cKeyIn[6]=0;   
  95.                     cKeyIn[7]=0;           
  96.                        
  97.                     D12_WriteEndpoint(5,8,cKeyIn);          //發8個字節到PC機   
  98.                 }   
  99.             }   
  100.             else if(K1 & K2)   
  101.             {   
  102.                 if(bKeyPressed)        
  103.                 {   
  104.                     bKeyPressed = 0;   
  105.                        
  106.                     cKeyIn[0]=0;           
  107.                     cKeyIn[1]=0;            //保留   
  108.                     cKeyIn[2]=0;               
  109.                     cKeyIn[3]=0;   
  110.                     cKeyIn[4]=0;   
  111.                     cKeyIn[5]=0;   
  112.                     cKeyIn[6]=0;   
  113.                     cKeyIn[7]=0;           
  114.                        
  115.                     D12_WriteEndpoint(5,8,cKeyIn);          //發8個字節到PC機   
  116.                 }   
  117.             }      
  118.            
  119.         }   
  120.     }   
  121. }  

  在测试这个例子时,按下EXT-BOARD-A上的K1键,会弹出开始菜单,按K2键,D0的状态会改变,同时原有鍵盤上的NumLock指示灯也会同EXT-BOARD-A上的D0状态同步,相反,按原有鍵盤上的NumLock键,D0的状态也会跟着改变。

下載源代碼

 
 
 
本站程序由百合電子工作室开发和维护
Copyright @ baihe electric studio
渝ICP備09006681號-4