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

2023-10-30

同上一篇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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 有没有办法通过浏览器与 USB 设备通信?

    有没有办法在常规浏览器 FF Safari IE8 等 中与 USB 设备通信 我认为它可能是 Javascript gt Java 桥或其他东西 请指教 P S 是否可以以最小的依赖性实现 如果没有 基于浏览器的软件的全部意义就毫无用处
  • AS3 USB 游戏控制器支持

    如何让 USB 控制器控制我的 AS3 游戏 我现在正在使用键盘 我考虑过映射按键 但我必须为此使用一个插件 我只是希望用户能够购买一个不需要任何驱动程序下载 配置的 简单控制器 他们应该能够插入 USB 控制器并通过浏览器中的 Flash
  • 无法使用 OpenCV 从辅助网络摄像头的 VideoCapture 读取帧

    Code 与主网络摄像头 设备 0 完美配合的简单示例 VideoCapture cap 0 if cap isOpened std cout lt lt Unable to read stream from specified devic
  • USB 传输数据

    我正在尝试通过 USB 发送和接收数据 我的设备 Acer Iconia A500 拥有连接到设备所需的一切 一切都很好并且工作正常 但是当我尝试发送和接收数据时 它的行为并不像预期的 这是我的代码 for this is the main
  • Java Android 激光条码扫描器

    我正在尝试开发一个能够读取条形码 1d 而不是 qr 的应用程序 真正的问题是 常见的条形码阅读器软件非常有用 但它们似乎太慢 无法作为非常频繁和常见的功能使用 这些条形码相当长 有 20 30 位数字 对于 zxing 等应用程序来说似乎
  • 如何在 OSX Catalina (10.15) 中使用 Wireshark 捕获 USB 流量

    我正在尝试让 Wireshark USB 捕获工作 显然 Catalina 之前的技巧就是调出界面 以便 Wireshark 可以看到它 https forums developer apple com thread 95380 https
  • 软件和与外部设备串行通信的波特率限制

    我使用 USB 端口作为虚拟 COM 端口运行 以使用 MATLAB 或 Visual Basic 6 实现与外部设备的串行通信 我面临波特率限制 具体取决于我用于与设备通信的软件 MATLAB 2018a 没有问题 因为它可以设置高波特率
  • 使用 C# 向 USB 连接的 GPRS 调制解调器发送 AT 命令

    任何人都可以给我一个关于如何访问连接到 USB 端口的 GPRS 调制解调器的良好指示或指南 我应该为我的程序制作一个 USB 驱动程序以将 AT 命令发送到调制解调器吗 或者就像一个路由器 我可以使用IP地址访问它 谢谢 如果 Windo
  • 通过 USB 记忆棒携带和操作整个开发盒。可行的?

    最近我一直在考虑投资一个有价值的 USB 笔式驱动器 类似于 并在虚拟机上安装操作系统并开始在其上进行开发 我的想法是 我希望能够携带我的开 发箱 成为一名Windows 发行版用于 Net 开发和Linux发行版像 RoR Perl 之类
  • Android 上的 HP TouchPad USB 驱动

    我可以在运行的 HP TouchPad 上进行应用程序开发吗氰基模组 http en wikipedia org wiki CyanogenMod 9 我有一个安装了 CyanogenMod 9 的 HP TouchPad 并且正在尝试从E
  • Android:与充当主机的 USB 设备通信

    我们制作了一个可以充当 USB 主机或从机并处理其接收到的数据的设备 我想使用Android手机通过USB向其发送数据 我的研究使我得出结论 如果不修改硬件或操作系统 我们就无法在 USB 主机模式下使用 Android 设备 这样做不是一
  • 通过Android USB主机与智能卡读卡器通信

    我正在尝试向智能卡发送命令 我用一个金雅拓 IDBridge CT30 PC TWIN 读卡器 http www smartcardfocus com images content GEMTR jpg and a ID桥K30 http c
  • 如何对 USB/HID/蓝牙适配器进行逆向工程。使用什么工具?

    需要对蓝牙USB鼠标适配器进行逆向工程 并使用鼠标板测量距离 并用绘图库 http matplotlib org 我发现这些工具可以帮助完成此任务 Linux lsusb usbmon usbhid转储 rfcomm hidrd 转换 xx
  • Windows 8.1 上的 Pyusb - 没有可用的后端 - 如何安装 libusb?

    使用 pyinstaller 3 1 python 2 7 9 和 tkinter 尝试使用 pyusb 而不是 pyserial 但没有可用的后端 调查Windows 上的 Pyusb 没有可用的后端 https stackoverflo
  • 我无法找到 libftdi 的 mpsse (SPI) 模式的示例代码

    这不是一个家庭作业问题 尽管这是一个工作问题 几个月前 我刚写完一份规范 老板就会把它外包出去 因为资金紧张 所以我正在尝试自己做这件事 我是一个很弱的 C 编码员 如果 gcc 能输出一些运行时不会出现段错误的东西 或者有时根本没有任何东
  • 使用 STM32 USB 设备库将闪存作为大容量存储设备

    我的板上有这个闪存IC 它连接到我的STM32F04 ARM处理器 处理器的USB端口可供用户使用 我希望我的闪存在通过 USB 连接到 PC 时被检测为存储设备 作为第一步 我在程序中将 USB 类定义为 MSC 效果很好 因为当我将主板
  • LibUsb 声明接口访问被拒绝 Java

    我希望能够从 USB 计步器读取数据 我正在 Java 中尝试此操作 并且使用 LibUsb 和 Usb4Java 库 我似乎无法认领 USB 管道或类似的东西 我正在使用的代码 final Context context new Cont
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • Android 两次请求 USB 许可

    我有一个运行 Android 4 0 4 的开发板 并将其用作 USB 配件 当我关闭板上的应用程序然后通过 USB 插入 Android 手机时 板会提示 连接此 USB 设备时打开 AppName 当我触摸 确定 时 应用程序启动并再次
  • 使用 iTextSharp 5.3.3 和 USB 令牌签署 PDF

    我是 iTextSharp 和 StackOverFlow 的新手 我正在尝试使用外部 USB 令牌在 C 中签署 PDF 我尝试使用从互联网上挖掘的以下代码 Org BouncyCastle X509 X509CertificatePar

随机推荐

  • android自定义弹出框样式实现

    前言 做项目时 感觉android自带的弹出框样式比较丑 很多应用都是自己做的弹出框 这里也试着自己做了一个 废话不说先上图片 实现机制 1 先自定义一个弹出框的样式 2 自己实现CustomDialog类 继承自Dialog 实现里面方法
  • Java多线程中join方法的理解

    thread Join把指定的线程加入到当前线程 可以将两个交替执行的线程合并为顺序执行的线程 比如在线程B中调用了线程A的Join 方法 直到线程A执行完毕后 才会继续执行线程B t join 使调用线程 t 在此之前执行完毕 t joi
  • 布尔盲注之sqlmap爆破

    布尔盲注之sqlmap爆破 好久没有更新了 语句也忘的差不多了 作为菜鸟的我也是终于弄懂了这道题 以及部分语句的意思 我将用尽可能详细且易理解的语言来表达 sqlmap爆破 首先你得下载sqlmap csdn中刚好有 注入语句 python
  • 图片像素、英寸、厘米之间的单位换算

    转自 http hi baidu com cjg501 blog item f040fc0898d5379f0b7b8244 html 今天朋友用photoshop处理图片时要把图片保存指定的大小 但她只对厘米要形像感 可是在软件里保存的图
  • Maven+Idea打jar包个人总结

    Maven Idea打jar包个人总结 文章目录 Maven Idea打jar包个人总结 1 前言 2 安装配置JDK 3 Maven安装和配置 3 1 下载安装maven 3 2 配置环境变量 3 3 修改maven配置文件并创建本地仓库
  • BigDecimal除法向上取整,保留 1 为小数(Java、MySQL各自写法)

    SQL写法 CEILING SUM hdd SEND OUT QTY hsl DEMAND QTY 1000 10 EXECUTE RATE Java写法 updateData getExecuteQty divide soLineQtyM
  • 阿里云实践 - HTML5断点播放m3u8视频(videojs)

    场景 HTML5页面需要通过
  • EMC问题之RE实验最优解

    EMC问题之RE实验最优解 RE实验中 最关键的可能也是最难的就是找到干扰源 进而确定是传导辐射还是空间辐射 选择对应的是一定要加屏蔽罩 哪怕环路面积很小 辐射能量还是很强 还是仅仅依靠滤波等就能解决问题 实验现象 在160MHz 80MH
  • shell中如何进行一段代码的注释

    在shell编程中 我们常常需要把一些语句注释掉 让它不执行 对单号或者少数几行shell脚本来说 在每行前面增加 符号就可以达到目的了 代码如下 cp a txt b txt mkdir p 1 2 4 2 4 6 echo ok 但如果
  • ARP欺骗和DNS劫持以及Wireshark分析

    一 实验目的 利用ettercap进行中间人攻击之ARP欺骗和DNS劫持 用Wireshark分析相关特征数据 提高对ettercap Wireshark的熟练度 同时也对中间人攻击有更加深入的认识 二 实验原理 常见的ARP欺骗方式有两种
  • 使用conda时出现Solving environment: failed with initial frozen solve. Retrying with flexible solve错误

    使用conda安装pytorch 出现了各种各样的错误 尝试了网上各种办法 最后我是这么解决的 首先添加镜像源 在终端运行以下代码 conda config add channels https mirrors tuna tsinghua
  • JDBC(数据库连接)

    JDBC 简介 什么是 JDBC JDBC 指 Java 数据库连接 是一种标准Java应用编程接口 JAVA API 用来连接 Java 编程语言和广泛的数据库 JDBC API 库包含下面提到的每个任务 都是与数据库相关的常用用法 制作
  • 学习开源项目NewBeeMall新蜂商城(1) - 初步了解与运行NewBeeMall

    文章目录 0 前言 1 NewBeeMall 新蜂商城简介 2 NewBeeMall项目配置与运行 2 1 配置MySQL数据库 2 2 配置图片资源 2 3 运行NewBeeMAll 3 NewBeeMall相关技术栈 3 1 项目原版技
  • 关于++与+=

    今天在模仿别人做购物车网页 本想用jquey的text 获取一件物品的数量 如 1 由于忽略了text 获取的是字符串 于是用了 1运算符 发现字符串也能用 输出为 11 看了模仿的网站发现他并不需要用到parseInt 函数来将字符串变为
  • 454. 4Sum II 解题记录

    题目描述 Given four lists A B C D of integer values compute how many tuples i j k l there are such that A i B j C k D l is z
  • MOSFET 导通条件

    MOSFET管是FET的一种 可以被制造为增强型或者耗尽型 P沟道或N沟道共四种类型 但实际应用的只有增强型的N沟道MOS管和增强型的P沟道MOS管 实际应用中 NMOS居多 如何分辨三个极 D极单独位于一边 而G极是第4PIN 剩下的3个
  • 支持IDE最新版!新一代报表工具FastReport VCL v6.7更新详情

    FastReport VCL是用于Delphi C Builder RAD Studio和Lazarus的报告和文档创建VCL库 它提供了可视化模板设计器 可以访问最受欢迎的数据源 报告引擎 预览 将过滤器导出为30多种格式 并可以部署到云
  • 激光条纹中心线提取算法总结和复现

    滤波 分割等预处理过程省略 输入图像为灰度图 激光条纹水平走向 目录 几何中心法 极值法 细化法 灰度重心法 法向质心法 Steger算法 几何中心法 检测出光条边界 l h 后 把两边界的中间线 l h 2作为激光条纹的中心线 inclu
  • Unity访问 FTP-SSL、FTP服务器(记录)

    1 ftps是基于ftp做了层加密 只记录ftps的访问 2 使用的工具 FluentFTP 由于直接导入unity访问文件会报安全句柄错误 因此需要以下操作 3 流程 下载FluentFTP 修改 cs文件 重新编译 dll 原因 Saf
  • 入门级详细USB移植教程——致正在为USB烦恼的朋友

    同上一篇MPU6050一样 我还是写一篇关于USB的帖子 在圈圈等玩USB的大神面前 我掌握的USB知识实在是九牛一毛 所以这篇帖子加上了入门级的修饰语 写这篇帖子主要是为了那些想快速开发USB的人 至于想深入了解USB协议 可以先学完我这