入門級詳細USB移植教程
阿新 • • 發佈:2019-01-23
同上一篇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,找了圈圈的書將前面足足看了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;