入门级详细USB移植教程——致正在为USB烦恼的朋友

usb 专栏收录该内容
24 篇文章 1 订阅

同上一篇MPU6050一样,我还是写一篇关于USB的帖子,在圈圈等玩USB的大神面前,我掌握的USB知识实在是九牛一毛,所以这篇帖子加上了入门级的修饰语。写这篇帖子主要是为了那些想快速开发USB的人,至于想深入了解USB协议,可以先学完我这个再去看看别的高级教程可能会好点,虽然我强调自己掌握的USB知识不多,但是对于一般的应用已经足够,我这里主要是学会怎么去用USB做自己的东西,而且我觉得刚学完一个东西就来教下别人实在是一个一举两得的做法,因为我更清楚学习时的心理和一些小问题,而且我也可以进一步总结。我有个观点不知道其他大神中不中听,好像圈圈大神就搞了USB几年达到了精通的水平,可能一些几个月达到了很懂的水平,但是这个过程实在是很漫长,对于很多人也没有必要对USB进行彻底的研究,只要会用就好,就像你可能不懂车的高级原理,但是你会开车就好了,^_^。其他大神不同意的话,写个高级的USB教程让我们学习下吧。这里其实也是个人学习笔记。
记得一个月前,我想做无线鼠标,开始在网上查找资料,从那个时候开始学习USB,找了圈圈的书将前面足足看了3、4遍,还只是瞎子摸象,不过瞎子摸象很正常啦,学什么东西一开始哪里可能能够弄清全局。看了3、4编,实在是很煎熬,一直没有什么进展,没有搞出点成果。就开始上网找资料,让瞎子摸象来得更彻底一点。

如果学过I2C的话,可能对USB理解会更透彻点,两者在某些方面还是有共通之处的,某种意义上来说,STM32的USB跟硬件I2C有点像,89C52的USB介于软件I2C和硬件I2C之间,89C52有了USB芯片的协助后,很多时序不用自己模拟,但是编程使用芯片搞USB这过程就跟软件I2C一样的蛋疼。首先STM32是有USB的两个数据端口的,首先看上面的这张图,是野火ISO板子USB原理图部分,一个VCC,一个GND连接到USB设备,充电的时候就是这两个起作用,供电的作用。D+和D-就是差分数据线,ID我们一般没有用到,是用来设备和主机之间的识别用到,有时候两台机器可以做设备和主机就靠这根线来区别。主要是三极管这部分,由电路图可以知道PD3口要给低电平上拉电阻才能接到D+,应该这里在D-还要有一个三极管和上拉电路,不然这里默认了所接的设备一定是全速设备和高速设备,不是低速设备,不知道接低速设备有没有事。USB运行的前提是要上拉电阻接通才行喔,这里不直接接上拉的原因就是可以自己控制,你可以再程序中控制这里来断开USB连接然后再接上去,选择较多,当然也有直接接上去的。除了STM32,89c52和STC12等单片机也可以搞USB,圈圈就是用89C52来搞的,但是89C52是没有像STM32那样子内置USB外设的,那么就需要加多1块USB芯片,还有相应的外围电路,但是STM32就不用,好方便的说。”主机在检测到设备接入后,会执行设备识别,这个过程比较麻烦,它的枚举过程包含了设备的一些相关信息与通信方式。 “这个过程我觉得如果不是要真正研究USB的话,也没有必要看,看了搞不好神经错乱。加上ST官方库的USB例程后,STM32搞USB难度降低了几个等级。ST官方有个JoyStickMouse的例程,就是通过摇杆来控制鼠标。我们接下来是用按键来控制鼠标。
官方的源文件地址:http://pan.baidu.com/s/1eQuyL8I
原子的触控USB鼠标实验就是其中一个很好的教程,很通俗,很符合初学者心理,可惜讲解得太简短了,不懂触摸屏的可以直接忽略它的触摸屏部分,
教程地址:http://pan.baidu.com/s/1jGuW9EI   
工程地址:http://pan.baidu.com/s/1bnD1IZd
USB2.0协议中文版: http://pan.baidu.com/s/1bnndwYV
关于 STM32F102/103 的USB模块和USB库函数:http://pan.baidu.com/s/1kT7LLhd这是官方对库的说明:http://pan.baidu.com/s/1ntmdbsP
USB英文文档:http://pan.baidu.com/s/1nt6vLs1
USB描述符: http://pan.baidu.com/s/1kTkLlf5
圈圈教你玩USB----PDF: http://pan.baidu.com/s/1ntoKSwL
基于STM32 的USB程序开发笔记(看不懂,不过好像很牛逼):http://pan.baidu.com/s/1kTkLlKb 
USB源代码分析(很详细的)http://pan.baidu.com/s/1o6mbbyq
USB鼠标工程(网上找的)圈圈USB资料合集:
首先说明的是你在过程里面看到很多的类似usmart,如果这些都是个人调试程序用的,如果你想进一步了解就看下我这个贴子,不想就直接忽略好了。http://www.amobbs.com/thread-5582408-1-1.html
记住,在网上找到一些USB例程无法直接使用在你的板子上面的原因之一就是硬件的原因,所以才需要稍微了解下原理才能根据自己的板子改装。本帖子会包括:STM32鼠标移植,STM32键盘移植,89C52鼠标移植,89C52键盘移植,可能后续还会有其他的USB作品的移植。

STM32-USB鼠标移植
1.首先你需要按照原子教程下面说的添加文件

这里顺便说下几个文件,大致了解下就好啦
上传一个USB的讲解文档,写得不错。这里面首先是讲解了USB的一些基本知识,如果全部懂了的话,那么USB已经算是学得很好了。鼠标改键盘也有,还有关于具体库函数的详解,看完觉得很赞!!!下面有些内容引用自这个文件!!
文件地址: http://pan.baidu.com/s/1dDw2Hep
usb_desc.c: 提供了设备、端点、接口、字符串、群组、制造商描述符(本来想在这里讲解下描述符的,但是描述符在这里的作用不大明显,到USB键盘那块的时候再讲解吧)

问题八:在标准的 USB 请求命令中,经常会看到 Descriptor,这是什么来的呢?
回答八:Descriptor 即描述符,是一个完整的数据结构,可以通过 C 语言等编程实现,并存
储在 USB 设备中,用于描述一个 USB 设备的所有属性,USB 主机是通过一系列命令来要
求设备发送这些信息的。它的作用就是通过如问答节中的命令***作来给主机传递信息,从
而让主机知道设备具有什么功能、属于哪一类设备、要占用多少带宽、使用哪类传输方式及
数据量的大小,只有主机确定了这些信息之后,设备才能真正开始工作,所以描述符也是十
分重要的部分,要好好掌握。标准的描述符有 5 种,USB 为这些描述符定义了编号:
1——设备描述符
2——配置描述符
3——字符描述符
4——接口描述符
5——端点描述符
上面的描述符之间有一定的关系,一个设备只有一个设备描述符,而一个设备描述符可以包
含多个配置描述符,而一个配置描述符可以包含多个接口描述符,一个接口使用了几个端点,
就有几个端点描述符。这间描述符是用一定的字段构成的,详细说明见上面文档的10页。

简单理解里面就是USB设备的一些描述,像档案。
usb_prop.c: 提供了 Device_Property(性能),Device_Table &USER_STANDARD_REQUEST(请求)结构描述,这 3 个东西定义于 usb_core.c,摇杆鼠标的一些处理过程
hw_config.c: 提供了实际硬件需要的操作函数,Joystick_Send()通过函数UserToPMABufferCopy 和  SetEPTxValid 将坐标值发给了 USB 端口。
STM32f10x_it.c:里面有中断处理函数,千万别忽略里面的内容。
usb_core.c:USB 总线数据处理的核心文件,标准协议。
usb_init.c,usb_int.c:用于端点数据输入输入中断处理
usb_mem.c:用于缓冲区操作
usb_regs.c:用于寄存器操作

2.接下来要在main里面配置USB

详细步骤如下:
1. 官方例函数Set_System(); 用途: 配置好系统时钟等,按键IO口,以及上面我们提到的接通上拉电阻的那个管脚,USB管脚等等,
这个函数根据作用分为以下几个部分:
系统时钟配置部分(可以省略)
开启上拉时钟管脚的时钟,然后配置该管脚,这个管脚( USB_DISCONNECT_PIN )应该找到它定义的地方,将它改为你板子上面对应的管脚,可以用Ctrl+F找,我们找到它所在的地方如下图,因为这里涉及条件编译,所以你需要找到你对应编译部分的全部改掉。我需要将它改成PD3.

如果你的板子上面没有对应的管脚,只有类似下面的一个跳帽,就需要接上跳帽,接上去之后不要管这部分了,跟你都已经没有关系了。


  1.   /* Enable the USB disconnect GPIO clock */
  2.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);

  3.   /* USB_DISCONNECT used as USB pull-up */
  4.   GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
  5.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  6.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  7.   GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
复制代码
配置USB管脚,不知道为什么没有设置USB还是可以工作。
  1. /*Set PA11,12 as IN - USB_DM,DP*/
  2.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  3.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
  4.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  5.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  6.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  7.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  8.   GPIO_Init(GPIOA, &GPIO_InitStructure);
  9.     
  10.   /*SET PA11,12 for USB: USB_DM,DP*/
  11.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_14);
  12.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_14);
复制代码
配置按键等端口,官方的库是配置摇杆的4个管脚。
  1.   /*Enable Joystick GPIOs clock*/
  2.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
  3.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  4.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
  5.   /*Configure the JoyStick IOs as input floating*/
  6.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4 | 
  7.                                 GPIO_Pin_9 | GPIO_Pin_10;
  8.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  9.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /*PullDown is mandatory for Joystick pins*/
  10.   GPIO_Init(GPIOF, &GPIO_InitStructure);

  11.   /*Configure the JoyStick IOs as input floating*/
  12.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_5;                  
  13.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  14.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /*PullDown is mandatory for Joystick pins*/
  15.   GPIO_Init(GPIOB, &GPIO_InitStructure); 
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  
  17.   GPIO_Init(GPIOF, &GPIO_InitStructure);
  18.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;  
  19.   GPIO_Init(GPIOE, &GPIO_InitStructure);
复制代码
配置USB中断向量,USB传输数据过程需要中断,这个是重中之重,你可以不设置上面其他的东西,但是这个必须设置,别人就会出现,后面自己设置这个,可能多次出现这个,很大原因是中断设置的问题。
  1.   EXTI_ClearITPendingBit(EXTI_Line18);
  2.   EXTI_InitStructure.EXTI_Line = EXTI_Line18;
  3.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  4.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  5.   EXTI_Init(&EXTI_InitStructure);
复制代码

上面几个步骤直接简化USB中断向量的设置,所以我在我的程序屏蔽了这个官方函数,直接自己写个,然后放在配置的最前面。
  1. EXTI_InitTypeDef EXTI_InitStructure;
  2. void USB_IT_Config(void)
  3. {
  4.         EXTI_ClearITPendingBit(EXTI_Line18);
  5.   EXTI_InitStructure.EXTI_Line = EXTI_Line18;
  6.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  7.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  8.   EXTI_Init(&EXTI_InitStructure);
  9. }
复制代码
2.官方例 函数 USB_Interrupts_Config();  作用:初始化USB中断,更准确的解释是: 开启USB唤醒中断和USB低优先级数据处理中断
3. 官方例 函数 Set_USBClock()       作用:开启USB时钟, 更准确的解释是: 配置USB 时钟,也就是从72M 的主频得到48M的USB 时钟(1.5 分频)
4. 官方例 函数 USB_Init();         作用:USB硬件初始化, 更准确的解释是: 用于初始化USB,最主要的就是调用了Joystick_init函数,开启了USB部分的电源等
这里面有个很重要的东西要说下
  1. void USB_Init(void)
  2. {
  3.   pInformation = &Device_Info;
  4.   pInformation->ControlState = 2;
  5.   pProperty = &Device_Property;
  6.   pUser_Standard_Requests = &User_Standard_Requests;
  7.   /* Initialize devices one by one */
  8.   pProperty->Init();
  9. }
复制代码
&Device_Property是跟 pProperty  一样的类型的结构体指针,结构体类型定义如下,不看也可以
  1. typedef struct _DEVICE_PROP
  2. {
  3.   void (*Init)(void);        /* Initialize the device */
  4.   void (*Reset)(void);       /* Reset routine of this device */

  5.   /* Device dependent process after the status stage */
  6.   void (*Process_Status_IN)(void);
  7.   void (*Process_Status_OUT)(void);

  8.   /* Procedure of process on setup stage of a class specified request with data stage */
  9.   /* All class specified requests with data stage are processed in Class_Data_Setup
  10.    Class_Data_Setup()
  11.     responses to check all special requests and fills ENDPOINT_INFO
  12.     according to the request
  13.     If IN tokens are expected, then wLength & wOffset will be filled
  14.     with the total transferring bytes and the starting position
  15.     If OUT tokens are expected, then rLength & rOffset will be filled
  16.     with the total expected bytes and the starting position in the buffer

  17.     If the request is valid, Class_Data_Setup returns SUCCESS, else UNSUPPORT

  18.    CAUTION:
  19.     Since GET_CONFIGURATION & GET_INTERFACE are highly related to
  20.     the individual classes, they will be checked and processed here.
  21.   */
  22.   RESULT (*Class_Data_Setup)(uint8_t RequestNo);

  23.   /* Procedure of process on setup stage of a class specified request without data stage */
  24.   /* All class specified requests without data stage are processed in Class_NoData_Setup
  25.    Class_NoData_Setup
  26.     responses to check all special requests and perform the request

  27.    CAUTION:
  28.     Since SET_CONFIGURATION & SET_INTERFACE are highly related to
  29.     the individual classes, they will be checked and processed here.
  30.   */
  31.   RESULT (*Class_NoData_Setup)(uint8_t RequestNo);

  32.   /*Class_Get_Interface_Setting
  33.    This function is used by the file usb_core.c to test if the selected Interface
  34.    and Alternate Setting (uint8_t Interface, uint8_t AlternateSetting) are supported by
  35.    the application.
  36.    This function is writing by user. It should return "SUCCESS" if the Interface
  37.    and Alternate Setting are supported by the application or "UNSUPPORT" if they
  38.    are not supported. */

  39.   RESULT  (*Class_Get_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting);

  40.   uint8_t* (*GetDeviceDescriptor)(uint16_t Length);
  41.   uint8_t* (*GetConfigDescriptor)(uint16_t Length);
  42.   uint8_t* (*GetStringDescriptor)(uint16_t Length);

  43.   /* This field is not used in current library version. It is kept only for 
  44.    compatibility with previous versions */
  45.   void* RxEP_buffer;
  46.    
  47.   uint8_t MaxPacketSize;

  48. }DEVICE_PROP;
复制代码
Device_Property这个结构体里面有多个我们已经定义好的函数,如下:
  1. DEVICE_PROP Device_Property =
  2.   {
  3.     Joystick_init,
  4.     Joystick_Reset,
  5.     Joystick_Status_In,
  6.     Joystick_Status_Out,
  7.     Joystick_Data_Setup,
  8.     Joystick_NoData_Setup,
  9.     Joystick_Get_Interface_Setting,
  10.     Joystick_GetDeviceDescriptor,
  11.     Joystick_GetConfigDescriptor,
  12.     Joystick_GetStringDescriptor,
  13.     0,
  14.     0x40 /*MAX PACKET SIZE*/
  15.   };
复制代码
在语句pProperty = &Device_Property; 和 pProperty->Init();之后,就运行了这个函数Joystick_init,里面有再看PowerOn,里面又有USB_Cable_Config(ENABLE);好不容易找到我们想要的函数。这个就是断开和连接上拉电阻的函数,前面我们只是打开时钟和配置这个管脚而已,同样的他对应的管脚是USB_DISCONNECT和USB_DISCONNECT_PIN,前面如果将这两个改了,就不要改了。
  1. /*******************************************************************************
  2. * Function Name  : USB_Cable_Config.
  3. * Description    : Software Connection/Disconnection of USB Cable.
  4. * Input          : NewState: new state.
  5. * Output         : None.
  6. * Return         : None
  7. *******************************************************************************/
  8. void USB_Cable_Config (FunctionalState NewState)
  9. {
  10. #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
  11.   if (NewState != DISABLE)
  12.   {
  13.     STM32L15_USB_CONNECT;
  14.   }
  15.   else
  16.   {
  17.     STM32L15_USB_DISCONNECT;
  18.   }  
  19.    
  20. #else
  21.   if (NewState != DISABLE)
  22.   {
  23.     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
  24.   }
  25.   else
  26.   {
  27.     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
  28.   }
  29. #endif /* STM32L1XX_MD */
  30. }
复制代码
这样子很麻烦,我就自己写了个函数 USB_Power_On();//给上拉电阻的三极管导通,我觉得这个是最重要的,如果你上拉电阻都没有连接上去,那还谈什么其他的。
要让你的电脑识别这是个鼠标的话,还需要配置中端函数,官方函数写在stm32_it.c这个文件里面,这个也是重点,我找了很久才知道在中断函数里面还有这样子的东西,相关代码如下:
  1. /******************************************************************************/
  2. /*            STM32 Peripherals Interrupt Handlers                            */
  3. /*******************************************************************************
  4. * Function Name  : USB_IRQHandler
  5. * Description    : This function handles USB Low Priority interrupts
  6. *                  requests.
  7. * Input          : None
  8. * Output         : None
  9. * Return         : None
  10. *******************************************************************************/
  11. #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS) || defined(STM32F37X)
  12. void USB_LP_IRQHandler(void)
  13. #else
  14. void USB_LP_CAN1_RX0_IRQHandler(void)
  15. #endif
  16. {
  17.   USB_Istr();
  18. }

  19. #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)

  20. /*******************************************************************************
  21. * Function Name  : USB_FS_WKUP_IRQHandler
  22. * Description    : This function handles USB WakeUp interrupt request.
  23. * Input          : None
  24. * Output         : None
  25. * Return         : None
  26. *******************************************************************************/
  27. void USB_FS_WKUP_IRQHandler(void)
  28. {
  29.   EXTI_ClearITPendingBit(EXTI_Line18);
  30. }
  31. #endif
  32. /*******************************************************************************
  33. * Function Name  : EXTI_IRQHandler
  34. * Description    : This function handles External lines  interrupt request.
  35. * Input          : None
  36. * Output         : None
  37. * Return         : None
  38. *******************************************************************************/
  39. #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
  40. void EXTI0_IRQHandler(void)
  41. #elif defined (STM32F37X)
  42. void EXTI2_TS_IRQHandler(void)
  43. #else
  44. void EXTI9_5_IRQHandler(void)
  45. #endif
  46. {
  47.   if (EXTI_GetITStatus(KEY_BUTTON_EXTI_LINE) != RESET)
  48.   {
  49.     /* Check if the remote wakeup feature is enabled (it could be disabled 
  50.         by the host through ClearFeature request) */
  51.     if (pInformation->Current_Feature & 0x20) 
  52.     {      
  53.       pInformation->Current_Feature &= ~0x20;  
  54.       /* Exit low power mode and re-configure clocks */
  55.       Resume(RESUME_INTERNAL);
  56.     }
  57.   
  58.     /* Clear the EXTI line pending bit */
  59.     EXTI_ClearITPendingBit(KEY_BUTTON_EXTI_LINE);
  60.   }
  61. }

  62. /*******************************************************************************
  63. * Function Name  : USBWakeUp_IRQHandler
  64. * Description    : This function handles USB WakeUp interrupt request.
  65. * Input          : None
  66. * Output         : None
  67. * Return         : None
  68. *******************************************************************************/
  69. void USBWakeUp_IRQHandler(void)
  70. {
  71.   EXTI_ClearITPendingBit(EXTI_Line18);
  72. }
复制代码
我将他们移植到我的stm32f10x_it.c文件里面,实际代码如下:
  1. /*******************************************************************************
  2. * Function Name  : USBWakeUp_IRQHandler
  3. * Description    : This function handles USB WakeUp interrupt request.
  4. * Input          : None
  5. * Output         : None
  6. * Return         : None
  7. *******************************************************************************/
  8. void USBWakeUp_IRQHandler(void)
  9. {
  10.   EXTI_ClearITPendingBit(EXTI_Line18);
  11. }
  12. /*******************************************************************************
  13. * Function Name  : USB_IRQHandler
  14. * Description    : This function handles USB Low Priority interrupts
  15. *                  requests.
  16. * Input          : None
  17. * Output         : None
  18. * Return         : None
  19. *******************************************************************************/
  20. void USB_LP_CAN1_RX0_IRQHandler(void)
  21. {
  22.   USB_Istr();
  23. }
复制代码
上面的东西移植好了的话,成功的话你会发现在你的电脑上多了一个鼠标设备。其实这个过程远远没有我上面描述的那么简单,上面只是几个小问题而已,你会遇到很多文件编译的问题,慢慢解决吧。怎么说呢?编译的问题很多,我不想写出来,留给大家慢慢去学习下怎么移植程序吧,部分问题可以看下这个帖子:
http://www.openedv.com/posts/list/33515.htm?privmsg=21890&&sysid=20#192355
3.真正控制鼠标
实际要控制鼠标还要看死循环里面的这些函数

  1.     if (bDeviceState == CONFIGURED)
  2.     {
  3.       if ((JoyState() != 0) && (PrevXferComplete))
  4.       {
  5.         Joystick_Send(JoyState());
  6.       }
  7.     }
复制代码
主要是Joystick_Send(JoyState());这个函数,JoyState获取摇杆的动作之后发给Joystick_Send进行处理,CURSOR_STEP是鼠标移动的距离,这个过程简单地说就是需要单片机要发送一些东西给电脑,什么东西呢?Mouse_Buffer[4] 里面的东西,这四个元素分别作用是:Mouse_Buffer[0]的D0就是左键,D1就是右键,D2是中键,Mouse_Buffer[1]为x轴,你发送个负数鼠标就左移相应的距离,正数右移,Mouse_Buffer[2]为y轴,Mouse_Buffer[3为滚轮。由这些解释我们可以知道Joystick_Send这个函数就是先判断遥感的方向,然后发送相应的键值过去罢了。左右按键可以看下我之前遇到的一个问题,你会对这部分有更深的理解,后面有我的解释。
请教在设计USB键盘的时候怎么才能达到一直按下的效果: http://www.amobbs.com/thread-5581955-1-1.html
  1. void Joystick_Send(uint8_t Keys)
  2. {
  3.   uint8_t Mouse_Buffer[4] = {0, 0, 0, 0};
  4.   int8_t X = 0, Y = 0;
  5.   
  6.   switch (Keys)
  7.   {
  8.     case JOY_LEFT:
  9.       X -= CURSOR_STEP;
  10.       break;
  11.     case JOY_RIGHT:

  12.       X += CURSOR_STEP;
  13.       break;
  14.     case JOY_UP:
  15.       Y -= CURSOR_STEP;
  16.       break;
  17.     case JOY_DOWN:
  18.       Y += CURSOR_STEP;
  19.       break;
  20.     default:
  21.       return;
  22.   }
  23.   /* prepare buffer to send */
  24.   Mouse_Buffer[1] = X;
  25.   Mouse_Buffer[2] = Y;
  26.   
  27.   /* Reset the control token to inform upper layer that a transfer is ongoing */
  28.   PrevXferComplete = 0;
  29.   
  30.   /* Copy mouse position info in ENDP1 Tx Packet Memory Area*/
  31.   USB_SIL_Write(EP1_IN, Mouse_Buffer, 4);
  32.   
  33.   /* Enable endpoint for transmission */
  34.   SetEPTxValid(ENDP1);

  35. }
复制代码
鼠标的移植到这里就结束了。如果你是野火ISO的板子,那么下载我的工程下去后,按下KEY1鼠标左移,KEY2鼠标右移。
工程地址: http://pan.baidu.com/s/1jGMaXU2

STM32-USB键盘移植
放上一个山寨的电脑遥感飞车的视频,使用的就是USB键盘的原理。
http://v.youku.com/v_show/id_XNzE5MTM3NDUy.html
被山寨的作品的帖子: http://www.amobbs.com/thread-5531209-1-1.html 成本:
目前成本:100块
元件清单:STM32最小系统+MPU6050+几根杜邦线+USB线
成本最低可降至:40块。
最低成本元件清单:2元STC12单片机+25元USB模块(可能可以降至更低,自己做的话)+MPU6050(最低10块),可能企业生产的话会便宜很多。
涉及的软硬件知识:
1.USB通讯协议,用来模拟键盘用,这个最坑
2.I2C通讯协议,用来获取MPU6050数据用
3.MPU6050的使用
4.STM32单片机的使用
5.串口方面的东西,用来调试MPU6050输出数据
项目细节介绍:
1.下面这两句是精华,可以看一个文档,就知道这里面的奥秘,从获取的各轴加速度大概算出MPU6050的姿态。高中物理知识啊!! http://pan.baidu.com/s/1jGzajLs
  1.         Roll =  (((atan2(temp1[2],temp1[0])*180)/3.1416)-90); //x轴角度
  2.         Pitch = (((atan2(temp1[2],temp1[1])*180)/3.1416)-90); //y轴角度
复制代码

2.MPU6050可以看这个我发的帖子: http://www.amobbs.com/thread-5581033-1-1.html
放上工程文件: http://pan.baidu.com/s/1bn3Yg3t  , 板子是野火ISO。

怎么将上面的鼠标改成键盘呢?鼠标和键盘在程序上的区别是什么?总体上来说,两者都是HID设备,很大部分都是一样,所以程序很大部分一样。不一样就是鼠标是鼠标,键盘是键盘,细节的东西不一样。可以参见两篇帖子,一篇是上面的飞车
一篇是圈圈的:
http://www.amobbs.com/thread-1387906-1-1.html
有人将其内容整理成文档,地址如下:
http://pan.baidu.com/s/1i3j3uEL
不过一开始读他们的帖子感觉还是很难懂。设备描述符可以结合上面的那个USB基础知识去看,多少页?都说是10页那里了。^__^这些比较繁琐,而且都有这么多资料了,大就慢慢看吧
按下某个键就相当于在按照相应的格式发送相应键的值过去,这些键的值就在这个文档里面,HID键盘代码: http://pan.baidu.com/s/1kThxxNh


from: https://www.amobbs.com/thread-5583091-1-1.html?_dsign=b0f4c123

  • 2
    点赞
  • 0
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值