同上一篇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就不用,好方便的说。”主机在检测到设备接入后,会执行设备识别,这个过程比较麻烦,它的枚举过程包含了设备的一些相关信息与通信方式。C
      “这个过程我觉得如果不是要真正研究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.

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

/* Enable the USB disconnect GPIO clock */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);

/* USB_DISCONNECT used as USB pull-up */

GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;

GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
      复制代码配置USB管脚,不知道为什么没有设置USB还是可以工作。

/*Set PA11,12 as IN - USB_DM,DP*/

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/*SET PA11,12 for USB: USB_DM,DP*/

GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_14);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_14);
      复制代码配置按键等端口,官方的库是配置摇杆的4个管脚。

/*Enable Joystick GPIOs clock*/

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);

/*Configure the JoyStick IOs as input floating*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4 |

GPIO_Pin_9 | GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /*PullDown is mandatory 
        for Joystick pins*/

GPIO_Init(GPIOF, &GPIO_InitStructure);

/*Configure the JoyStick IOs as input floating*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_5;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /*PullDown is mandatory 
        for Joystick pins*/

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

GPIO_Init(GPIOF, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_Init(GPIOE, &GPIO_InitStructure);

复制代码配置USB中断向量,USB传输数据过程需要中断,这个是重中之重,你可以不设置上面其他的东西,但是这个必须设置,别人就会出现,后面自己设置这个,可能多次出现这个,很大原因是中断设置的问题。

EXTI_ClearITPendingBit(EXTI_Line18);

EXTI_InitStructure.EXTI_Line = EXTI_Line18;

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure);
      复制代码
      上面几个步骤直接简化USB中断向量的设置,所以我在我的程序屏蔽了这个官方函数,直接自己写个,然后放在配置的最前面。

EXTI_InitTypeDef EXTI_InitStructure;

void USB_IT_Config(void)

{

EXTI_ClearITPendingBit(EXTI_Line18);

EXTI_InitStructure.EXTI_Line = EXTI_Line18;

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure);

}
      复制代码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部分的电源等
      这里面有个很重要的东西要说下

void USB_Init(void)

{

pInformation = &Device_Info;

pInformation->ControlState = 2;

pProperty = &Device_Property;

pUser_Standard_Requests = &User_Standard_Requests;

/* Initialize devices one by one */

pProperty->Init();

}
      复制代码&Device_Property是跟pProperty 一样的类型的结构体指针,结构体类型定义如下,不看也可以

typedef struct _DEVICE_PROP

{

void (*Init)(void);        /* Initialize the device */

void (*Reset)(void);       /* Reset routine of this device */

/* Device dependent process after the status stage */

void (*Process_Status_IN)(void);

void (*Process_Status_OUT)(void);

/* Procedure of process on setup stage of a class specified request 
        with data stage */

/* All class specified requests with data stage are processed in 
        Class_Data_Setup

Class_Data_Setup()

responses to check all special requests and fills ENDPOINT_INFO

according to the request

If IN tokens are expected, then wLength & wOffset will be filled

with the total transferring bytes and the starting position

If OUT tokens are expected, then rLength & rOffset will be filled

with the total expected bytes and the starting position in the 
buffer

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

CAUTION:

Since GET_CONFIGURATION & GET_INTERFACE are highly related to

the individual classes, they will be checked and processed here.

*/

RESULT (*Class_Data_Setup)(uint8_t RequestNo);

/* Procedure of process on setup stage of a class specified request 
        without data stage */

/* All class specified requests without data stage are processed in 
        Class_NoData_Setup

Class_NoData_Setup

responses to check all special requests and perform the request

CAUTION:

Since SET_CONFIGURATION & SET_INTERFACE are highly related to

the individual classes, they will be checked and processed here.

*/

RESULT (*Class_NoData_Setup)(uint8_t RequestNo);

/*Class_Get_Interface_Setting

This function is used by the file usb_core.c to test if the selected 
        Interface

and Alternate Setting (uint8_t Interface, uint8_t AlternateSetting) 
        are supported by

the application.

This function is writing by user. It should return "SUCCESS" if the 
        Interface

and Alternate Setting are supported by the application or "UNSUPPORT" 
        if they

are not supported. */

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

uint8_t* (*GetDeviceDescriptor)(uint16_t Length);

uint8_t* (*GetConfigDescriptor)(uint16_t Length);

uint8_t* (*GetStringDescriptor)(uint16_t Length);

/* This field is not used in current library version. It is kept only 
        for

compatibility with previous versions */

void* RxEP_buffer;

uint8_t MaxPacketSize;

}DEVICE_PROP;

复制代码Device_Property这个结构体里面有多个我们已经定义好的函数,如下:

DEVICE_PROP Device_Property =

{

Joystick_init,

Joystick_Reset,

Joystick_Status_In,

Joystick_Status_Out,

Joystick_Data_Setup,

Joystick_NoData_Setup,

Joystick_Get_Interface_Setting,

Joystick_GetDeviceDescriptor,

Joystick_GetConfigDescriptor,

Joystick_GetStringDescriptor,

0,

0x40 /*MAX PACKET SIZE*/

};
      复制代码在语句pProperty = &Device_Property; 和 
      pProperty->Init();之后,就运行了这个函数Joystick_init,里面有再看PowerOn,里面又有USB_Cable_Config(ENABLE);好不容易找到我们想要的函数。这个就是断开和连接上拉电阻的函数,前面我们只是打开时钟和配置这个管脚而已,同样的他对应的管脚是USB_DISCONNECT和USB_DISCONNECT_PIN,前面如果将这两个改了,就不要改了。

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

* Function Name  : USB_Cable_Config.

* Description    : Software Connection/Disconnection of USB Cable.

* Input          : NewState: new state.

* Output         : None.

* Return         : None

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

void USB_Cable_Config (FunctionalState NewState)

{

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

if (NewState != DISABLE)

{

STM32L15_USB_CONNECT;

}

else

{

STM32L15_USB_DISCONNECT;

}

#else

if (NewState != DISABLE)

{

GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);

}

else

{

GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);

}

#endif /* STM32L1XX_MD */

}
      复制代码这样子很麻烦,我就自己写了个函数USB_Power_On();//给上拉电阻的三极管导通,我觉得这个是最重要的,如果你上拉电阻都没有连接上去,那还谈什么其他的。
      要让你的电脑识别这是个鼠标的话,还需要配置中端函数,官方函数写在stm32_it.c这个文件里面,这个也是重点,我找了很久才知道在中断函数里面还有这样子的东西,相关代码如下:

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

/*            STM32 Peripherals Interrupt Handlers                       
             */

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

* Function Name  : USB_IRQHandler

* Description    : This function handles USB Low Priority interrupts

*                  requests.

* Input          : None

* Output         : None

* Return         : None

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

#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| 
        defined(STM32L1XX_MD_PLUS) || defined(STM32F37X)

void USB_LP_IRQHandler(void)

#else

void USB_LP_CAN1_RX0_IRQHandler(void)

#endif

{

USB_Istr();

}

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

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

* Function Name  : USB_FS_WKUP_IRQHandler

* Description    : This function handles USB WakeUp interrupt request.

* Input          : None

* Output         : None

* Return         : None

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

void USB_FS_WKUP_IRQHandler(void)

{

EXTI_ClearITPendingBit(EXTI_Line18);

}

#endif

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

* Function Name  : EXTI_IRQHandler

* Description    : This function handles External lines  interrupt 
        request.

* Input          : None

* Output         : None

* Return         : None

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

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

void EXTI0_IRQHandler(void)

#elif defined (STM32F37X)

void EXTI2_TS_IRQHandler(void)

#else

void EXTI9_5_IRQHandler(void)

#endif

{

if (EXTI_GetITStatus(KEY_BUTTON_EXTI_LINE) != RESET)

{

/* Check if the remote wakeup feature is enabled (it could be 
        disabled

by the host through ClearFeature request) */

if (pInformation->Current_Feature & 0x20)

{

pInformation->Current_Feature &= ~0x20;

/* Exit low power mode and re-configure clocks */

Resume(RESUME_INTERNAL);

}

/* Clear the EXTI line pending bit */

EXTI_ClearITPendingBit(KEY_BUTTON_EXTI_LINE);

}

}

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

* Function Name  : USBWakeUp_IRQHandler

* Description    : This function handles USB WakeUp interrupt request.

* Input          : None

* Output         : None

* Return         : None

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

void USBWakeUp_IRQHandler(void)

{

EXTI_ClearITPendingBit(EXTI_Line18);

}

复制代码我将他们移植到我的stm32f10x_it.c文件里面,实际代码如下:

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

* Function Name  : USBWakeUp_IRQHandler

* Description    : This function handles USB WakeUp interrupt request.

* Input          : None

* Output         : None

* Return         : None

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

void USBWakeUp_IRQHandler(void)

{

EXTI_ClearITPendingBit(EXTI_Line18);

}

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

* Function Name  : USB_IRQHandler

* Description    : This function handles USB Low Priority interrupts

*                  requests.

* Input          : None

* Output         : None

* Return         : None

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

void USB_LP_CAN1_RX0_IRQHandler(void)

{

USB_Istr();

}
      复制代码上面的东西移植好了的话,成功的话你会发现在你的电脑上多了一个鼠标设备。其实这个过程远远没有我上面描述的那么简单,上面只是几个小问题而已,你会遇到很多文件编译的问题,慢慢解决吧。怎么说呢?编译的问题很多,我不想写出来,留给大家慢慢去学习下怎么移植程序吧,部分问题可以看下这个帖子:
      http://www.openedv.com/posts/list/33515.htm?privmsg=21890&&sysid=20#192355
      3.真正控制鼠标
      实际要控制鼠标还要看死循环里面的这些函数

if (bDeviceState == CONFIGURED)

{

if ((JoyState() != 0) && (PrevXferComplete))

{

Joystick_Send(JoyState());

}

}
      复制代码主要是Joystick_Send(JoyState());这个函数,JoyState获取摇杆的动作之后发给Joystick_Send进行处理,CURSOR_STEP是鼠标移动的距离,这个过程简单地说就是需要单片机要发送一些东西给电脑,什么东西呢?Mouse_Buffer[4]v
      里面的东西,这四个元素分别作用是: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

void Joystick_Send(uint8_t Keys)

{

uint8_t Mouse_Buffer[4] = {0, 0, 0, 0};

int8_t X = 0, Y = 0;

switch (Keys)

{

case JOY_LEFT:

X -= CURSOR_STEP;

break;

case JOY_RIGHT:

X += CURSOR_STEP;

break;

case JOY_UP:

Y -= CURSOR_STEP;

break;

case JOY_DOWN:

Y += CURSOR_STEP;

break;

default:

return;

}

/* prepare buffer to send */

Mouse_Buffer[1] = X;

Mouse_Buffer[2] = Y;

/* Reset the control token to inform upper layer that a transfer is 
        ongoing */

PrevXferComplete = 0;

/* Copy mouse position info in ENDP1 Tx Packet Memory Area*/

USB_SIL_Write(EP1_IN, Mouse_Buffer, 4);

/* Enable endpoint for transmission */

SetEPTxValid(ENDP1);

}

复制代码鼠标的移植到这里就结束了。如果你是野火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

Roll =  (((atan2(temp1[2],temp1[0])*180)/3.1416)-90); //x轴角度

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
    var html = document.getElementById("artContent").innerHTML; 
    document.getElementById("artContent").innerHTML = html;

入门级详细USB移植教程相关推荐

  1. 基于STM32F4的CANOpen移植教程(超级详细)

    CANopen移植到STM32F4平台 前言 1 物品准备 2 相关软件安装 2.1 CAN上位机 2.2 对象字典生成工具objdictedit环境配置 3 将CANopen移植到STM32F407 ...

  2. 嵌入式数据库Sqlite移植教程-转

    嵌入式数据库Sqlite移植教程 sqlite-3.3.6编译安装与交叉编译全过程详细记录 本文介绍的内容都是基于 Linux RedHat 9.0 平台的. 一.PC机编译安装 请阅读在安装包里的 ...

  3. win7开机卡在正在启动_手把手教你大白菜PE启动盘安装win7最详细的图解教程

    一.准备工作,制作好PE启动盘U盘一个,把所需要的文件提前复制到U盘里: 2,把启动盘插到电脑USB插口上: 3-1,按下电脑主机开机按钮: 3-2,如果电脑是打开的请重启电脑: 4,设置从U盘启动: ...

  4. rtl8723bu linux wifi驱动移植教程

    rtl8723bu linux 驱动移植教程 将文件 rtl8723BU_WiFi_linux_v4.3.9.3_13200.20150106_BTCOEX20140929-5443.tar.gz 拷 ...

  5. 电脑密码解锁教程详细版图文教程

    电脑密码解锁教程详细版图文教程 第一步制作启动优盘及网站 第二部进入优盘pe 选择优盘启动项后选择Win10PE 成功进入pe后选择密码修改工具 最后电脑重启拔掉优盘,输入最新修改的密码就解锁完成 第 ...

  6. 百度云ROM移植教程

    这是基于MTK平头的  ,应该高通的移植起来差不多 百度云ROM移植教程(基于MTK6577) 百度云已经有了MTK6577平台的ROM A789,基于A789,我们可以将百度云ROM移植到其他的MT ...

  7. 《全面掌握Linux C语言嵌入式系统移植教程》学习笔记(Liunx速查简明)

    全面掌握Linux C语言嵌入式系统移植教程学习笔记(Liunx速查简明) 笔记前言: P3: shell命令简明 P4: vi /vim编辑器入门 P5:vi /vim编辑器扩展 P8: gcc编译 ...

  8. ROS 不能再详细的安装教程

    ROS 不能再详细的安装教程 版权声明:本文为博主原创文章,转载请标明出处: http://www.cnblogs.com/liu-fa/p/5779206.html 关于ROS(Robot OS 机 ...

  9. 超强、超详细Redis入门教程【转】

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使用r ...

  10. System之Ubuntu:VMware虚拟机 Ubuntu安装详细过程(图文教程,最强攻略,步骤详细,建议收藏)

    System之Ubuntu:VMware虚拟机 Ubuntu安装详细过程(图文教程,最强攻略,步骤详细,建议收藏) 导读         不是每一个程序员都必须玩过linux,只是博主觉得现在的很多服 ...

最新文章

  1. CentOS6.9中搭建FTP服务器
  2. mysql错误消息1_MySQL出错信息: Subquery returns more than 1 row及其解决方法
  3. 解决oh my zsh Insecure completion-dependent directories detected
  4. php打开目录文件类型,php中打开目录并输出目录文件实现代码
  5. Windows10电脑系统时间校准
  6. android图片适配到裁剪框,Android图片剪裁-调用系统实现,完美适配魅族等机型
  7. 1105学习笔记 数组的算法上
  8. Mysql基础代码(不断完善中)
  9. H3C 路由器 与 CISCO 路由器在配置上的差别
  10. 压缩JavaScript的宏
  11. 地籍数据属性质检功能设计与实现
  12. Solidity 系列教程
  13. 奔图打印机显示未连接_打印机连接正常,但是没反应
  14. Hadoop单机版安装
  15. 写入clickhouse效率低总结
  16. 小程序JSAPI预下单与回调
  17. atomic原子类实现机制_atomic实现原理
  18. android ibinder类接口编辑
  19. lavavel 环境配置 summer版
  20. 国内镜像加速 Android 源码下载

热门文章

  1. 韩顺平 jdbc 之 mysql,(韩顺平讲解)jdbc学习(四)---java连接mysql
  2. mysql web报表_2021最新流行的Web报表工具推荐
  3. vue展示日历 考勤展示_vue实现简单的日历效果
  4. python 24点题目分析_24点游戏的递归解法和Python实现
  5. esp8266 at接收数据中断时间_关于嵌入式系统中断优先级的一点思考
  6. 下划线_Excel添加下划线的快捷键,下划线颜色设置
  7. 现代密码法学算法分类
  8. C# fmpeg加虹软的人脸识别demo
  9. C Programming Style 总结
  10. Docker安装nginx以及负载均衡